ETH Price: $2,015.39 (-0.34%)

Contract

0xe17C83c373b2cDC65fa471BAc459A4d9B558FfDf
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

2 Internal Transactions found.

Latest 2 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x3d602d80245581202026-02-28 21:29:479 days ago1772314187  Contract Creation0 ETH
0x3d602d80245581052026-02-28 21:26:479 days ago1772314007  Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Minimal Proxy Contract for 0xace41cf6d750d7ba06f4de57ac9e063246b2b090

Contract Name:
Unit

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 21 : Unit.sol
// SPDX-License-Identifier: LicenseRef-Uniteum

pragma solidity ^0.8.30;

import {IUnit, IMigratable, IERC20} from "./IUnit.sol";
import {CloneERC20, Prototype} from "./CloneERC20.sol";
import {Units, Term} from "./Units.sol";
import {SafeERC20} from "erc20/SafeERC20.sol";
import {Math} from "math/Math.sol";

/**
 * @title IUnit — A universal liquidity system based on symbolic units.
 * See {IUnit} for details.
 */
contract Unit is CloneERC20, IUnit {
    using Units for *;
    using SafeERC20 for IERC20;

    /// @notice The ERC-20 symbol for the central 1 token.
    string public constant ONE_SYMBOL = "1";

    /// @notice The ERC-20 symbol for the central 1 token.
    string public constant NAME_PREFIX = "Uniteum-0.7 ";

    /// @notice The total original supply of {1} minted.
    /// @dev The total supply of {1} will never exceed this value.
    uint256 public immutable ONE_MINTED;

    /// @notice The central 1 unit.
    Unit private immutable ONE = this;

    /// @inheritdoc IUnit
    IUnit public reciprocal;

    /// @inheritdoc IUnit
    IERC20 public anchor;

    /// @inheritdoc IUnit
    mapping(IUnit => uint256) public reserves;

    /// @inheritdoc IUnit
    function one() public view returns (IUnit) {
        return ONE;
    }

    /// @inheritdoc IUnit
    function invariant(uint256 u, uint256 v) public pure returns (uint256 w) {
        w = Math.sqrt(u * v);
    }

    /// @inheritdoc IUnit
    function invariant() public view notOne returns (uint256 u, uint256 v, uint256 w) {
        u = ONE.reserves(this);
        v = ONE.reserves(reciprocal);
        w = invariant(u, v);
    }

    /// @inheritdoc IUnit
    function invariant(IUnit V) public view returns (IUnit W, uint256 u, uint256 v, uint256 w) {
        if (address(V) == address(this)) {
            revert DuplicateUnits();
        } else if (address(V) == address(reciprocal)) {
            W = one();
            (u, v, w) = invariant();
        } else {
            (IUnit P,) = product(V);
            (W,) = P.sqrt();
            u = W.reserves(this);
            v = W.reserves(V);
            w = invariant(u, v);
        }
    }

    /// @inheritdoc IUnit
    function forgeQuote(int256 du, int256 dv) public view returns (int256 dw) {
        (uint256 u0, uint256 v0, uint256 w0) = invariant();

        uint256 u1 = add(this, u0, du);
        uint256 v1 = add(reciprocal, v0, dv);
        uint256 w1 = invariant(u1, v1);

        // forge-lint: disable-next-line(unsafe-typecast)
        dw = int256(w0) - int256(w1);
        // Double dw if no anchor tokens are involved to keep the invariant balanced.
        if (address(anchor) == address(0) && address(reciprocal.anchor()) == address(0)) {
            dw *= 2;
        }
    }

    /// @inheritdoc IUnit
    function forgeQuote(IUnit V, int256 du, int256 dv) public view returns (IUnit W, int256 dw) {
        if (address(V) == address(reciprocal)) {
            W = one();
            dw = forgeQuote(du, dv);
        } else {
            uint256 u0;
            uint256 v0;
            uint256 w0;
            (W, u0, v0, w0) = invariant(V);

            uint256 u1 = add(this, u0, -du);
            uint256 v1 = add(V, v0, -dv);
            uint256 w1 = invariant(u1, v1);

            // forge-lint: disable-next-line(unsafe-typecast)
            int256 floatingCount = int256(
                uint256(
                    ((address(anchor) == address(0)) ? 1 : 0) + ((address(reciprocal.anchor()) == address(0)) ? 1 : 0)
                )
            );
            // forge-lint: disable-next-line(unsafe-typecast)
            dw = floatingCount * (int256(w1) - int256(w0));
        }
    }

    /// @inheritdoc IUnit
    function forge(int256 du, int256 dv) external returns (int256 dw) {
        dw = forgeQuote(du, dv);
        ONE.__forge(msg.sender, this, reciprocal, du, dv, dw);
        emit Forge(msg.sender, this, du, dv, dw);
    }

    //// @inheritdoc IUnit
    function forge(IUnit V, int256 du, int256 dv) external returns (IUnit W, int256 dw) {
        multiply(V).sqrtResolve();
        (W, dw) = forgeQuote(V, du, dv);
        ONE.__forge(msg.sender, this, reciprocal, du, dv, dw);
        emit Forge(msg.sender, this, du, dv, dw);
    }

    /// @dev This function must be non-reentrant to thwart malicious anchor tokens.
    function __forge(address holder, IUnit U, IUnit V, int256 du, int256 dv, int256 dw) external nonReentrant {
        __forge(holder, U, du);
        __forge(holder, V, dv);
        __forge(holder, this, dw);
        emit Forge(msg.sender, this, du, dv, dw);
    }

    function __forge(address holder, IUnit V, int256 dv) internal {
        reserves[V] = add(V, reserves[V], dv);
        if (dv < 0) {
            // forge-lint: disable-next-line(unsafe-typecast)
            Unit(address(V)).__burn(holder, uint256(-dv));
        } else if (dv > 0) {
            // forge-lint: disable-next-line(unsafe-typecast)
            Unit(address(V)).__mint(holder, uint256(dv));
        }
    }

    /**
     * @notice Burn units of the holder.
     * @dev - Only Units with the same 1 can call this function.
     * @param holder The holder of the burned units.
     * @param units The number of units to burn.
     */
    function __burn(address holder, uint256 units) external onlyUnit {
        _burn(holder, units);
        // If this Unit wraps an external token, send wrapped tokens to the holder.
        if (address(anchor) != address(0)) {
            anchor.safeTransfer(holder, units);
        }
    }

    /**
     * @notice Mint units for the holder.
     * @dev - Only Units with the same 1 can call this function.
     * @param holder The recipient of the minted units.
     * @param units The number of units to mint.
     */
    function __mint(address holder, uint256 units) external onlyUnit {
        // If this Unit wraps an external token, get wrapped tokens from the holder.
        if (address(anchor) != address(0)) {
            anchor.safeTransferFrom(holder, address(this), units);
        }
        _mint(holder, units);
    }

    /**
     * @notice Safely computes an updated supply of tokens and reverts if the supply would be negative.
     * @param U The unit whose supply is being calculated. For errors only.
     * @param u0 The current supply of U.
     * @param du The change in the supply of U.
     * @return u1 The updated supply of U.
     */
    function add(IUnit U, uint256 u0, int256 du) private pure returns (uint256 u1) {
        // forge-lint: disable-next-line(unsafe-typecast)
        int256 u = int256(u0) + du;
        if (u < 0) {
            revert NegativeSupply(U, u);
        }
        // forge-lint: disable-next-line(unsafe-typecast)
        u1 = uint256(u);
    }

    /**
     * @dev Only one() can call this method.
     * @param canonical expression defining the unit.
     */
    function __initialize(string memory canonical) internal {
        _symbol = canonical;
        _name = string.concat(NAME_PREFIX, canonical);
        Term[] memory terms = canonical.parseTerms();
        if (terms.length == 1) {
            anchor = IERC20(terms[0].anchor());
        }
        terms = terms.reciprocal().sortAndMerge();
        (address reciprocalAddress,) = __clone(bytes(terms.symbol()));
        reciprocal = IUnit(reciprocalAddress);
    }

    /// @inheritdoc Prototype
    function __initialize(bytes memory initData) public virtual override onlyPrototype {
        __initialize(string(initData));
    }

    /// @inheritdoc IUnit
    function product(string memory expression) public view returns (IUnit unit, string memory canonical) {
        Term[] memory terms = symbol().parseTerms().product(expression.parseTerms().sortAndMerge());
        if (terms.length > 0) {
            terms = terms.sortAndMerge();
        }
        canonical = terms.symbol();
        if (terms.length == 0) {
            unit = one();
        } else {
            (address unitAddress,) = __predict(bytes(canonical));
            unit = IUnit(unitAddress);
        }
    }

    /// @inheritdoc IUnit
    function multiply(string memory expression) public returns (IUnit unit) {
        string memory canonical;
        (unit, canonical) = product(expression);
        if (address(unit).code.length == 0) {
            __clone(bytes(canonical));
        }
    }

    /// @inheritdoc IUnit
    function anchoredSymbol(IERC20 token) public pure returns (string memory s) {
        s = address(token).withExponent(Units.ONE_RATIONAL_8).symbol();
    }

    /// @inheritdoc IUnit
    function anchoredPredict(IERC20 token) external view returns (IUnit unit, string memory canonical) {
        (unit, canonical) = product(anchoredSymbol(token));
    }

    /// @inheritdoc IUnit
    function anchored(IERC20 token) external returns (IUnit unit) {
        unit = multiply(anchoredSymbol(token));
    }

    /// @dev Mapping of multipliers to their product units.
    mapping(IUnit => IUnit) private _products;

    /// @inheritdoc IUnit
    function product(IUnit multiplier) public view returns (IUnit unit, string memory canonical) {
        unit = _products[multiplier];
        if (address(unit) != address(0)) {
            canonical = unit.symbol();
        } else {
            (unit, canonical) = product(multiplier.symbol());
        }
    }

    /// @inheritdoc IUnit
    function multiply(IUnit multiplier) public returns (IUnit unit) {
        unit = _products[multiplier];
        if (address(unit) == address(0)) {
            unit = multiply(multiplier.symbol());
            _products[multiplier] = unit;
        }
    }

    IUnit private _sqrt;

    /// @inheritdoc IUnit
    function sqrt() public view returns (IUnit unit, string memory canonical) {
        unit = _sqrt;
        if (address(unit) == address(0)) {
            Term[] memory terms = symbol().parseTerms().sqrt();
            canonical = terms.symbol();
            (address sqrtAddress,) = __predict(bytes(canonical));
            unit = IUnit(sqrtAddress);
        }
    }

    /// @inheritdoc IUnit
    function sqrtResolve() external returns (IUnit root) {
        if (_sqrt != IUnit(address(0))) {
            return _sqrt;
        }
        string memory sqrtSymbol;
        (root, sqrtSymbol) = sqrt();
        if (address(root).code.length == 0) {
            __clone(bytes(sqrtSymbol));
        }
        _sqrt = root;
    }

    modifier onlyUnit() {
        _onlyUnit();
        _;
    }

    function _onlyUnit() private view {
        // Revert if the caller does not have the same address as predicted by its hash.
        // Prevent malicious actors from calling protected functions.
        if ((msg.sender != PROTOTYPE) && (!Prototype(PROTOTYPE).isClone(msg.sender))) {
            revert Unauthorized();
        }
    }

    modifier onlyOne() {
        _onlyOne();
        _;
    }

    function _onlyOne() private view {
        if (this != one()) {
            revert FunctionNotCalledOnOne();
        }
    }

    modifier notOne() {
        _notOne();
        _;
    }

    function _notOne() private view {
        if (this == one()) {
            revert FunctionCalledOnOne();
        }
    }

    // The following reentrancy code was modified from openzeppelin.storage.ReentrancyGuardTransient
    // It uses transient boolean storage on {one()} to prevent reentrancy on all units during a transaction.
    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuardTransient")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant REENTRANCY_GUARD_STORAGE =
        0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    function tstore(bool value) internal {
        assembly ("memory-safe") {
            tstore(REENTRANCY_GUARD_STORAGE, value)
        }
    }

    function _reentrancyGuardEntered() internal view returns (bool value) {
        assembly ("memory-safe") {
            value := tload(REENTRANCY_GUARD_STORAGE)
        }
    }

    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        ONE.__nonReentrantBefore();
    }

    function _nonReentrantAfter() private {
        ONE.__nonReentrantAfter();
    }

    function __nonReentrantBefore() public onlyOne {
        if (_reentrancyGuardEntered()) {
            revert ReentryForbidden();
        }

        // Any calls to nonReentrant after this point will fail
        tstore(true);
    }

    function __nonReentrantAfter() public onlyOne {
        tstore(false);
    }

    /// @inheritdoc IMigratable
    IERC20 public immutable UPSTREAM;

    /// @inheritdoc IMigratable
    function migrate(uint256 units) external onlyOne {
        UPSTREAM.safeTransferFrom(msg.sender, address(this), units);
        // forge-lint: disable-next-line(unsafe-typecast)
        __forge(msg.sender, ONE, int256(units));
        emit Migrate(msg.sender, units);
    }

    /// @inheritdoc IMigratable
    function unmigrate(uint256 units) external onlyOne {
        // forge-lint: disable-next-line(unsafe-typecast)
        __forge(msg.sender, ONE, -int256(units));
        UPSTREAM.safeTransferFrom(address(this), msg.sender, units);
        emit Unmigrate(msg.sender, units);
    }

    constructor(IERC20 upstream) CloneERC20(ONE_SYMBOL, ONE_SYMBOL) {
        reciprocal = this;
        _sqrt = this;
        _symbol = ONE_SYMBOL;
        _name = string.concat(NAME_PREFIX, ONE_SYMBOL);
        UPSTREAM = upstream;
        emit UnitCreate(this, anchor, bytes32(0), _symbol);
    }
}

// SPDX-License-Identifier: LicenseRef-Uniteum

pragma solidity ^0.8.30;

import {IERC20Metadata, IERC20} from "ierc20/IERC20Metadata.sol";
import {IMigratable} from "./IMigratable.sol";

/**
 * @title IUnit — A universal liquidity system based on symbolic units.
 * @notice A Unit (U) is an ERC-20 token with built-in liquidity via reciprocal minting/burning.
 * The identity unit {one()} aka {1} is the universal liquidity token around which a unit and its reciprocal pivot.
 * Units support a {forge} operation that mints/burns combinations of 1, U, and 1/U to maintain a constant product invariant.
 * If a unit goes up in price, its reciprocal goes down, and vice versa.
 * Some units are anchored to external ERC-20 tokens, to integrate the system with the broader ERC-20 ecosystem.
 *
 * A Unit symbolically represents a unit of measure, such as a a physical dimension, abstract quantity, linked ERC-20 token, or compound units.
 * It supports rational powers of base units and algebraic composition such as product and reciprocal.
 * A base unit has two varieties: anchored or unanchored.
 *
 * An anchored unit is a 1:1 custodial owner of an external ERC-20 token
 *   Its symbol is the Ethereum address of the external token.
 *   Examples: 0xdAC17F958D2ee523a2206206994597C13D831ec7 (USDT), 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 (UNI)
 *
 * An unanchored base unit has no associated external token.
 *   Its symbol is an unbroken sequence of the following characters: 'a'-'z', 'A'-'Z', '0'-'9', '_', '-', '.'
 *   Symbols are case sensitive and are limited to 30 characters.
 *   Examples: kg, KG, kG, Kg, m, s, MSFT, USD, _, -, ., example.com, QmFzZTY0IGVuY29kZWQgdW5pdA
 *   Note: unanchored base units have no inherent connection to real world entities.
 *         MSFT IS NOT inherently connected to Microsoft stock.
 *         kg IS NOT inherently connected to the concept of a kilogram.
 *
 * A pure power unit, aka term, is a base unit raised to a power using a combination of '^' and '1/' notation
 *   Division in exponents uses ':' instead of '/' to simplify parsing
 *   Powers can be rational fractions represented using ':' for division in the exponent
 *     Examples: kg^2, 1/s, 1/m^2, 1/T^1:4, 1/0xdAC17F958D2ee523a2206206994597C13D831ec7^3:7
 *   Operations within terms:
 *     ^ power
 *     : divide
 * Compound units are products of pure power units separated by '*' or '/'
 *   Examples: kg*m/s^2, MSFT/USD, 1/foo^2:5/bar^7:9
 *   Operations combining terms:
 *     * multiply
 *     / divide
 *
 * @dev Version scope (v1)
 * - Value constraints exist between a Unit and its reciprocal, and between an anchored token and its anchor.
 * - Powers/exponentials (e.g., constraining value across A and A^k like power perpetuals) are
 *   not enforced; this may be future work.
 *
 * @dev Safety
 * - Anchored units are custodial: underlying tokens are held by this contract.
 * - This system uses no price oracles or off-chain dependencies.
 *
 * @dev Reentrancy Protection
 * All state-changing functions use a transient reentrancy guard stored on the "1" unit
 * per EIP-1153. This protects against malicious anchor token callbacks.
 * @custom:security Uses transient storage; requires EVM version Cancun or later
 *
 * @dev Internal Function Naming Convention
 * Functions prefixed with __ are restricted to calls from other Units in the same system (same ONE).
 * These are used for cross-unit operations during forge.
 */
interface IUnit is IERC20Metadata, IMigratable {
    /**
     * @notice Compute the constant product invariant for a reciprocal pair.
     * The implied price for the unit is w/u, and w/v for its reciprocal.
     * @param u Total supply of a unit.
     * @param v Total supply of its reciprocal.
     * @return w sqrt(u * v).
     */
    function invariant(uint256 u, uint256 v) external view returns (uint256 w);

    /**
     * @notice Return the constant product invariant for a reciprocal pair.
     * @return u Total supply of the unit.
     * @return v Total supply of its reciprocal.
     * @return w sqrt(u * v).
     */
    function invariant() external view returns (uint256 u, uint256 v, uint256 w);

    /**
     * @notice Return the constant product invariant for a pair.
     * @param V The invariant pair for this unit.
     * @return W Product of this unit and V.
     * @return u Total supply of the unit.
     * @return v Total supply of its reciprocal.
     * @return w sqrt(u * v).
     */
    function invariant(IUnit V) external view returns (IUnit W, uint256 u, uint256 v, uint256 w);

    /**
     * @notice Return the amount of a reserve unit where this unit is the liquidity provider.
     * @param V One of the reserve units associated with this unit.
     * @return v The reserve amount.
     */
    function reserves(IUnit V) external view returns (uint256 v);

    /**
     * @notice Compute the change of the caller's 1 balance that would result from forging this unit.
     *
     * @dev Invariant solver for the forge operation.
     * Given signed changes to the caller's balances of the unit `du` and its reciprocal `dv`,
     * this function computes the signed change to 1 `dw` required to preserve the
     * constant-product relationship across the triad (U, 1/U, 1).
     *
     * Sign convention:
     *  - Positive values mint units to the caller.
     *  - Negative values burn units from the caller.
     *
     * @param V Other unit.
     * @param du Signed change of the caller's unit balance.
     * @param dv Signed change of the caller's reciprocal balance.
     * @return W Product of this unit and V.
     * @return dw Signed change of caller's 1 balance.
     */
    function forgeQuote(IUnit V, int256 du, int256 dv) external view returns (IUnit W, int256 dw);

    /**
     * @notice Compute the change of the caller's 1 balance that would result from forging this unit.
     *
     * @dev Invariant solver for the forge operation.
     * Given signed changes to the caller's balances of the unit `du` and its reciprocal `dv`,
     * this function computes the signed change to 1 `dw` required to preserve the
     * constant-product relationship across the triad (U, 1/U, 1).
     *
     * Sign convention:
     *  - Positive values mint units to the caller.
     *  - Negative values burn units from the caller.
     *
     * @param du Signed change of the caller's unit balance.
     * @param dv Signed change of the caller's reciprocal balance.
     * @return dw Signed change of caller's 1 balance.
     */
    function forgeQuote(int256 du, int256 dv) external view returns (int256 dw);

    /**
     * @notice Mint/burn combinations of this unit, its reciprocal and 1.
     * @dev
     * Uses {forgeQuote} to compute the necessary deltas to maintain the invariant,
     * then mints/burns the corresponding amounts of du, dv, and dw for the caller.
     * To mint an anchored unit, even if it participates as the reciprocal,
     * the caller must approve transferring the anchor token to the unit:
     *     u.anchor().approve(address(u)), uint256(du));
     *
     * @param V Other unit.
     * @param du Signed delta of the unit U.
     * @param dv Signed delta of the unit 1/U.
     * @return W Product of this unit and V.
     * @return dw Signed delta of 1 minted/burned for the caller.
     */
    function forge(IUnit V, int256 du, int256 dv) external returns (IUnit W, int256 dw);

    /**
     * @notice Mint/burn combinations of this unit, its reciprocal and 1.
     * @dev
     * Uses {forgeQuote} to compute the necessary deltas to maintain the invariant,
     * then mints/burns the corresponding amounts of du, dv, and dw for the caller.
     * To mint an anchored unit, even if it participates as the reciprocal,
     * the caller must approve transferring the anchor token to the unit:
     *     u.anchor().approve(address(u)), uint256(du));
     *
     * @param du Signed delta of the unit U.
     * @param dv Signed delta of the unit 1/U.
     * @return dw Signed delta of 1 minted/burned for the caller.
     */
    function forge(int256 du, int256 dv) external returns (int256 dw);

    /**
     * @notice Predict the address of the IUnit resulting from multiplying by a symbolic expression.
     * @dev View-only; does not create the unit. Use {multiply} to create if needed.
     * @param expression a string representation of the unit.
     * @return unit the IUnit for the given expression.
     * @return symbol the canonical form of the string representation of the unit.
     */
    function product(string memory expression) external view returns (IUnit unit, string memory symbol);

    /**
     * @notice Create a new unit if it does not exist, or return existing unit.
     * @dev Creates the unit by multiplying this unit with the expression.
     * @param expression a string representation of the unit to multiply by.
     * @return unit the IUnit with the resulting symbol.
     */
    function multiply(string memory expression) external returns (IUnit unit);

    /**
     * @notice Predict the unit resulting from multiplying this unit by another unit.
     * @dev View-only; uses cached product mapping when available, otherwise computes from symbols.
     * Does not create the unit. Use {multiply} to create if needed.
     * @param multiplier The right-hand unit operand.
     * @return unit The IUnit representing the product.
     * @return symbol the canonical form of the string representation of the unit.
     */
    function product(IUnit multiplier) external view returns (IUnit unit, string memory symbol);

    /**
     * @notice Create or return the product of this unit with another unit.
     * @dev Creates the product unit if it doesn't exist, caches the mapping for future calls.
     * @param multiplier The right-hand unit operand.
     * @return product The new or existing IUnit representing the product.
     */
    function multiply(IUnit multiplier) external returns (IUnit product);

    /**
     * @notice Predict the address of an anchored unit.
     * @param token to be anchored to.
     * @return unit the IUnit anchored to the given token.
     * @return symbol the canonical form of the string representation of the unit.
     */
    function anchoredPredict(IERC20 token) external view returns (IUnit unit, string memory symbol);

    /**
     * @notice Create an anchored unit if it does not exist.
     * @param token to be anchored to.
     * @return unit the IUnit anchored to the given token.
     */
    function anchored(IERC20 token) external returns (IUnit unit);

    /**
     * @notice Return the symbol for an anchored token.
     *   Example: 0xdAC17F958D2ee523a2206206994597C13D831ec7 (USDT)
     * @param token to be anchored to.
     * @return symbol the canonical form of the string representation of the unit.
     */
    function anchoredSymbol(IERC20 token) external pure returns (string memory symbol);

    /**
     * @notice The identity unit "1".
     * @dev Also the implementation and deployer for all other units, which are clones.
     */
    function one() external view returns (IUnit);

    /**
     * @return The IUnit representing the reciprocal of this unit.
     */
    function reciprocal() external view returns (IUnit);

    /**
     * @return root The IUnit representing the sqrt of this unit.
     * @return symbol the canonical form of the string representation of the unit.
     * Symbol is only returned if the root is not known to be deployed.
     */
    function sqrt() external view returns (IUnit root, string memory symbol);

    /**
     * @notice Ensure the sqrt of this unit is deployed.
     */
    function sqrtResolve() external returns (IUnit root);

    /**
     * @return The external token, if any, anchored to this unit.
     */
    function anchor() external view returns (IERC20);

    /**
     * @dev Revert when called with duplicate units.
     */
    error DuplicateUnits();

    /**
     * @dev Revert when called on 1.
     */
    error FunctionCalledOnOne();

    /**
     * @dev Revert when called on anything but 1.
     */
    error FunctionNotCalledOnOne();

    /**
     * @dev Revert when a negative supply would result from an operation.
     * @param unit The unit that would have negative supply.
     * @param supply The calculated negative supply value.
     */
    error NegativeSupply(IUnit unit, int256 supply);

    /**
     * @dev Reentrant calls are forbidden.
     */
    error ReentryForbidden();

    /**
     * @notice Emit on unit creation.
     * @param unit The created unit.
     * @param hash used to compute the address of the unit.
     * @param symbol The symbol of the the unit.
     */
    event UnitCreate(IUnit indexed unit, IERC20 indexed anchor, bytes32 indexed hash, string symbol);

    /**
     * @notice Emit when a holder calls forge.
     * @param holder The address whose balances were updated.
     * @param unit   The unit doing the forge.
     * @param du     signed change to the holder's balance of the unit.
     * @param dv     signed change to the holder's balance of the reciprocal unit.
     * @param dw     signed change to the holder's balance of 1.
     */
    event Forge(address indexed holder, IUnit indexed unit, int256 du, int256 dv, int256 dw);

    /**
     * @notice Emitted when tokens are migrated into the system.
     * @param user The address migrating tokens.
     * @param amount Amount of tokens migrated.
     */
    event Migrate(address indexed user, uint256 amount);

    /**
     * @notice Emitted when tokens are unmigrated from the system.
     * @param user The address unmigrating tokens.
     * @param amount Amount of tokens unmigrated.
     */
    event Unmigrate(address indexed user, uint256 amount);
}

File 3 of 21 : CloneERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.30;

import {Prototype} from "./Prototype.sol";
import {ERC20} from "erc20/ERC20.sol";

/**
 * @title CloneERC20
 * @notice ERC-20 base contract with support for minimal proxy cloning (EIP-1167).
 * @dev
 * Combines OpenZeppelin's ERC20 standard implementation with the Prototype
 * cloning pattern, enabling gas-efficient deployment of multiple ERC-20 tokens
 * that share the same implementation logic but maintain isolated storage.
 *
 * **Key Design Pattern:**
 * Standard ERC-20 implementations store name and symbol as immutables set during
 * construction. This prevents cloning because minimal proxies (EIP-1167) delegate
 * all logic via DELEGATECALL and cannot have their own constructor parameters.
 *
 * CloneERC20 solves this by:
 * 1. Storing name and symbol in regular storage variables (_name, _symbol)
 * 2. Overriding name() and symbol() accessors to read from storage
 * 3. Allowing these values to be set during __initialize() on each clone
 *
 * **Usage Pattern:**
 * ```solidity
 * // 1. Deploy prototype
 * MyToken prototype = new MyToken("PROTO", "PROTO");
 *
 * // 2. Create clones with custom metadata
 * bytes memory initData = abi.encode(creator, "Token A", "TKA");
 * (address tokenA, ) = prototype.__clone(initData);
 *
 * initData = abi.encode(creator, "Token B", "TKB");
 * (address tokenB, ) = prototype.__clone(initData);
 *
 * // 3. Each clone has its own name/symbol but shares logic
 * assert(MyToken(tokenA).name() == "Token A");
 * assert(MyToken(tokenB).name() == "Token B");
 * ```
 *
 * **Storage Layout:**
 * Each clone maintains its own:
 * - _name: Token name (settable during initialization)
 * - _symbol: Token symbol (settable during initialization)
 * - _balances: Mapping of account balances (ERC20 inherited)
 * - _allowances: Mapping of allowances (ERC20 inherited)
 * - _totalSupply: Total token supply (ERC20 inherited)
 *
 * **Why Empty String Constructor:**
 * The ERC20 base constructor is passed empty strings because:
 * - Those values would only affect the prototype contract itself
 * - Clones override name() and symbol() to read from their own storage
 * - This prevents confusion between prototype metadata and clone metadata
 *
 * **Inheritance Chain:**
 * CloneERC20 → ERC20 (OpenZeppelin) + Prototype (factory pattern)
 *
 * @author Paul Reinholdtsen (reinholdtsen.eth)
 */
abstract contract CloneERC20 is ERC20, Prototype {
    // ============ Constructor ============

    /**
     * @notice Initializes the prototype implementation with name and symbol.
     * @dev
     * **Important:** These parameters only affect the prototype contract itself,
     * NOT the clones. Each clone sets its own _name and _symbol during __initialize().
     *
     * The ERC20 base constructor receives empty strings ("", "") because:
     * 1. We override name() and symbol() to read from storage instead
     * 2. The prototype's metadata is rarely used (clones are what matter)
     * 3. This keeps the pattern consistent across prototype and clones
     *
     * **For derived contracts:**
     * Pass descriptive metadata for the prototype (often "PROTO" or similar)
     * to distinguish it from actual clone instances.
     *
     * @param name_ Name for the prototype implementation.
     * @param symbol_ Symbol for the prototype implementation.
     */
    constructor(string memory name_, string memory symbol_) ERC20("", "") {
        _name = name_;
        _symbol = symbol_;
    }

    // ============ ERC-20 Metadata Overrides ============

    /**
     * @notice Returns the name of the token.
     * @dev Overrides ERC20.name() to read from storage instead of immutables.
     *
     *      **On the prototype:** Returns the name set in constructor.
     *      **On clones:** Returns the name set during __initialize().
     *
     *      This allows each clone to have distinct metadata while sharing
     *      the same implementation logic.
     *
     * @return The token name.
     */
    function name() public view virtual override(ERC20) returns (string memory) {
        return _name;
    }

    /**
     * @notice Returns the symbol of the token.
     * @dev Overrides ERC20.symbol() to read from storage instead of immutables.
     *
     *      **On the prototype:** Returns the symbol set in constructor.
     *      **On clones:** Returns the symbol set during __initialize().
     *
     *      This allows each clone to have distinct metadata while sharing
     *      the same implementation logic.
     *
     * @return The token symbol.
     */
    function symbol() public view virtual override(ERC20) returns (string memory) {
        return _symbol;
    }
}

// SPDX-License-Identifier: LicenseRef-Uniteum

pragma solidity ^0.8.30;

import {Term} from "./Term.sol";
import {Rationals, Rational, Rational8} from "./Rationals.sol";
import {Strings} from "strings/Strings.sol";

/**
 * @title Units
 * @dev Library for unit term operations.
 * Base unit terms are packed into uint:
 * The last two bytes (30, 31) are a rational exponent.
 * Symbolic terms have the first 30 bytes as the base symbol.
 * Address terms have the first byte = 1, and the next 20 bytes are an address.
 * +0......0|1.........................20|21................29|30...........31+
 * | Symbol                                                   |    Exponent   |
 * |----------------------------------------------------------| ± num / den   |
 * | Type=1 | Address [1..20]            | Reserved           |  int8 | uint8 |
 * +255................................96|95................16|15....8|7.....0+
 * Example 1: meter^2:3
 * |6d 6574657200000000000000000000000000000000 000000000000000000 02 03|
 * |  |                                        |                  |  |  |
 * |01 c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 000000000000000000 ff 01|
 * Example 2: [address of WETH]^-1
 */
library Units {
    using Units for *;
    using Rationals for *;
    using Strings for *;

    /// @dev Bitmap indicating valid symbol characters: 0-9, A-Z, a-z, _, -, .
    uint256 constant SYMBOL_CHAR_BITS = 0x7fffffe87fffffe03ff600000000000;
    /// @dev The term for 1^0. The ascii code for "1" is 0x31.
    /// @dev Shift amount for term type byte: 248 bits (31 bytes) = 0xf8
    uint256 constant ONE_TERM = 0x31 << 0xf8;
    /// @dev A term with this type is an encoded address reference with an exponent.
    uint256 constant ANCHOR_TERM_TYPE = 1;
    /// @dev Shift amount for anchor address: 20 bytes (address) + 9 bytes (reserved) = 88 bits = 0x58
    uint256 constant ANCHOR_SHIFT = 0x58;
    string constant ONE_SYMBOL = "1";
    bytes1 constant DIVIDE_SYMBOL = "/";
    bytes1 constant MULTIPLY_SYMBOL = "*";
    bytes1 constant POWER_SYMBOL = "^";
    bytes1 constant POWER_DIV = ":";
    Rational8 constant ZERO_RATIONAL_8 = Rational8.wrap(1);
    Rational8 constant ONE_RATIONAL_8 = Rational8.wrap(0x101);
    uint256 constant EXPONENT_MASK = 0xffff;
    uint256 constant MAX_SYMBOL_SIZE = 30;

    // Errors
    error BaseSymbolTooBig();
    error ExponentTooBig();
    error InvalidAddressTerm(Term term);
    error BadHexCharacter(uint8 char);
    error UnexpectedCharacter(bytes1 char);
    error UnexpectedEndOfInput();

    /// @dev Extracts the base part from a term (clears the exponent byte)
    function base(Term term) internal pure returns (Term base_) {
        base_ = Term.wrap(term.raw() & ~uint256(EXPONENT_MASK));
    }

    function raw(Term term) internal pure returns (uint256) {
        return Term.unwrap(term);
    }

    /// @dev Extracts the exponent from a term (int8 stored in lowest byte)
    function exponent(Term term) internal pure returns (Rational8) {
        return Rational8.wrap(int16(uint16(term.raw())));
    }

    /// @dev Returns whether the char is one of 0-9, A-Z, a-z, _, -, .
    function isSymbolChar(bytes1 char) internal pure returns (bool) {
        return (SYMBOL_CHAR_BITS >> uint8(char)) & 1 != 0;
    }

    function termType(Term term) internal pure returns (uint8 termType_) {
        termType_ = uint8(term.raw() >> 0xf8);
    }

    function isBase(Term term) internal pure returns (bool) {
        return term.exponent().raw() == ONE_RATIONAL_8.raw();
    }

    /**
     * @notice Return the external token address represented by the term.
     * @dev Return address(0) if the term is not an external token term.
     */
    function anchor(Term term) internal pure returns (address token) {
        if (term.termType() == ANCHOR_TERM_TYPE && term.isBase()) {
            // forge-lint: disable-next-line(unsafe-typecast)
            token = address(uint160(term.raw() >> ANCHOR_SHIFT));
        }
    }

    /**
     * @notice Return the external token address represented by the terms.
     * @dev Return address(0) if the term is not an external token term.
     */
    function anchor(Term[] memory terms) internal pure returns (address token) {
        if (terms.length == 1) {
            token = terms[0].anchor();
        }
    }

    function parts(Term term)
        internal
        pure
        returns (
            uint256 bits,
            bool isBase_,
            uint8 termType_,
            address tokenAddress_,
            bytes30 symbol_,
            int8 numerator_,
            uint8 denominator_
        )
    {
        bits = term.raw();
        // forge-lint: disable-next-line(unsafe-typecast)
        termType_ = uint8(bits >> 0xf8);
        // forge-lint: disable-next-line(unsafe-typecast)
        numerator_ = int8(uint8(bits >> 8));
        // forge-lint: disable-next-line(unsafe-typecast)
        denominator_ = uint8(bits);
        isBase_ = numerator_ == 1 && denominator_ == 1;
        if (termType_ != ANCHOR_TERM_TYPE) {
            // forge-lint: disable-next-line(unsafe-typecast)
            symbol_ = bytes30(uint240(bits >> 16));
        } else {
            // forge-lint: disable-next-line(unsafe-typecast)
            tokenAddress_ = address(uint160(bits >> ANCHOR_SHIFT));
        }
    }

    /**
     * @dev Reverts if the term is not valid
     *      - has non-symbol characters before the zero padding
     *      - has an exponent numerator = -128
     */
    function mustBeValidTerm(Term term) internal pure {
        (uint256 c,, uint8 t,, bytes30 s, int8 n, uint8 d) = term.parts();
        if (n == -128) {
            revert ExponentTooBig();
        }
        if (d == 0) {
            revert Rationals.ZeroDenominator();
        }

        if (t == ANCHOR_TERM_TYPE) {
            if (0 != ((c >> 16) << 23 * 8)) {
                revert InvalidAddressTerm(term);
            }
        } else {
            uint256 i;
            unchecked {
                for (; i < 30; ++i) {
                    if (!s[i].isSymbolChar()) {
                        break;
                    }
                }

                for (; i < 30; ++i) {
                    if (s[i] != 0) {
                        revert UnexpectedCharacter(s[i]);
                    }
                }
            }
        }
    }

    /// @dev Revert if any term is invalid.
    function mustBeValidTerms(Term[] memory terms) internal pure {
        unchecked {
            for (uint256 i = 0; i < terms.length; ++i) {
                terms[i].mustBeValidTerm();
            }
        }
    }

    /// @dev Packs a base and exponent into a term
    function withExponent(Term base_, Rational8 exp) internal pure returns (Term term) {
        term = Term.wrap((base_.raw() & ~uint256(EXPONENT_MASK)) | uint256(uint16(int16(exp.raw()))));
    }

    /// @dev Packs a base and exponent into a term
    function withExponent(address base_, Rational8 exp) internal pure returns (Term term) {
        term = Term.wrap((uint256(uint160(base_)) << ANCHOR_SHIFT) | (ANCHOR_TERM_TYPE << 0xf8)).withExponent(exp);
    }

    /// @dev Return the reciprocal of a term (negates exponent)
    function reciprocal(Term term) internal pure returns (Term reciprocal_) {
        reciprocal_ = term.withExponent(term.exponent().neg());
    }

    /// @dev Return the reciprocal terms. Modifies the input.
    function reciprocal(Term[] memory terms) internal pure returns (Term[] memory reciprocal_) {
        reciprocal_ = terms;
        unchecked {
            for (uint256 i = 0; i < terms.length; ++i) {
                reciprocal_[i] = terms[i].reciprocal();
            }
        }
    }

    /// @dev Return the sqrt of a term (halves exponent)
    function sqrt(Term term) internal pure returns (Term root) {
        root = term.withExponent(term.exponent().div(2));
    }

    /// @dev Return the sqrt terms. Modifies the input.
    function sqrt(Term[] memory terms) internal pure returns (Term[] memory root) {
        root = terms;
        for (uint256 i = 0; i < terms.length; ++i) {
            root[i] = terms[i].sqrt();
        }
    }

    /// @dev Concatenates three strings
    function add(string memory s1, string memory s2, string memory s3) internal pure returns (string memory) {
        return string.concat(s1, s2, s3);
    }

    function toString(bytes30 b) internal pure returns (string memory s) {
        uint256 end;
        unchecked {
            // Find trailing zeros
            for (; end < 30; ++end) {
                if (b[end] == 0) {
                    break;
                }
            }
        }

        bytes memory sb = new bytes(end);

        unchecked {
            for (uint256 i = 0; i < end; ++i) {
                sb[i] = b[i];
            }
        }
        s = string(sb);
    }

    /// @dev Returns the symbol string for a single term
    function symbol(Term term) internal pure returns (string memory symbol_) {
        (,, uint8 t, address a, bytes30 s, int8 n, uint8 d) = term.parts();
        if (n == 0) {
            return ONE_SYMBOL;
        }
        if (t == ANCHOR_TERM_TYPE) {
            symbol_ = Strings.toChecksumHexString(a);
        } else {
            symbol_ = toString(s);
        }
        if (n != 1 || d != 1) {
            symbol_ = symbol_.add("^", Strings.toStringSigned(n));
            if (d != 1) {
                symbol_ = symbol_.add(":", Strings.toString(d));
            }
        }
    }

    /// @dev Returns the full compound unit symbol from an array of terms
    function symbol(Term[] memory terms) internal pure returns (string memory symbol_) {
        if (terms.length == 0) {
            return ONE_SYMBOL;
        }

        string memory mul = ""; // Do not put * before the first term
        unchecked {
            for (uint256 i = 0; i < terms.length; ++i) {
                int256 n = terms[i].exponent().numerator();
                if (n > 0) {
                    symbol_ = symbol_.add(mul, terms[i].symbol());
                    mul = "*";
                }
            }
        }

        if (bytes(symbol_).length == 0) {
            symbol_ = ONE_SYMBOL;
        }

        unchecked {
            for (uint256 i = 0; i < terms.length; ++i) {
                int256 n = terms[i].exponent().numerator();
                if (n < 0) {
                    symbol_ = symbol_.add("/", terms[i].reciprocal().symbol());
                }
            }
        }
    }

    /// @dev Parses a base symbol starting at buffer[start], returns base-packed uint
    function parseAddress(bytes memory buffer, uint256 start)
        internal
        pure
        returns (bool isAddress, Term term, uint256 cursor)
    {
        uint256 end = buffer.length;
        cursor = start + 42;
        if (cursor > end) {
            return (false, term, start);
        }
        if (buffer[start] != "0") {
            return (false, term, start);
        }
        if (buffer[start + 1] != "x") {
            return (false, term, start);
        }
        start += 2;
        uint160 result = 0;
        unchecked {
            for (uint256 i = start; i < cursor; ++i) {
                uint8 c = uint8(buffer[i]);
                if (c >= 48 && c <= 57) {
                    // '0'-'9'
                    result = result * 16 + (c - 48);
                } else if (c >= 65 && c <= 70) {
                    // 'A'-'F'
                    result = result * 16 + (c - 55);
                } else if (c >= 97 && c <= 102) {
                    // 'a'-'f'
                    result = result * 16 + (c - 87);
                } else {
                    return (false, term, start);
                }
            }
        }
        isAddress = true;
        term = address(result).withExponent(ONE_RATIONAL_8);
    }

    /// @dev Parses a base symbol starting at buffer[start], returns base-packed uint
    function parseBase(bytes memory buffer, uint256 start) internal pure returns (Term term, uint256 cursor) {
        uint256 end = buffer.length;
        cursor = start;

        // Advance the cursor past symbol characters.
        while (cursor < end && buffer[cursor].isSymbolChar()) {
            cursor++;
        }

        uint256 baseLength = cursor - start;

        if (baseLength > MAX_SYMBOL_SIZE) {
            revert BaseSymbolTooBig();
        }

        // SAFETY: Reading from memory buffer at validated offset (start < cursor < end)
        // and masking to exact baseLength bytes. No out-of-bounds access possible.
        assembly {
            let word := mload(add(add(buffer, 32), start))
            let shift := sub(256, mul(baseLength, 8))
            let mask := shl(shift, sub(exp(2, mul(baseLength, 8)), 1))
            term := and(word, mask)
        }
    }

    /// @dev Parse an integer starting at buffer[start].
    function parseNumber(bytes memory buffer, uint256 start) internal pure returns (uint256 n, uint256 cursor) {
        uint256 end = buffer.length;
        cursor = start;
        unchecked {
            while (cursor < end && n <= 128 && buffer[cursor] >= "0" && buffer[cursor] <= "9") {
                n = n * 10 + uint8(buffer[cursor]) - 48;
                ++cursor;
            }
        }
    }

    /// @dev Reverts if cursor is not less than end
    function mustBeLessThan(uint256 cursor, uint256 end) internal pure {
        if (cursor >= end) {
            revert UnexpectedEndOfInput();
        }
    }

    /// @dev Parses a full compound symbol into an array of terms
    function parseTerms(string memory symbol_) internal pure returns (Term[] memory terms) {
        bytes memory buffer = bytes(symbol_);
        uint256 end = buffer.length;
        uint256 cursor = 0;

        cursor.mustBeLessThan(end);

        // Count number of terms
        uint256 termCount = 1;
        unchecked {
            for (uint256 j = 1; j < end; ++j) {
                if (buffer[j] == DIVIDE_SYMBOL || buffer[j] == MULTIPLY_SYMBOL) termCount++;
            }
        }

        terms = new Term[](termCount);
        uint256 termIndex = 0;

        while (cursor < end) {
            int256 exp = 1;

            // Skip * or /
            if (cursor > 0) {
                if (buffer[cursor] == MULTIPLY_SYMBOL) {
                    cursor++;
                } else if (buffer[cursor] == DIVIDE_SYMBOL) {
                    exp = -exp;
                    cursor++;
                }
            }

            cursor.mustBeLessThan(end);

            Term term;
            bool isAddress;
            (isAddress, term, cursor) = parseAddress(buffer, cursor);
            if (!isAddress) {
                (term, cursor) = parseBase(buffer, cursor);
            }

            if (term.raw() == 0) {
                revert UnexpectedCharacter(cursor == end ? bytes1(0) : buffer[cursor]);
            }

            uint256 expDenom = 1;

            // Extract exponent if present
            if (cursor < end && buffer[cursor] == POWER_SYMBOL) {
                cursor++;
                cursor.mustBeLessThan(end);

                uint256 pow = 0;
                (pow, cursor) = parseNumber(buffer, cursor);
                exp *= pow.toInt128();

                if (cursor < end && buffer[cursor] == POWER_DIV) {
                    cursor++;
                    cursor.mustBeLessThan(end);
                    (expDenom, cursor) = parseNumber(buffer, cursor);
                }
            }

            Rational8 exp8 = exp.divRational8(expDenom);
            term = term.withExponent(exp8);
            terms[termIndex++] = term;
        }
    }

    function toInt128(uint256 x) internal pure returns (int128 y) {
        if (x <= uint128(type(int128).max)) {
            // forge-lint: disable-next-line(unsafe-typecast)
            y = int128(uint128(x));
        } else {
            revert ExponentTooBig();
        }
    }

    /// @dev Returns first n terms from array
    function take(Term[] memory long, uint256 n) internal pure returns (Term[] memory short) {
        if (long.length == n) {
            short = long;
        } else {
            short = new Term[](n);
            unchecked {
                for (uint256 i = 0; i < n; ++i) {
                    short[i] = long[i];
                }
            }
        }
    }

    /// @dev Merges two sorted term arrays
    function product(Term[] memory t1, Term[] memory t2) internal pure returns (Term[] memory t3) {
        uint256 n1 = t1.length;
        uint256 n2 = t2.length;

        if (n1 == 0) {
            return t2;
        }
        if (n2 == 0) {
            return t1;
        }

        t3 = new Term[](n1 + n2);
        uint256 i1 = 0;
        uint256 i2 = 0;
        uint256 i3 = 0;

        while (i1 < n1 && i2 < n2) {
            Term base1 = t1[i1].base();
            Term base2 = t2[i2].base();

            if (base1.raw() < base2.raw()) {
                t3[i3++] = t1[i1++];
            } else if (base2.raw() < base1.raw()) {
                t3[i3++] = t2[i2++];
            } else {
                // Same base, combine exponents
                // Sum using int16 then check for too big.
                Rational8 exp = t1[i1].exponent().add(t2[i2].exponent());
                if (exp.raw() != ZERO_RATIONAL_8.raw()) {
                    t3[i3++] = base1.withExponent(exp);
                }
                i1++;
                i2++;
            }
        }

        // Copy remaining terms
        while (i1 < n1) {
            t3[i3++] = t1[i1++];
        }
        while (i2 < n2) {
            t3[i3++] = t2[i2++];
        }

        t3 = t3.take(i3);
    }

    /// @dev Determine if the terms are in order.
    function inOrder(Term[] memory terms) internal pure returns (bool) {
        uint256 n = terms.length;
        if (n == 0) return true;

        unchecked {
            for (uint256 i = 0; i < n - 1; ++i) {
                if (terms[i].raw() > terms[i + 1].raw()) return false;
            }
        }
        return true;
    }

    /// @dev Sorts terms in ascending base order using heap sort
    function sort(Term[] memory terms) internal pure returns (Term[] memory) {
        if (terms.inOrder()) return terms;

        uint256 n = terms.length;

        // Build max heap
        for (uint256 i = n / 2; i > 0; i--) {
            heapify(terms, n, i - 1);
        }

        // Extract elements from heap one by one
        for (uint256 i = n - 1; i > 0; i--) {
            // Move current root to end
            (terms[0], terms[i]) = (terms[i], terms[0]);
            // call max heapify on the reduced heap
            heapify(terms, i, 0);
        }
        return terms;
    }

    function heapify(Term[] memory terms, uint256 n, uint256 i) private pure {
        uint256 largest = i;
        uint256 left = 2 * i + 1;
        uint256 right = 2 * i + 2;

        if (left < n && terms[left].raw() > terms[largest].raw()) {
            largest = left;
        }
        if (right < n && terms[right].raw() > terms[largest].raw()) {
            largest = right;
        }
        if (largest != i) {
            (terms[i], terms[largest]) = (terms[largest], terms[i]);
            heapify(terms, n, largest);
        }
    }

    /// @dev Sorts and merges duplicate bases
    function sortAndMerge(Term[] memory terms) internal pure returns (Term[] memory) {
        if (terms.length == 0) return terms;

        terms = terms.sort();

        // Quick check if merge is needed or if "1" terms need filtering
        bool needsProcessing = false;
        unchecked {
            for (uint256 i = 0; i < terms.length; ++i) {
                if (terms[i].base().raw() == ONE_TERM) {
                    needsProcessing = true;
                    break;
                }
                if (i > 0 && terms[i].base().raw() == terms[i - 1].base().raw()) {
                    needsProcessing = true;
                    break;
                }
            }
        }
        if (!needsProcessing) return terms;

        uint256 termCount = terms.length;
        uint256 j = 0;
        Term term = terms[0].base();
        Rational exp = terms[0].exponent().toRational();
        unchecked {
            for (uint256 i = 1; i < termCount; ++i) {
                if (terms[i].base().raw() == term.raw()) {
                    exp = exp.add(terms[i].exponent().toRational());
                } else {
                    if (exp.raw() != ZERO_RATIONAL_8.raw() && term.raw() != ONE_TERM) {
                        terms[j] = term.withExponent(exp.toRational8());
                        ++j;
                    }
                    term = terms[i].base();
                    exp = terms[i].exponent().toRational();
                }
            }
            if (exp.raw() != ZERO_RATIONAL_8.raw() && term.raw() != ONE_TERM) {
                terms[j] = term.withExponent(exp.toRational8());
                ++j;
            }
        }

        terms = terms.take(j);
        return terms;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "ierc20/IERC20.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

File 6 of 21 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

import {Panic} from "panic/Panic.sol";
import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Return the 512-bit addition of two uint256.
     *
     * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
     */
    function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
        assembly ("memory-safe") {
            low := add(a, b)
            high := lt(low, a)
        }
    }

    /**
     * @dev Return the 512-bit multiplication of two uint256.
     *
     * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
     */
    function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
        // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
        // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = high * 2²⁵⁶ + low.
        assembly ("memory-safe") {
            let mm := mulmod(a, b, not(0))
            low := mul(a, b)
            high := sub(sub(mm, low), lt(mm, low))
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            success = c >= a;
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a - b;
            success = c <= a;
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a * b;
            assembly ("memory-safe") {
                // Only true when the multiplication doesn't overflow
                // (c / a == b) || (a == 0)
                success := or(eq(div(c, a), b), iszero(a))
            }
            // equivalent to: success ? c : 0
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            success = b > 0;
            assembly ("memory-safe") {
                // The `DIV` opcode returns zero when the denominator is 0.
                result := div(a, b)
            }
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            success = b > 0;
            assembly ("memory-safe") {
                // The `MOD` opcode returns zero when the denominator is 0.
                result := mod(a, b)
            }
        }
    }

    /**
     * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
     */
    function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
        (bool success, uint256 result) = tryAdd(a, b);
        return ternary(success, result, type(uint256).max);
    }

    /**
     * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
     */
    function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
        (, uint256 result) = trySub(a, b);
        return result;
    }

    /**
     * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
     */
    function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
        (bool success, uint256 result) = tryMul(a, b);
        return ternary(success, result, type(uint256).max);
    }

    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * SafeCast.toUint(condition));
        }
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }

        // The following calculation ensures accurate ceiling division without overflow.
        // Since a is non-zero, (a - 1) / b will not overflow.
        // The largest possible result occurs when (a - 1) / b is type(uint256).max,
        // but the largest value we can obtain is type(uint256).max - 1, which happens
        // when a = type(uint256).max and b = 1.
        unchecked {
            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
        }
    }

    /**
     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     *
     * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            (uint256 high, uint256 low) = mul512(x, y);

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

            // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
            if (denominator <= high) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
            }

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

            // Make division exact by subtracting the remainder from [high low].
            uint256 remainder;
            assembly ("memory-safe") {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

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

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

            uint256 twos = denominator & (0 - denominator);
            assembly ("memory-safe") {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [high low] by twos.
                low := div(low, twos)

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

            // Shift in bits from high into low.
            low |= high * twos;

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

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
            inverse *= 2 - denominator * inverse; // inverse mod 2³²
            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶

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

    /**
     * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
    }

    /**
     * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
     */
    function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
        unchecked {
            (uint256 high, uint256 low) = mul512(x, y);
            if (high >= 1 << n) {
                Panic.panic(Panic.UNDER_OVERFLOW);
            }
            return (high << (256 - n)) | (low >> n);
        }
    }

    /**
     * @dev Calculates x * y >> n with full precision, following the selected rounding direction.
     */
    function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
        return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
    }

    /**
     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
     *
     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
     *
     * If the input value is not inversible, 0 is returned.
     *
     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
     */
    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
        unchecked {
            if (n == 0) return 0;

            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
            // Used to compute integers x and y such that: ax + ny = gcd(a, n).
            // When the gcd is 1, then the inverse of a modulo n exists and it's x.
            // ax + ny = 1
            // ax = 1 + (-y)n
            // ax ≡ 1 (mod n) # x is the inverse of a modulo n

            // If the remainder is 0 the gcd is n right away.
            uint256 remainder = a % n;
            uint256 gcd = n;

            // Therefore the initial coefficients are:
            // ax + ny = gcd(a, n) = n
            // 0a + 1n = n
            int256 x = 0;
            int256 y = 1;

            while (remainder != 0) {
                uint256 quotient = gcd / remainder;

                (gcd, remainder) = (
                    // The old remainder is the next gcd to try.
                    remainder,
                    // Compute the next remainder.
                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                    // where gcd is at most n (capped to type(uint256).max)
                    gcd - remainder * quotient
                );

                (x, y) = (
                    // Increment the coefficient of a.
                    y,
                    // Decrement the coefficient of n.
                    // Can overflow, but the result is casted to uint256 so that the
                    // next value of y is "wrapped around" to a value between 0 and n - 1.
                    x - y * int256(quotient)
                );
            }

            if (gcd != 1) return 0; // No inverse exists.
            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
        }
    }

    /**
     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
     *
     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
     *
     * NOTE: this function does NOT check that `p` is a prime greater than `2`.
     */
    function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
        unchecked {
            return Math.modExp(a, p - 2, p);
        }
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
     *
     * Requirements:
     * - modulus can't be zero
     * - underlying staticcall to precompile must succeed
     *
     * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
     * sure the chain you're using it on supports the precompiled contract for modular exponentiation
     * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
     * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
     * interpreted as 0.
     */
    function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
        (bool success, uint256 result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
     * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
     * to operate modulo 0 or if the underlying precompile reverted.
     *
     * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
     * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
     * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
     * of a revert, but the result may be incorrectly interpreted as 0.
     */
    function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
        if (m == 0) return (false, 0);
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            // | Offset    | Content    | Content (Hex)                                                      |
            // |-----------|------------|--------------------------------------------------------------------|
            // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x60:0x7f | value of b | 0x<.............................................................b> |
            // | 0x80:0x9f | value of e | 0x<.............................................................e> |
            // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
            mstore(ptr, 0x20)
            mstore(add(ptr, 0x20), 0x20)
            mstore(add(ptr, 0x40), 0x20)
            mstore(add(ptr, 0x60), b)
            mstore(add(ptr, 0x80), e)
            mstore(add(ptr, 0xa0), m)

            // Given the result < m, it's guaranteed to fit in 32 bytes,
            // so we can use the memory scratch space located at offset 0.
            success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
            result := mload(0x00)
        }
    }

    /**
     * @dev Variant of {modExp} that supports inputs of arbitrary length.
     */
    function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
        (bool success, bytes memory result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
     */
    function tryModExp(
        bytes memory b,
        bytes memory e,
        bytes memory m
    ) internal view returns (bool success, bytes memory result) {
        if (_zeroBytes(m)) return (false, new bytes(0));

        uint256 mLen = m.length;

        // Encode call args in result and move the free memory pointer
        result = abi.encodePacked(b.length, e.length, mLen, b, e, m);

        assembly ("memory-safe") {
            let dataPtr := add(result, 0x20)
            // Write result on top of args to avoid allocating extra memory.
            success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
            // Overwrite the length.
            // result.length > returndatasize() is guaranteed because returndatasize() == m.length
            mstore(result, mLen)
            // Set the memory pointer after the returned data.
            mstore(0x40, add(dataPtr, mLen))
        }
    }

    /**
     * @dev Returns whether the provided byte array is zero.
     */
    function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
        for (uint256 i = 0; i < byteArray.length; ++i) {
            if (byteArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
     * using integer operations.
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        unchecked {
            // Take care of easy edge cases when a == 0 or a == 1
            if (a <= 1) {
                return a;
            }

            // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
            // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
            // the current value as `ε_n = | x_n - sqrt(a) |`.
            //
            // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
            // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
            // bigger than any uint256.
            //
            // By noticing that
            // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
            // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
            // to the msb function.
            uint256 aa = a;
            uint256 xn = 1;

            if (aa >= (1 << 128)) {
                aa >>= 128;
                xn <<= 64;
            }
            if (aa >= (1 << 64)) {
                aa >>= 64;
                xn <<= 32;
            }
            if (aa >= (1 << 32)) {
                aa >>= 32;
                xn <<= 16;
            }
            if (aa >= (1 << 16)) {
                aa >>= 16;
                xn <<= 8;
            }
            if (aa >= (1 << 8)) {
                aa >>= 8;
                xn <<= 4;
            }
            if (aa >= (1 << 4)) {
                aa >>= 4;
                xn <<= 2;
            }
            if (aa >= (1 << 2)) {
                xn <<= 1;
            }

            // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
            //
            // We can refine our estimation by noticing that the middle of that interval minimizes the error.
            // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
            // This is going to be our x_0 (and ε_0)
            xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)

            // From here, Newton's method give us:
            // x_{n+1} = (x_n + a / x_n) / 2
            //
            // One should note that:
            // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
            //              = ((x_n² + a) / (2 * x_n))² - a
            //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
            //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
            //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
            //              = (x_n² - a)² / (2 * x_n)²
            //              = ((x_n² - a) / (2 * x_n))²
            //              ≥ 0
            // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
            //
            // This gives us the proof of quadratic convergence of the sequence:
            // ε_{n+1} = | x_{n+1} - sqrt(a) |
            //         = | (x_n + a / x_n) / 2 - sqrt(a) |
            //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
            //         = | (x_n - sqrt(a))² / (2 * x_n) |
            //         = | ε_n² / (2 * x_n) |
            //         = ε_n² / | (2 * x_n) |
            //
            // For the first iteration, we have a special case where x_0 is known:
            // ε_1 = ε_0² / | (2 * x_0) |
            //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
            //     ≤ 2**(2*e-4) / (3 * 2**(e-1))
            //     ≤ 2**(e-3) / 3
            //     ≤ 2**(e-3-log2(3))
            //     ≤ 2**(e-4.5)
            //
            // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
            // ε_{n+1} = ε_n² / | (2 * x_n) |
            //         ≤ (2**(e-k))² / (2 * 2**(e-1))
            //         ≤ 2**(2*e-2*k) / 2**e
            //         ≤ 2**(e-2*k)
            xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above
            xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5
            xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9
            xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18
            xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36
            xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72

            // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
            // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
            // sqrt(a) or sqrt(a) + 1.
            return xn - SafeCast.toUint(xn > a / xn);
        }
    }

    /**
     * @dev Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 x) internal pure returns (uint256 r) {
        // If value has upper 128 bits set, log2 result is at least 128
        r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
        // If upper 64 bits of 128-bit half set, add 64 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
        // If upper 32 bits of 64-bit half set, add 32 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
        // If upper 16 bits of 32-bit half set, add 16 to result
        r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
        // If upper 8 bits of 16-bit half set, add 8 to result
        r |= SafeCast.toUint((x >> r) > 0xff) << 3;
        // If upper 4 bits of 8-bit half set, add 4 to result
        r |= SafeCast.toUint((x >> r) > 0xf) << 2;

        // Shifts value right by the current result and use it as an index into this lookup table:
        //
        // | x (4 bits) |  index  | table[index] = MSB position |
        // |------------|---------|-----------------------------|
        // |    0000    |    0    |        table[0] = 0         |
        // |    0001    |    1    |        table[1] = 0         |
        // |    0010    |    2    |        table[2] = 1         |
        // |    0011    |    3    |        table[3] = 1         |
        // |    0100    |    4    |        table[4] = 2         |
        // |    0101    |    5    |        table[5] = 2         |
        // |    0110    |    6    |        table[6] = 2         |
        // |    0111    |    7    |        table[7] = 2         |
        // |    1000    |    8    |        table[8] = 3         |
        // |    1001    |    9    |        table[9] = 3         |
        // |    1010    |   10    |        table[10] = 3        |
        // |    1011    |   11    |        table[11] = 3        |
        // |    1100    |   12    |        table[12] = 3        |
        // |    1101    |   13    |        table[13] = 3        |
        // |    1110    |   14    |        table[14] = 3        |
        // |    1111    |   15    |        table[15] = 3        |
        //
        // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
        assembly ("memory-safe") {
            r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
        }
    }

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

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

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

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 x) internal pure returns (uint256 r) {
        // If value has upper 128 bits set, log2 result is at least 128
        r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
        // If upper 64 bits of 128-bit half set, add 64 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
        // If upper 32 bits of 64-bit half set, add 32 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
        // If upper 16 bits of 32-bit half set, add 16 to result
        r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
        // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
        return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
    }

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

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: LicenseRef-Uniteum

pragma solidity ^0.8.30;

import {IERC20} from "ierc20/IERC20.sol";

/**
 * @title IMigratable
 * @notice Interface for tokens that support migration from an upstream version.
 * @dev Tokens implementing this interface can accept upstream tokens and issue
 *      an equivalent amount of this token in exchange.
 * @author Paul Reinholdtsen (reinholdtsen.eth)
 */
interface IMigratable {
    /**
     * @notice Upstream token this contract accepts for migration.
     * @dev Circulating supply is conserved across all migrations.
     * @return upstream token this contract accepts for migration.
     */
    function UPSTREAM() external view returns (IERC20 upstream);

    /**
     * @notice Migrate upstream tokens to this token.
     * @dev The caller must approve this contract to transfer the upstream tokens.
     *      The upstream tokens are transferred from the caller to this contract,
     *      and an equivalent amount of this token is minted/transferred to the caller.
     * @param amount The number of tokens to migrate.
     */
    function migrate(uint256 amount) external;

    /**
     * @notice Reverse migrate this token to its upstream token.
     * @dev The caller's tokens are transferred to this contract,
     *      and an equivalent amount of upstream tokens is transferred to the caller.
     * @param amount The number of tokens to reverse migrate.
     */
    function unmigrate(uint256 amount) external;

    /**
     * @notice Emitted when tokens are migrated from upstream to downstream.
     * @param upstream The upstream token address (source).
     * @param downstream The downstream token address (destination).
     * @param amount The number of tokens migrated.
     */
    event Migrated(address indexed upstream, address indexed downstream, uint256 amount);

    /**
     * @notice Emitted when tokens are reverse migrated from downstream to upstream.
     * @param upstream The upstream token address (destination).
     * @param downstream The downstream token address (source).
     * @param amount The number of tokens reverse migrated.
     */
    event Unmigrated(address indexed upstream, address indexed downstream, uint256 amount);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.30;

import {Clones} from "clones/Clones.sol";

/**
 * @title Prototype
 * @notice Base contract for self-cloning minimal proxy implementations using EIP-1167.
 * @dev
 * The contract deployed as the Prototype acts as:
 *   - the reference implementation with canonical logic, and
 *   - a factory that deterministically deploys minimal proxy clones of itself.
 *
 * Each clone:
 *   - delegates all logic to the Prototype via DELEGATECALL,
 *   - maintains its own isolated storage,
 *   - preserves the original msg.sender through the proxy,
 *   - inherits the same immutable PROTOTYPE address.
 *
 * **Deterministic Deployment:**
 * All clones are deployed with CREATE2 using salts derived from initialization
 * data via keccak256(abi.encode(initData)), ensuring predictable, repeatable
 * addresses. Calling __clone with identical initData will return the same
 * address without redeploying.
 *
 * **Usage Pattern:**
 * 1. Deploy Prototype implementation contract
 * 2. Call __clone(initData) to create instances
 * 3. Each clone is automatically initialized via __initialize(initData)
 * 4. Clones can call __clone to create more clones (forwarded to Prototype)
 *
 * @author Paul Reinholdtsen (reinholdtsen.eth)
 */
abstract contract Prototype {
    // ============ State Variables ============

    /**
     * @notice Address of the original Prototype implementation.
     * @dev Clones inherit this immutable value through bytecode; on the Prototype
     *      itself it equals address(this). This creates a shared reference point
     *      for all clones to delegate calls to and query state from.
     *
     *      Immutables are embedded in bytecode during deployment, so each clone's
     *      bytecode contains the Prototype address even though storage is separate.
     */
    address internal immutable PROTOTYPE = address(this);

    /**
     * @dev Registry mapping clone addresses to their CREATE2 salts.
     *      Only populated on the Prototype contract, not on clones.
     *
     *      Maps: clone address → CREATE2 salt
     *
     *      A non-zero value indicates the address was deployed as a valid clone.
     *      Used by isClone() for verification and to prevent duplicate deployments.
     */
    mapping(address => bytes32) private salts;

    // ============ View Functions ============

    /**
     * @notice Returns true if `check` is a clone of this Prototype.
     * @dev When called on the Prototype: checks the salts registry directly.
     *      When called on a clone: delegates to the Prototype for verification.
     *
     *      This pattern ensures a single source of truth (the Prototype's registry)
     *      while allowing verification from any context.
     *
     * @param check Address to examine.
     * @return yes True if the address was deployed as a clone via __clone().
     */
    function isClone(address check) public view returns (bool yes) {
        yes = address(this) == PROTOTYPE ? salts[check] != 0x0 : Prototype(PROTOTYPE).isClone(check);
    }

    /**
     * @notice Returns the immutable Prototype address.
     * @dev Identical for both the implementation and all clones because it reads
     *      from the immutable PROTOTYPE field embedded in bytecode.
     *
     *      Useful for:
     *      - Accessing the canonical registry (salts mapping)
     *      - Delegating operations back to the implementation
     *      - Verifying clone authenticity
     *
     * @return The address of the Prototype implementation contract.
     */
    function prototype() public view returns (address) {
        return PROTOTYPE;
    }

    /**
     * @notice Predicts the clone address for a given salt.
     * @dev Uses OpenZeppelin's Clones library to compute the deterministic address
     *      based on the Prototype address and salt. This is a view function that
     *      does not deploy anything.
     *
     *      The address is computed as: CREATE2(PROTOTYPE, salt, PROTOTYPE, initcode)
     *      where the deployer is the Prototype itself.
     *
     * @param newSalt The CREATE2 salt that will be used.
     * @return predicted The deterministic clone address that would be deployed.
     */
    function __predict(bytes32 newSalt) public view returns (address predicted) {
        predicted = Clones.predictDeterministicAddress(PROTOTYPE, newSalt, PROTOTYPE);
    }

    /**
     * @notice Predicts the clone address for initialization data.
     * @dev Salt is deterministically derived from initData as:
     *      keccak256(abi.encode(initData))
     *
     *      Note: abi.encode is used (not abi.encodePacked) to ensure proper
     *      ABI encoding with type information, preventing collisions.
     *
     *      This overload is the primary entry point for predicting addresses
     *      when you have initialization parameters but not a precomputed salt.
     *
     * @param initData Initialization calldata for the clone.
     * @return predicted Deterministic clone address.
     * @return newSalt The CREATE2 salt derived from initData.
     */
    function __predict(bytes memory initData) public view returns (address predicted, bytes32 newSalt) {
        newSalt = keccak256(abi.encode(initData));
        predicted = __predict(newSalt);
    }

    // ============ Factory Functions ============

    /**
     * @notice Deploys a deterministic minimal proxy clone.
     * @dev
     * **When called on the Prototype:**
     *   1. Computes salt from keccak256(abi.encode(initData))
     *   2. Predicts clone address using CREATE2 formula
     *   3. If no code at address: deploys clone, records salt, calls __initialize
     *   4. If code exists: returns existing address (idempotent)
     *   5. Calls __initialize(initData) on newly deployed clones only
     *
     * **When called on a clone:**
     *   - Forwards the request back to PROTOTYPE.__clone(initData)
     *   - This enables clones to create other clones transparently
     *
     * **Idempotency:**
     * Calling __clone with the same initData multiple times returns the same
     * address. Only the first call performs deployment and initialization.
     *
     * **Security:**
     * Only the Prototype can call __initialize due to onlyPrototype modifier.
     * Clones cannot initialize themselves or other clones directly.
     *
     * @param initData Initialization data passed to the clone's __initialize.
     * @return instance The deployed (or existing) clone address.
     * @return newSalt The CREATE2 salt used for deterministic deployment.
     */
    function __clone(bytes memory initData) public returns (address instance, bytes32 newSalt) {
        if (address(this) == PROTOTYPE) {
            (instance, newSalt) = __predict(initData);

            if (instance.code.length == 0) {
                instance = Clones.cloneDeterministic(PROTOTYPE, newSalt, 0);
                salts[instance] = newSalt;
                Prototype(instance).__initialize(initData);
            }
        } else {
            (instance, newSalt) = Prototype(PROTOTYPE).__clone(initData);
        }
    }

    /**
     * @notice Initialize a newly deployed clone.
     * @dev **Must be implemented by derived classes.**
     *
     *      **Security considerations:**
     *      - MUST use the onlyPrototype modifier to prevent unauthorized calls
     *      - SHOULD validate initData to prevent malicious initialization
     *      - SHOULD consider using a reentrancy guard if calling external contracts
     *      - MUST NOT assume msg.sender is the end user (it's always PROTOTYPE)
     *
     *      **Initialization pattern:**
     *      Decode initData, set storage variables, emit events. The actual user
     *      who called __clone is typically encoded in initData, not msg.sender.
     *
     *      **Called automatically** by __clone during clone deployment.
     *
     * @param initData ABI-encoded initialization parameters.
     */
    function __initialize(bytes memory initData) public virtual;

    // ============ Internal Functions ============

    /**
     * @notice Restricts calls to the Prototype implementation contract only.
     * @dev Applied to __initialize to ensure only the Prototype can initialize
     *      new clones during deployment. Prevents external actors or clones
     *      themselves from calling initialization logic.
     *
     *      Uses internal _onlyPrototype() for the actual check.
     */
    modifier onlyPrototype() {
        _onlyPrototype();
        _;
    }

    /**
     * @dev Reverts if msg.sender is not the Prototype implementation.
     *
     *      This check ensures that only the factory (Prototype) can call
     *      protected functions, preventing unauthorized initialization or
     *      configuration of clones.
     *
     *      Reverts with Unauthorized() custom error for gas efficiency.
     */
    function _onlyPrototype() internal view {
        if (msg.sender != PROTOTYPE) {
            revert Unauthorized();
        }
    }

    // ============ Errors ============

    /**
     * @notice Error raised when a caller lacks permission to execute a protected function.
     * @dev Thrown by the onlyPrototype modifier when msg.sender != PROTOTYPE.
     *      Custom errors are more gas-efficient than require strings.
     */
    error Unauthorized();
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.30;

import {Context} from "./Context.sol";
import {IERC20Metadata, IERC20} from "ierc20/IERC20Metadata.sol";
import {IERC20Errors} from "ierc20/IERC20Errors.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string internal _name;
    string internal _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * Both values are immutable: they can only be set once during construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     *
     * ```solidity
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner`'s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

File 11 of 21 : Term.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.30;

/**
 * @title Term — Unit Term Type
 * @dev Type for unit term operations.
 * Base unit terms are packed into uint:
 * The last two bytes (30, 31) are a rational exponent.
 * Symbolic terms have the first 30 bytes as the base symbol.
 * Address terms have the first byte = 1, and the next 20 bytes are an address.
 * +0......0|1.........................20|21................29|30...........31+
 * | Symbol                                                   |    Exponent   |
 * |----------------------------------------------------------| ± num / den   |
 * | Type=1 | Address [1..20]            | Reserved           |  int8 | uint8 |
 * +255................................96|95................16|15....8|7.....0+
 * Example 1: meter^2:3
 * |6d 6574657200000000000000000000000000000000 000000000000000000 02 03|
 * |  |                                        |                  |  |  |
 * |01 c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 000000000000000000 ff 01|
 * Example 2: 1/[address of WETH]
 */
type Term is uint256;

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

import {Rational, Rational8} from "./Rational.sol";
import {Strings} from "strings/Strings.sol";

/**
 * @title Rationals
 * @notice Library for rational number arithmetic with 128-bit and 8-bit representations
 * @dev Rational: int256 with high 128 bits = numerator, low 128 bits = denominator
 *      Rational8: int16 with high 8 bits = numerator, low 8 bits = denominator
 *      All rationals are stored in reduced form (lowest terms)
 */
library Rationals {
    int128 constant NUMERATOR_MAX = type(int128).max;
    uint128 constant DENOMINATOR_MAX = type(uint128).max;
    int8 constant NUMERATOR8_MAX = type(int8).max;
    uint8 constant DENOMINATOR8_MAX = type(uint8).max;

    using Rationals for *;
    using Strings for *;

    /**
     * @dev Reverts when a denominator is zero
     */
    error ZeroDenominator();

    /**
     * @dev Reverts when a value cannot safely downcast to a smaller type
     */
    error ExponentTooBig();
    error DenominatorTooBig(uint256 d);
    error NumeratorTooBig(int256 n);

    /**
     * @dev Reverts when exact Rat16 encoding is impossible
     */
    error Rat16EncodingImpossible();

    /**
     * @notice Unwraps a Rational to its underlying int256 representation
     */
    function raw(Rational n) internal pure returns (int256) {
        return Rational.unwrap(n);
    }

    /**
     * @notice Unwraps a Rational8 to its underlying int16 representation
     */
    function raw(Rational8 n) internal pure returns (int256) {
        return Rational8.unwrap(n);
    }

    /**
     * @notice Decodes a Ratio128 value into numerator and denominator
     * @param a A Ratio128-encoded int value
     * @return n Signed 128-bit numerator
     * @return d Unsigned 128-bit denominator
     */
    function parts(Rational a) internal pure returns (int256 n, uint256 d) {
        int256 r = a.raw();
        n = r >> 128;
        // forge-lint: disable-next-line(unsafe-typecast)
        d = uint256(r) & DENOMINATOR_MAX;
    }

    /**
     * @notice Encodes a numerator and denominator as a Rational, reduced to lowest terms
     * @param n Signed 128-bit numerator
     * @param d Unsigned 128-bit denominator (must be nonzero)
     * @return a Encoded Rational value in reduced form
     */
    function divRational(int256 n, uint256 d) internal pure returns (Rational a) {
        if (d == 0) {
            revert ZeroDenominator();
        }

        uint256 g = gcd(_abs(n), d);
        // forge-lint: disable-next-line(unsafe-typecast)
        n /= int128(uint128(g));
        // forge-lint: disable-next-line(unsafe-typecast)
        d /= uint128(g);

        if (n < -NUMERATOR_MAX || NUMERATOR_MAX < n) {
            revert NumeratorTooBig(n);
        }

        if (d > DENOMINATOR_MAX) {
            revert DenominatorTooBig(d);
        }

        // forge-lint: disable-next-line(unsafe-typecast)
        a = Rational.wrap((n << 128) | int256(uint256(d)));
    }

    /**
     * @notice Negates a Ratio128-encoded value
     * @param a A Ratio128-encoded int value
     * @return Negated Ratio128-encoded value
     */
    function neg(Rational a) internal pure returns (Rational) {
        (int256 n, uint256 d) = a.parts();
        return (-n).divRational(d);
    }

    /**
     * @notice Adds two Rational values and returns normalized result
     * @dev Computes a/b + c/d by finding common denominator using LCM
     */
    function add(Rational a, Rational b) internal pure returns (Rational) {
        (int256 an, uint256 ad) = a.parts();
        (int256 bn, uint256 bd) = b.parts();
        uint256 gd = gcd(ad, bd);
        // forge-lint: disable-next-line(divide-before-multiply)
        uint256 d = (ad / gd) * bd;
        // forge-lint: disable-next-line(unsafe-typecast)
        int256 n = an * int256(d / ad) + bn * int256(d / bd);

        return n.divRational(d);
    }

    /**
     * @notice Subtracts two Rational values and returns normalized result
     * @dev Computes a - b as a + (-b)
     */
    function sub(Rational a, Rational b) internal pure returns (Rational) {
        return a.add(b.neg());
    }

    /**
     * @notice Multiplies two Rational values and returns normalized result
     * @dev Computes (a/b) * (c/d) = (a*c)/(b*d), then reduces using GCD
     */
    function mul(Rational a, Rational b) internal pure returns (Rational) {
        (int256 an, uint256 ad) = a.parts();
        (int256 bn, uint256 bd) = b.parts();
        int256 n = int256(an) * int256(bn);
        uint256 d = uint256(ad) * uint256(bd);
        uint256 g = gcd(uint256(_abs(n)), d);
        return (n / g.toInt256()).divRational(d / g);
    }

    /**
     * @notice Divides Rational a by Rational b and returns normalized result
     * @dev Computes (a/b) / (c/d) = (a*d)/(b*c), handling sign normalization
     */
    function div(Rational a, Rational b) internal pure returns (Rational r) {
        (int256 an, uint256 ad) = a.parts();
        (int256 bn, uint256 bd) = b.parts();
        if (bn == 0) {
            revert ZeroDenominator();
        }
        int256 n = an * bd.toInt256();
        int256 d = ad.toInt256() * bn;
        if (d < 0) {
            n = -n;
            d = -d;
        }
        // forge-lint: disable-next-line(unsafe-typecast)
        r = n.divRational(uint256(d));
    }

    /**
     * @notice Extracts the numerator from a Rational8 value
     */
    function numerator(Rational8 a8) internal pure returns (int256 n) {
        n = int8(a8.raw() >> 8);
    }

    /**
     * @notice Extracts the denominator from a Rational8 value
     */
    function denominator(Rational8 a8) internal pure returns (uint256 d) {
        d = uint8(uint256(a8.raw()));
    }

    /**
     * @notice Decodes a Rational8 value into numerator and denominator
     * @param a A Rational8-encoded int value
     * @return n Signed 8-bit numerator
     * @return d Unsigned 8-bit denominator
     */
    function parts(Rational8 a) internal pure returns (int8 n, uint8 d) {
        int256 r = a.raw();
        // forge-lint: disable-next-line(unsafe-typecast)
        n = int8(r >> 8);
        // forge-lint: disable-next-line(unsafe-typecast)
        d = uint8(uint256(r) & DENOMINATOR8_MAX);
    }

    /**
     * @notice Encodes a numerator and denominator as a Rational8, reduced to lowest terms
     * @param n Signed 8-bit numerator
     * @param d Unsigned 8-bit denominator (must be nonzero)
     * @return a Rational8 value in reduced form
     */
    function divRational8(int256 n, uint256 d) internal pure returns (Rational8 a) {
        if (d == 0) {
            revert ZeroDenominator();
        }

        uint256 g = gcd(_abs(n), d);
        // forge-lint: disable-next-line(unsafe-typecast)
        n /= int256(g);
        // forge-lint: disable-next-line(unsafe-typecast)
        d /= g;

        if (n < -NUMERATOR8_MAX || n > NUMERATOR8_MAX) {
            revert NumeratorTooBig(n);
        }
        if (d > DENOMINATOR8_MAX) {
            revert DenominatorTooBig(d);
        }
        // forge-lint: disable-next-line(unsafe-typecast)
        int256 encoded = (n << 8) | int256(uint256(d));
        // forge-lint: disable-next-line(unsafe-typecast)
        a = Rational8.wrap(int16(encoded));
    }

    /**
     * @notice Negates a Rational8-encoded value
     * @param a A Rational8-encoded int16 value
     * @return Negated Rational8-encoded value
     */
    function neg(Rational8 a) internal pure returns (Rational8) {
        (int8 n, uint8 d) = a.parts();
        return divRational8(-n, d);
    }

    /**
     * @notice Adds two Rational8 values by converting to Rational, adding, then converting back
     */
    function add(Rational8 a, Rational8 b) internal pure returns (Rational8) {
        return a.toRational().add(b.toRational()).toRational8();
    }

    /**
     * @notice Divides a Rational8 values by an unsigned integer
     */
    function div(Rational8 a, uint256 b) internal pure returns (Rational8 q) {
        (int256 n, uint256 d) = a.parts();
        q = n.divRational8(d * b);
    }

    /**
     * @notice Converts a Rational to an exact Rational8, reverts if not representable
     */
    function toRational8(Rational a) internal pure returns (Rational8 a8) {
        (int256 n, uint256 d) = a.parts();
        a8 = n.divRational8(d);
    }

    /**
     * @notice Converts a Rational8 value to Rational
     */
    function toRational(Rational8 a8) internal pure returns (Rational a) {
        (int256 n, uint256 d) = a8.parts();
        a = n.divRational(d);
    }

    /**
     * @notice Safely converts uint256 to int256, reverting on overflow
     */
    function toInt256(uint256 x) internal pure returns (int256 y) {
        if (x <= uint256(type(int256).max)) {
            // forge-lint: disable-next-line(unsafe-typecast)
            y = int256(uint256(x));
        } else {
            revert ExponentTooBig();
        }
    }

    /**
     * @notice Computes greatest common divisor using Euclidean algorithm
     */
    function gcd(uint256 a, uint256 b) public pure returns (uint256) {
        while (b != 0) {
            uint256 t = b;
            b = a % b;
            a = t;
        }
        return a;
    }

    /**
     * @notice Computes least common multiple of two denominators
     * @dev Uses identity lcm(a, b) = (a / gcd(a, b)) * b
     */
    function lcm(uint256 a, uint256 b) public pure returns (uint256) {
        // forge-lint: disable-next-line(divide-before-multiply)
        return (a / gcd(a, b)) * b;
    }

    /**
     * @notice Returns the absolute value of an int256
     * @dev Handles type(int256).min safely using unchecked negation
     */
    function _abs(int256 x) internal pure returns (uint256) {
        if (x >= 0) {
            // forge-lint: disable-next-line(unsafe-typecast)
            return uint256(x);
        } else {
            unchecked {
                // forge-lint: disable-next-line(unsafe-typecast)
                return uint256(-x);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "math/Math.sol";
import {SafeCast} from "math/SafeCast.sol";
import {SignedMath} from "math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    using SafeCast for *;

    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;
    uint256 private constant SPECIAL_CHARS_LOOKUP =
        (1 << 0x08) | // backspace
            (1 << 0x09) | // tab
            (1 << 0x0a) | // newline
            (1 << 0x0c) | // form feed
            (1 << 0x0d) | // carriage return
            (1 << 0x22) | // double quote
            (1 << 0x5c); // backslash

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev The string being parsed contains characters that are not in scope of the given base.
     */
    error StringsInvalidChar();

    /**
     * @dev The string being parsed is not a properly formatted address.
     */
    error StringsInvalidAddressFormat();

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            assembly ("memory-safe") {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                assembly ("memory-safe") {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
     * representation, according to EIP-55.
     */
    function toChecksumHexString(address addr) internal pure returns (string memory) {
        bytes memory buffer = bytes(toHexString(addr));

        // hash the hex part of buffer (skip length + 2 bytes, length 40)
        uint256 hashValue;
        assembly ("memory-safe") {
            hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
        }

        for (uint256 i = 41; i > 1; --i) {
            // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
            if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
                // case shift by xoring with 0x20
                buffer[i] ^= 0x20;
            }
            hashValue >>= 4;
        }
        return string(buffer);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }

    /**
     * @dev Parse a decimal string and returns the value as a `uint256`.
     *
     * Requirements:
     * - The string must be formatted as `[0-9]*`
     * - The result must fit into an `uint256` type
     */
    function parseUint(string memory input) internal pure returns (uint256) {
        return parseUint(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `[0-9]*`
     * - The result must fit into an `uint256` type
     */
    function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
        (bool success, uint256 value) = tryParseUint(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
        return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
     * character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseUint(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, uint256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseUintUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseUintUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, uint256 value) {
        bytes memory buffer = bytes(input);

        uint256 result = 0;
        for (uint256 i = begin; i < end; ++i) {
            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
            if (chr > 9) return (false, 0);
            result *= 10;
            result += chr;
        }
        return (true, result);
    }

    /**
     * @dev Parse a decimal string and returns the value as a `int256`.
     *
     * Requirements:
     * - The string must be formatted as `[-+]?[0-9]*`
     * - The result must fit in an `int256` type.
     */
    function parseInt(string memory input) internal pure returns (int256) {
        return parseInt(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `[-+]?[0-9]*`
     * - The result must fit in an `int256` type.
     */
    function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
        (bool success, int256 value) = tryParseInt(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
     * the result does not fit in a `int256`.
     *
     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
     */
    function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
        return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
    }

    uint256 private constant ABS_MIN_INT256 = 2 ** 255;

    /**
     * @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
     * character or if the result does not fit in a `int256`.
     *
     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
     */
    function tryParseInt(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, int256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseIntUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseIntUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, int256 value) {
        bytes memory buffer = bytes(input);

        // Check presence of a negative sign.
        bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        bool positiveSign = sign == bytes1("+");
        bool negativeSign = sign == bytes1("-");
        uint256 offset = (positiveSign || negativeSign).toUint();

        (bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);

        if (absSuccess && absValue < ABS_MIN_INT256) {
            return (true, negativeSign ? -int256(absValue) : int256(absValue));
        } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
            return (true, type(int256).min);
        } else return (false, 0);
    }

    /**
     * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
     *
     * Requirements:
     * - The string must be formatted as `(0x)?[0-9a-fA-F]*`
     * - The result must fit in an `uint256` type.
     */
    function parseHexUint(string memory input) internal pure returns (uint256) {
        return parseHexUint(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
     * - The result must fit in an `uint256` type.
     */
    function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
        (bool success, uint256 value) = tryParseHexUint(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
        return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
     * invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseHexUint(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, uint256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseHexUintUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseHexUintUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, uint256 value) {
        bytes memory buffer = bytes(input);

        // skip 0x prefix if present
        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        uint256 offset = hasPrefix.toUint() * 2;

        uint256 result = 0;
        for (uint256 i = begin + offset; i < end; ++i) {
            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
            if (chr > 15) return (false, 0);
            result *= 16;
            unchecked {
                // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
                // This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
                result += chr;
            }
        }
        return (true, result);
    }

    /**
     * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
     *
     * Requirements:
     * - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
     */
    function parseAddress(string memory input) internal pure returns (address) {
        return parseAddress(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
     */
    function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
        (bool success, address value) = tryParseAddress(input, begin, end);
        if (!success) revert StringsInvalidAddressFormat();
        return value;
    }

    /**
     * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
     * formatted address. See {parseAddress-string} requirements.
     */
    function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
        return tryParseAddress(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
     * formatted address. See {parseAddress-string-uint256-uint256} requirements.
     */
    function tryParseAddress(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, address value) {
        if (end > bytes(input).length || begin > end) return (false, address(0));

        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        uint256 expectedLength = 40 + hasPrefix.toUint() * 2;

        // check that input is the correct length
        if (end - begin == expectedLength) {
            // length guarantees that this does not overflow, and value is at most type(uint160).max
            (bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
            return (s, address(uint160(v)));
        } else {
            return (false, address(0));
        }
    }

    function _tryParseChr(bytes1 chr) private pure returns (uint8) {
        uint8 value = uint8(chr);

        // Try to parse `chr`:
        // - Case 1: [0-9]
        // - Case 2: [a-f]
        // - Case 3: [A-F]
        // - otherwise not supported
        unchecked {
            if (value > 47 && value < 58) value -= 48;
            else if (value > 96 && value < 103) value -= 87;
            else if (value > 64 && value < 71) value -= 55;
            else return type(uint8).max;
        }

        return value;
    }

    /**
     * @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
     *
     * WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
     *
     * NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
     * RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
     * characters that are not in this range, but other tooling may provide different results.
     */
    function escapeJSON(string memory input) internal pure returns (string memory) {
        bytes memory buffer = bytes(input);
        bytes memory output = new bytes(2 * buffer.length); // worst case scenario
        uint256 outputLength = 0;

        for (uint256 i; i < buffer.length; ++i) {
            bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
            if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
                output[outputLength++] = "\\";
                if (char == 0x08) output[outputLength++] = "b";
                else if (char == 0x09) output[outputLength++] = "t";
                else if (char == 0x0a) output[outputLength++] = "n";
                else if (char == 0x0c) output[outputLength++] = "f";
                else if (char == 0x0d) output[outputLength++] = "r";
                else if (char == 0x5c) output[outputLength++] = "\\";
                else if (char == 0x22) {
                    // solhint-disable-next-line quotes
                    output[outputLength++] = '"';
                }
            } else {
                output[outputLength++] = char;
            }
        }
        // write the actual length and deallocate unused memory
        assembly ("memory-safe") {
            mstore(output, outputLength)
            mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
        }

        return string(output);
    }

    /**
     * @dev Reads a bytes32 from a bytes array without bounds checking.
     *
     * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
     * assembly block as such would prevent some optimizations.
     */
    function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
        // This is not memory safe in the general case, but all calls to this private function are within bounds.
        assembly ("memory-safe") {
            value := mload(add(buffer, add(0x20, offset)))
        }
    }
}

// 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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)

pragma solidity ^0.8.20;

/**
 * @dev Helper library for emitting standardized panic codes.
 *
 * ```solidity
 * contract Example {
 *      using Panic for uint256;
 *
 *      // Use any of the declared internal constants
 *      function foo() { Panic.GENERIC.panic(); }
 *
 *      // Alternatively
 *      function foo() { Panic.panic(Panic.GENERIC); }
 * }
 * ```
 *
 * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
 *
 * _Available since v5.1._
 */
// slither-disable-next-line unused-state
library Panic {
    /// @dev generic / unspecified error
    uint256 internal constant GENERIC = 0x00;
    /// @dev used by the assert() builtin
    uint256 internal constant ASSERT = 0x01;
    /// @dev arithmetic underflow or overflow
    uint256 internal constant UNDER_OVERFLOW = 0x11;
    /// @dev division or modulo by zero
    uint256 internal constant DIVISION_BY_ZERO = 0x12;
    /// @dev enum conversion error
    uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
    /// @dev invalid encoding in storage
    uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
    /// @dev empty array pop
    uint256 internal constant EMPTY_ARRAY_POP = 0x31;
    /// @dev array out of bounds access
    uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    /// @dev resource error (too large allocation or too large array)
    uint256 internal constant RESOURCE_ERROR = 0x41;
    /// @dev calling invalid internal function
    uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;

    /// @dev Reverts with a panic code. Recommended to use with
    /// the internal constants with predefined codes.
    function panic(uint256 code) internal pure {
        assembly ("memory-safe") {
            mstore(0x00, 0x4e487b71)
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }
}

File 16 of 21 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }

    /**
     * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
     */
    function toUint(bool b) internal pure returns (uint256 u) {
        assembly ("memory-safe") {
            u := iszero(iszero(b))
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/Clones.sol)

pragma solidity ^0.8.30;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * This is a stripped-down version of OpenZeppelin's Clones library (v5.3.0), containing only
 * deterministic CREATE2 deployment functions with explicit value parameter support.
 * The following functions have been removed: clone(), and the overloads of cloneDeterministic()
 * and predictDeterministicAddress() that omit the value or deployer parameters.
 *
 * Original source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.3.0/contracts/proxy/Clones.sol
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple times will revert, since
     * the clones cannot be deployed twice at the same address.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function cloneDeterministic(
        address implementation,
        bytes32 salt,
        uint256 value
    ) internal returns (address instance) {
        assembly ("memory-safe") {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(
                0x00,
                or(
                    shr(0xe8, shl(0x60, implementation)),
                    0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000
                )
            )
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(
                0x20,
                or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)
            )
            instance := create2(value, 0x09, 0x37, salt)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := and(
                keccak256(add(ptr, 0x43), 0x55),
                0xffffffffffffffffffffffffffffffffffffffff
            )
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.30;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 19 of 21 : IERC20Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.30;

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

File 20 of 21 : Rational.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.30;

type Rational is int256;
type Rational8 is int16;

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
        }
    }

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

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
            // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
            // taking advantage of the most significant (or "sign" bit) in two's complement representation.
            // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
            // the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
            int256 mask = n >> 255;

            // A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
            return uint256((n + mask) ^ mask);
        }
    }
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "clones/=lib/clones/",
    "ierc20/=lib/ierc20/",
    "erc20/=lib/erc20/",
    "math/=lib/math/",
    "strings/=lib/strings/",
    "panic/=lib/panic/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "debug": {
    "revertStrings": "default"
  }
}

Contract ABI

API
[{"inputs":[{"internalType":"contract IERC20","name":"upstream","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BaseSymbolTooBig","type":"error"},{"inputs":[{"internalType":"uint256","name":"d","type":"uint256"}],"name":"DenominatorTooBig","type":"error"},{"inputs":[],"name":"DuplicateUnits","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[],"name":"ExponentTooBig","type":"error"},{"inputs":[],"name":"FunctionCalledOnOne","type":"error"},{"inputs":[],"name":"FunctionNotCalledOnOne","type":"error"},{"inputs":[{"internalType":"contract IUnit","name":"unit","type":"address"},{"internalType":"int256","name":"supply","type":"int256"}],"name":"NegativeSupply","type":"error"},{"inputs":[{"internalType":"int256","name":"n","type":"int256"}],"name":"NumeratorTooBig","type":"error"},{"inputs":[],"name":"ReentryForbidden","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"StringsInsufficientHexLength","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"bytes1","name":"char","type":"bytes1"}],"name":"UnexpectedCharacter","type":"error"},{"inputs":[],"name":"UnexpectedEndOfInput","type":"error"},{"inputs":[],"name":"ZeroDenominator","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":"holder","type":"address"},{"indexed":true,"internalType":"contract IUnit","name":"unit","type":"address"},{"indexed":false,"internalType":"int256","name":"du","type":"int256"},{"indexed":false,"internalType":"int256","name":"dv","type":"int256"},{"indexed":false,"internalType":"int256","name":"dw","type":"int256"}],"name":"Forge","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Migrate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"upstream","type":"address"},{"indexed":true,"internalType":"address","name":"downstream","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Migrated","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IUnit","name":"unit","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"anchor","type":"address"},{"indexed":true,"internalType":"bytes32","name":"hash","type":"bytes32"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"}],"name":"UnitCreate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unmigrate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"upstream","type":"address"},{"indexed":true,"internalType":"address","name":"downstream","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unmigrated","type":"event"},{"inputs":[],"name":"NAME_PREFIX","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_MINTED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_SYMBOL","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPSTREAM","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"units","type":"uint256"}],"name":"__burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"initData","type":"bytes"}],"name":"__clone","outputs":[{"internalType":"address","name":"instance","type":"address"},{"internalType":"bytes32","name":"newSalt","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"contract IUnit","name":"U","type":"address"},{"internalType":"contract IUnit","name":"V","type":"address"},{"internalType":"int256","name":"du","type":"int256"},{"internalType":"int256","name":"dv","type":"int256"},{"internalType":"int256","name":"dw","type":"int256"}],"name":"__forge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"initData","type":"bytes"}],"name":"__initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"units","type":"uint256"}],"name":"__mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"__nonReentrantAfter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"__nonReentrantBefore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"initData","type":"bytes"}],"name":"__predict","outputs":[{"internalType":"address","name":"predicted","type":"address"},{"internalType":"bytes32","name":"newSalt","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newSalt","type":"bytes32"}],"name":"__predict","outputs":[{"internalType":"address","name":"predicted","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":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"anchor","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"anchored","outputs":[{"internalType":"contract IUnit","name":"unit","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"anchoredPredict","outputs":[{"internalType":"contract IUnit","name":"unit","type":"address"},{"internalType":"string","name":"canonical","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"anchoredSymbol","outputs":[{"internalType":"string","name":"s","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","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":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"du","type":"int256"},{"internalType":"int256","name":"dv","type":"int256"}],"name":"forge","outputs":[{"internalType":"int256","name":"dw","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IUnit","name":"V","type":"address"},{"internalType":"int256","name":"du","type":"int256"},{"internalType":"int256","name":"dv","type":"int256"}],"name":"forge","outputs":[{"internalType":"contract IUnit","name":"W","type":"address"},{"internalType":"int256","name":"dw","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"du","type":"int256"},{"internalType":"int256","name":"dv","type":"int256"}],"name":"forgeQuote","outputs":[{"internalType":"int256","name":"dw","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IUnit","name":"V","type":"address"},{"internalType":"int256","name":"du","type":"int256"},{"internalType":"int256","name":"dv","type":"int256"}],"name":"forgeQuote","outputs":[{"internalType":"contract IUnit","name":"W","type":"address"},{"internalType":"int256","name":"dw","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IUnit","name":"V","type":"address"}],"name":"invariant","outputs":[{"internalType":"contract IUnit","name":"W","type":"address"},{"internalType":"uint256","name":"u","type":"uint256"},{"internalType":"uint256","name":"v","type":"uint256"},{"internalType":"uint256","name":"w","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"invariant","outputs":[{"internalType":"uint256","name":"u","type":"uint256"},{"internalType":"uint256","name":"v","type":"uint256"},{"internalType":"uint256","name":"w","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"u","type":"uint256"},{"internalType":"uint256","name":"v","type":"uint256"}],"name":"invariant","outputs":[{"internalType":"uint256","name":"w","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"check","type":"address"}],"name":"isClone","outputs":[{"internalType":"bool","name":"yes","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"units","type":"uint256"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IUnit","name":"multiplier","type":"address"}],"name":"multiply","outputs":[{"internalType":"contract IUnit","name":"unit","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"expression","type":"string"}],"name":"multiply","outputs":[{"internalType":"contract IUnit","name":"unit","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"one","outputs":[{"internalType":"contract IUnit","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IUnit","name":"multiplier","type":"address"}],"name":"product","outputs":[{"internalType":"contract IUnit","name":"unit","type":"address"},{"internalType":"string","name":"canonical","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"expression","type":"string"}],"name":"product","outputs":[{"internalType":"contract IUnit","name":"unit","type":"address"},{"internalType":"string","name":"canonical","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prototype","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reciprocal","outputs":[{"internalType":"contract IUnit","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IUnit","name":"","type":"address"}],"name":"reserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sqrt","outputs":[{"internalType":"contract IUnit","name":"unit","type":"address"},{"internalType":"string","name":"canonical","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sqrtResolve","outputs":[{"internalType":"contract IUnit","name":"root","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"units","type":"uint256"}],"name":"unmigrate","outputs":[],"stateMutability":"nonpayable","type":"function"}]

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

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