false
true
0

Contract Address Details

0xffff42F13E48b286B81Bc82937a96f0C654Bffff

Token
MOON (MOON)
Creator
0x7cdccd–c7a9a2 at 0x80c91d–db1d53
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
27017145
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
Moon




Optimization enabled
true
Compiler version
v0.8.24+commit.e11b9ed9




Optimization runs
800
EVM Version
paris




Verified at
2026-06-19T02:22:15.188159Z

Constructor Arguments

000000000000000000000000b915c09b1f28a6711aa42c54b5044a16896e55090000000000000000000000000000b08b88fdee1a65e52f942aa9612a093a0000

Arg [0] (address) : 0xb915c09b1f28a6711aa42c54b5044a16896e5509
Arg [1] (address) : 0x0000b08b88fdee1a65e52f942aa9612a093a0000

              

contracts/Moon.sol

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

/*
 ┌────────────────────────────────────────────────────────────────────────┐
 │                                                                        │
 │                 ███    ███  ██████   ██████  ███    ██                 │
 │                 ████  ████ ██    ██ ██    ██ ████   ██                 │
 │                 ██ ████ ██ ██    ██ ██    ██ ██ ██  ██                 │
 │                 ██  ██  ██ ██    ██ ██    ██ ██  ██ ██                 │
 │                 ██      ██  ██████   ██████  ██   ████                 │
 │                                                                        │
 │        MOON turns market activity into rewards for SUN holders.        │
 │                                                                        │
 │  How it works                                                          │
 │   • Pair your token with MOON on any V2 DEX.                           │
 │   • MOON takes a small tax on all swaps and uses it to buy your token. │
 │   • SUN gives these tokens directly to its holders.                    │
 │   • Transfers, LP operations, and non-V2 DEX's are untaxed.            │
 │                                                                        │
 │  Why pair with MOON?                                                   │
 │   • Constant buy pressure: Tax from every swap buys your token,        │
 │     regardless of market direction.                                    │
 │   • Market movement drives awareness: Natural price movement           │
 │     triggers swaps that expose your token directly to SUN holders.     │
 │   • Liquidity-positive design: During swapBacks, A portion of the      │
 │     output is left in the pool to strengthen liquidity.                │
 │                                                                        │
 │  Getting started                                                       │
 │   • Pairing your token with MOON on any V2 connects it automatically.  │
 │   • When adding liquidity, select your token first in the router to    │
 │     avoid swap tax.                                                    │
 │   • Remove liquidity anytime, tax free.                                │
 │                                                                        │
 └────────────────────────────────────────────────────────────────────────┘
*/

/*────────────────────────────  INTERFACES  ─────────────────────────────*/

interface IERC20 {
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);
    function approve(address spender, uint amount) external returns (bool);
    function transfer(address recipient, uint amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}

interface IKernel {
    function isUnlocked(address sender, address owner)
        external
        view
        returns (bool);

    function onMoonTransfer(
        address from,
        address to,
        uint256 amount
    )
        external
        returns (int fTax, int tTax);
}

/*────────────────────────────────  MOON  ───────────────────────────────*/

/// @title Moon (MOON)
/// @notice ERC-20 token with integrated pair-aware taxation.
/// impl, watty
contract Moon is IERC20 {

    /*/////////////////////////////////////////////////////////////////////////
                                      METADATA
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Token name.
    string public constant name = "MOON";
    /// @notice Token symbol.
    string public constant symbol = "MOON";
    /// @notice Token decimals (fixed at 18).
    uint8  public constant decimals = 18;

    /*/////////////////////////////////////////////////////////////////////////
                                       ERRORS
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Reentrancy guard violation.
    error Reentrancy();

    /// @dev Function restricted to callable only by kernel.
    error InternalOnly();

    /*/////////////////////////////////////////////////////////////////////////
                              IMMUTABLES AND CONSTANTS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Protocol coordinator that connects SUN and MOON to the engine.
    address private immutable kernel;

    /// @notice Peer token contract.
    address private immutable sun;

    /// @notice Max Swap Tax rate (cap on kernel taxation)
    uint private constant MAX_TAX = 6e16; // 6%

    /// @notice Requested gas cap for `onMoonTransfer`.
    /// @dev Actual forwarded gas is lower, callee can expect at least 95%.
    uint private constant HOOK_GAS_CAP = 1_200_000;

    /// @notice Conventional burn sink address (irretrievable).
    address private constant DEAD =
        0x000000000000000000000000000000000000dEaD;

    /*/////////////////////////////////////////////////////////////////////////
                                  ERC-20 STORAGE
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Current total supply of MOON tokens in existence.
    /// @dev This value is initialized at deployment and monotonically decreases
    ///      as tokens are burned. There is no mechanism to increase supply.
    uint public override totalSupply;

    /// @notice Current MOON balances.
    mapping(address => uint) internal balances;

    /// @notice Kernel spendable taxable balances.
    /// @dev Separate from ERC-20 allowance. Only the hidden kernel path uses this.
    mapping(address => uint) internal taxable;

    /// @dev ERC-20 allowances: owner => (spender => amount).
    mapping(address => mapping(address => uint)) internal allowances;

    /*/////////////////////////////////////////////////////////////////////////
                            SHARED REENTRANCY GUARD
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Selector for MOON-controlled shared lock sync through SUN's
    /// internal communication channel.
    bytes4 private constant _SUN_REENTRANCY_SELECTOR =
        bytes4(keccak256("sharedReentrancy(bool)"));

    /// @notice Shared protocol reentrancy guard.
    /// @dev
    /// - SUN stores the single shared lock for both tokens.
    /// - Entering MOON acquires that lock through SUN's trusted fallback route.
    /// - Exiting MOON releases it the same way.
    /// - A nested guarded entry reverts from SUN.
    modifier nonReentrant() {
        _setSharedLock(true);
        _;
        _setSharedLock(false);
    }

    /// @dev Acquires or releases the shared protocol lock stored in SUN.
    /// `lock == true` attempts to enter the shared guard.
    /// `lock == false` releases it.
    /// Bubbles SUN's exact revert reason on failure.
    function _setSharedLock(bool lock) private {
        (bool ok, bytes memory ret) = sun.call(
            abi.encodeWithSelector(_SUN_REENTRANCY_SELECTOR, lock)
        );

        if (!ok) {
            assembly ("memory-safe") {
                revert(add(ret, 0x20), mload(ret))
            }
        }
    }

    /*/////////////////////////////////////////////////////////////////////////
                                    CONSTRUCTOR
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Initializes Moon: mints full MOON supply and primes kernel spend.
    constructor(address _kernel, address _sun)
    {
        require(_kernel != address(0) && _sun != address(0), "Zero delegator");

        kernel = _kernel;
        sun = _sun;

        totalSupply = 1e27;
        balances[msg.sender] = totalSupply;
        emit Transfer(address(0), msg.sender, totalSupply);

        // MOON's own inventory is always kernel-spendable.
        taxable[address(this)] = type(uint).max;
    }

    /*/////////////////////////////////////////////////////////////////////////
                                  EIP-2612 PERMIT
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice EIP-2612 permit nonces.
    mapping(address => uint256) public nonces;

    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
    bytes32 private constant _PERMIT_TYPEHASH =
        keccak256(
            "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
        );

    // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
    bytes32 private constant _EIP712_DOMAIN_TYPEHASH =
        keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );

    // secp256k1n / 2
    uint256 private constant _SECP256K1N_HALF =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;

    /// @notice Returns the EIP-712 domain separator used for permit signatures.
    /// @dev Uses:
    /// - token name from `name`
    /// - version = "1"
    /// - current chain ID
    /// - this contract address
    function DOMAIN_SEPARATOR() public view returns (bytes32) {
        return keccak256(
            abi.encode(
                _EIP712_DOMAIN_TYPEHASH,
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );
    }

    /// @notice Approve by signature, per EIP-2612.
    /// @param owner Token owner signing the permit.
    /// @param spender Address receiving allowance.
    /// @param value Allowance amount.
    /// @param deadline Signature expiry timestamp.
    /// @param v Signature v.
    /// @param r Signature r.
    /// @param s Signature s.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8   v,
        bytes32 r,
        bytes32 s
    ) external {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
        require(owner != address(0), "ERC20Permit: invalid owner");

        // Read current nonce. It is only consumed after the signature checks pass.
        uint256 nonce = nonces[owner];

        // Hash the typed Permit struct.
        bytes32 structHash = keccak256(
            abi.encode(
                _PERMIT_TYPEHASH,
                owner,
                spender,
                value,
                nonce,
                deadline
            )
        );

        // EIP-712 digest:
        // keccak256("\x19\x01" || DOMAIN_SEPARATOR() || structHash)
        bytes32 digest = keccak256(
            abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), structHash)
        );

        // Recover signer and verify.
        address signer = _recoverPermitSigner(digest, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        // Consume nonce only after successful verification.
        unchecked {
            nonces[owner] = nonce + 1;
        }

        // Set allowance exactly like approve().
        _approve(owner, spender, value);
    }

    /// @dev Minimal safe ECDSA recover with malleability checks.
    /// Rejects high-s signatures and invalid v values.
    function _recoverPermitSigner(
        bytes32 digest,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address signer) {
        require(
            uint256(s) <= _SECP256K1N_HALF,
            "ECDSA: invalid signature 's' value"
        );

        // Accept both 27/28 and 0/1 style v values.
        if (v < 27) v += 27;
        require(
            v == 27 || v == 28,
            "ECDSA: invalid signature 'v' value"
        );

        signer = ecrecover(digest, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");
    }

    /*/////////////////////////////////////////////////////////////////////////
                                ERC-20 VIEW FUNCTIONS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Return MOON balance of `owner`.
    /// @dev Canonical balance getter for general use.
    /// May revert when called in swap paths that attempt to avoid tax-aware balance checks.
    /// Optional raw balance fallback exists, but taxable pairs cannot use it.
    function balanceOf(address owner) public view override returns (uint) {
        require(IKernel(kernel).isUnlocked(msg.sender, owner), "MOON: Tax Evasion");
        return balances[owner];
    }

    /// @notice Return allowance from `owner` to `spender`.
    function allowance(address owner, address spender) public view override returns (uint) {
        return allowances[owner][spender];
    }

    /*/////////////////////////////////////////////////////////////////////////
                     ERC-20 — MUTATIVE (ALLOWANCE & TRANSFERS)
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Approve `spender` to transfer up to `amount` MOON on caller’s behalf.
    /// @dev Emits an {Approval} event.
    function approve(address spender, uint amount)
        external
        override
        returns (bool)
    {
        _approve(msg.sender, spender, amount);
        return true;
    }

    /// @notice Transfer `amount` MOON from msg.sender to `to`.
    /// @dev `_transfer` may apply kernel-planned tax.
    function transfer(address to, uint amount)
        external
        override
        nonReentrant
        returns (bool)
    {
        _transfer(msg.sender, to, amount);
        return true;
    }

    /// @notice Move `amount` MOON from `from` to `to` using caller’s allowance.
    /// @dev `_transfer` may apply kernel-planned tax.
    /// @dev If not at max value, deducts allowance and emits {Transfer} & {Approval} events.
    function transferFrom(address from, address to, uint amount)
        external
        override
        nonReentrant
        returns (bool)
    {
        uint currentAllowance = allowances[from][msg.sender];
        if (currentAllowance != type(uint).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                allowances[from][msg.sender] = currentAllowance - amount;
            }
            emit Approval(from, msg.sender, currentAllowance - amount);
        }
        _transfer(from, to, amount);
        return true;
    }

    /*/////////////////////////////////////////////////////////////////////////
                            ERC-20 — INTERNAL LOGIC
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Set `spender`'s allowance over `owner`'s tokens to `amount`.
    /// @dev Internal helper, emits an {Approval} event.
    function _approve(address owner, address spender, uint amount) internal {
        allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /// @dev Burns `amount` from `from`, reducing total supply.
    function _burn(address from, uint amount) internal {
        uint fromBal = balances[from];
        require(fromBal >= amount, "ERC20: burn exceeds balance");

        unchecked {
            balances[from] = fromBal - amount;
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }

    /// @notice Executes a MOON transfer with kernel-directed taxation.
    /// @dev
    /// - Informs the kernel through `onMoonTransfer(from, to, amount)`.
    /// - The kernel may:
    ///   - apply up to `MAX_TAX` total tax to this transfer, or
    ///   - add taxable credit to `from` and/or `to` for temporary retroactive taxation.
    /// - Otherwise hook failure is swallowed and fallback max-tax burn logic is used.
    /// - Then executes the transfer using the resulting tax plan.
    /// - Transfers to `DEAD` are treated as official burns and reduce total supply.
    /// @param from   Sender address.
    /// @param to     Recipient address (non-zero, not sun, not moon)
    /// @param amount MOON amount to move.
    function _transfer(address from, address to, uint amount) internal {
        require(to != address(0) && to != sun && to != address(this), "Invalid recipient");

        /// MUST REMOVE THIS LINE BEFORE PRODUCTION VERSION.
        require(block.timestamp < 1781899097, "Expired");

        // Inform kernel of transfer.
        uint taxAmt;
        (bool ok, int fTax, int tTax) = _onMoonTransfer(from, to, amount);

        uint fromBal = balances[from];
        require(fromBal >= amount, "ERC20: Balance insufficient");

        bool burnTax;
        if (ok) {
            taxAmt =
                _planTax(from, amount, fTax) +
                _planTax(to,  amount,  tTax);
        } else {
            // `_onMoonTransfer` failed so we burn tax.
            taxAmt = (amount * MAX_TAX) / 1e18;
            burnTax = true;
        }

        // Change balances.
        uint recvAmt;
        unchecked { recvAmt = amount - taxAmt; }

        if (to == DEAD) {
            _burn(from, burnTax ? amount : recvAmt);
        } else {
            unchecked {
                balances[from] = fromBal - (burnTax ? recvAmt : amount);
                balances[to] += recvAmt;
            }

            emit Transfer(from, to, recvAmt);
        }

        // Handle tax.
        if (taxAmt != 0) {
            if (burnTax) {
                if (to != DEAD) _burn(from, taxAmt);
            } else {
                if (to == DEAD) {
                    unchecked {
                        balances[from] -= taxAmt;
                    }
                }
                balances[address(this)] += taxAmt;
                emit Transfer(from, address(this), taxAmt);
            }
        }
    }

    /// @dev Plans one side of tax.
    /// - `t > 0`: returns tax to take, capped at MAX_TAX / 2 of `amount`.
    /// - `t < 0`: adds to taxable of `who`, capped at MAX_TAX / 2 of `amount`.
    /// - Never reverts on kernel-provided values.
    /// - If tax is positive, zeros taxable.
    /// - Stored taxable credit is capped per transfer; kernel spend is later capped by balance.
    function _planTax(address who, uint amount, int t)
        internal
        returns (uint addTax)
    {
        if (t == 0) return 0;
        uint max = (amount * (MAX_TAX / 2)) / 1e18;

        if (t > 0) {
            // Tax now.
            addTax = uint(t) > max ? max : uint(t);
            if (taxable[who] != 0) taxable[who] = 0; // clear taxable
        } else {
            // Allow tax later.
            uint addTxb = uint(-(t + 1)) + 1; // safe abs for int256 min
            taxable[who] += addTxb <= max ? addTxb : max;
        }
    }

    /// @notice Kernel-only untaxed transfer path.
    /// @dev
    /// - Uses `taxable[from]` as a saturating spend cap that gets zeroed every time it is spent.
    /// - `from == address(this)` skips the taxable cap.
    /// - Reverts if balance cannot cover the capped amount or invalid recipient.
    /// - Supports DEAD burns.
    function _kernelTransfer(address from, address to, uint amount) internal returns (uint moveAmt) {
        require(to != address(0) && to != sun, "Invalid recipient");

        moveAmt = amount;
        uint fromBal = balances[from];

        // Spend taxable
        if (from != address(this)) {
            uint txb = taxable[from];
            if (moveAmt > txb) moveAmt = txb;       // saturate to taxable
            uint balCap = fromBal * MAX_TAX / 1e18;
            if (moveAmt > balCap) moveAmt = balCap; // Saturate to MAX_TAX % of fromBal
            taxable[from] = 0;
        }

        require(fromBal >= moveAmt, "ERC20: Balance insufficient");

        // Move tokens
        if (to == DEAD) { _burn(from, moveAmt); return (moveAmt); }
        unchecked {
            balances[from] = fromBal - moveAmt;
            balances[to] += moveAmt;
        }
        emit Transfer(from, to, moveAmt);
    }

    /*/////////////////////////////////////////////////////////////////////////
                                  TRANSFER HOOK
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Notifies `kernel` of a MOON balance move using a capped raw call.
    /// @dev
    /// - Calls `IKernel(kernel).onMoonTransfer(from, to, amount)` with `HOOK_GAS_CAP`.
    /// - Reverts the outer transfer when the kernel call fails and the transaction
    ///   did not supply enough gas to satisfy the hook requirement.
    /// - If the transaction did provide enough gas for the full hook cap, kernel
    ///   failure is swallowed and reported through `ok = false`.
    /// - Successful calls decode `(fTax, tTax)` from 64-byte returndata, or only
    ///   `fTax` from 32-byte returndata with `tTax = 0`.
    /// - Uses a raw call with no output buffer so kernel returndata is never
    ///   copied unless its size is explicitly accepted.
    /// @return ok true if the kernel call succeeded.
    /// @return fTax decoded first tax value, or 0 if returndata is absent or malformed.
    /// @return tTax decoded second tax value, or 0 if missing or malformed.
    function _onMoonTransfer(
        address from,
        address to,
        uint256 amount
    ) internal returns (bool ok, int256 fTax, int256 tTax) {
        address target = kernel;

        bytes memory data = abi.encodeWithSelector(
            IKernel.onMoonTransfer.selector,
            from,
            to,
            amount
        );

        uint256 gasBefore;

        assembly ("memory-safe") {
            gasBefore := gas()
            ok := call(
                HOOK_GAS_CAP,
                target,
                0,
                add(data, 0x20),
                mload(data),
                0,
                0
            )

            // Never copy unbounded returndata.
            // Only accept canonical fixed-size returns:
            // - 0x40: two int256 words (fTax, tTax)
            // - 0x20: one int256 word (fTax), keep tTax = 0 (backwards compatible)
            let rds := returndatasize()

            switch and(ok, eq(rds, 0x40))
            case 1 {
                returndatacopy(0x00, 0x00, 0x40)
                fTax := mload(0x00)
                tTax := mload(0x20)
            }
            default {
                if and(ok, eq(rds, 0x20)) {
                    returndatacopy(0x00, 0x00, 0x20)
                    fTax := mload(0x00)
                    // tTax remains 0
                }
            }
        }

        // Only require gas cap if call fails.
        if (!ok && gasBefore < HOOK_GAS_CAP) {
            assembly ("memory-safe") {
                revert(0, 0)
            }
        }
    }

    /*/////////////////////////////////////////////////////////////////////////
                                 FALLBACK ROUTER
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Optional raw balance path open to anyone.
    /// This is not a bypass: taxable pairs should automatically use `balanceOf`,
    /// all other callers can use either without affecting tax detection.
    bytes4 private constant RAW_BALANCE_SELECTOR =
        bytes4(keccak256("rawBalanceOf(address)"));

    /// @dev Optional raw taxable path open to anyone.
    bytes4 private constant RAW_TAXABLE_SELECTOR =
        bytes4(keccak256("rawTaxableOf(address)"));

    /// @dev Selector for kernel untaxed transfers through the internal communication channel.
    bytes4 private constant KERNEL_TRANSFER_SELECTOR =
        bytes4(keccak256("kernelTransfer(address,address,uint256)"));

    /// @notice Internal communication channel for raw balance reads and kernel transfers.
    /// @dev
    /// MOON intentionally exposes only the standard ERC-20 surface.
    /// These protocol routes stay in the fallback for a cleaner public ABI.
    /// This is a UI/UX choice, not an access-control mechanism:
    /// all logic is visible and strictly gated by `msg.sender`.
    fallback() external {
        bytes4 sel;
        assembly ("memory-safe") {
            sel := calldataload(0)
        }


        // rawBalanceOf(address)
        if (sel == RAW_BALANCE_SELECTOR) {
            address owner;
            assembly ("memory-safe") {
                owner := and(calldataload(4), 0xffffffffffffffffffffffffffffffffffffffff)
            }

            uint bal = balances[owner];

            assembly ("memory-safe") {
                mstore(0x00, bal)
                return(0x00, 0x20)
            }
        }

        // rawTaxableOf(address)
        if (sel == RAW_TAXABLE_SELECTOR) {
            address owner;
            assembly ("memory-safe") {
                owner := and(calldataload(4), 0xffffffffffffffffffffffffffffffffffffffff)
            }

            uint txb = taxable[owner];

            assembly ("memory-safe") {
                mstore(0x00, txb)
                return(0x00, 0x20)
            }
        }

        // kernelTransfer(address,address,uint256)
        if (msg.data.length == 100 && sel == KERNEL_TRANSFER_SELECTOR) {
            if (msg.sender != kernel) revert InternalOnly();

            address from;
            address to;
            uint amount;

            assembly ("memory-safe") {
                from := and(calldataload(4),  0xffffffffffffffffffffffffffffffffffffffff)
                to   := and(calldataload(36), 0xffffffffffffffffffffffffffffffffffffffff)
                amount := calldataload(68)
            }

            uint moved = _kernelTransfer(from, to, amount);

            assembly ("memory-safe") {
                mstore(0x00, moved)
                return(0x00, 0x20)
            }
        }

        revert("MOON: invalid fallback");
    }
}
        

/Vault.sol

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

/// @dev Minimal ERC-20 transfer used by this contract.
interface IERC20 {
    function transfer(address recipient, uint256 amount) external returns (bool);
}

/// @dev Minimal interface for the Vault contract.
/// @notice Allows the Moon contract to instruct the Vault to release
///         a specified `amount` of `ray` to a given `member`.
interface IVault {
    function releaseToMember(address ray, uint256 amount, address member) external;
}

/*──────────────────────────────  VAULT  ─────────────────────────────────*/

/// @notice
/// A minimal vault that passively stores rays and releases them
/// to SUN members when instructed by the Moon contract.
contract Vault {
    /*//////////////////////////////////////////////////////////////////////
                                      STATE
    //////////////////////////////////////////////////////////////////////*/

    /// Moon contract that is authorized to pull funds out of the vault.
    address public immutable moon;

    /*//////////////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
    //////////////////////////////////////////////////////////////////////*/

    /// @param _moon The address of the Moon ERC‑20 contract.
    constructor(address _moon) {
        require(_moon != address(0), "Vault: zero Moon addr");
        moon = _moon;
    }

    /*//////////////////////////////////////////////////////////////////////
                             CONTROLLED PAY‑OUT HOOK
    //////////////////////////////////////////////////////////////////////*/
    
    /// @notice Sends `amount` of `ray` to `member`.
    /// @dev Reverts if token reverts, returns false, or returns malformed data.
    ///      Zero-length return data is treated as success.
    function releaseToMember(
        address ray,
        uint256 amount,
        address member
    ) external {
        require(msg.sender == moon, "Vault: not MOON");
        require(member != address(0), "Vault: zero member");

        (bool ok, bytes memory data) = ray.call(
            abi.encodeWithSelector(IERC20.transfer.selector, member, amount)
        );

        // Token reverted → bubble as failure
        require(ok, "Vault: token transfer reverted");

        // No return data → accept as success (supports non-standard tokens)
        if (data.length == 0) return;

        // Must be exactly 32 bytes that decode to true
        require(data.length == 32 && abi.decode(data, (bool)), "Vault: token transfer failed");
    }

}
          

/TestEngines.sol

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

/// @notice Minimal no-op engine used for SUN/Kernel governance upgrade tests.
/// @dev Kernel delegatecalls into this contract, so keep it storage-free.
contract TestEngineV1 {
    function testEngineV1Method() external pure returns (uint256) {
        return 1;
    }

    function initialize(address) external pure {}

    function newEngineSet() external pure {}

    function onSunTransfer(
        address,
        address,
        uint256,
        uint256,
        uint256
    ) external pure {}

    function _onSunTransfer(
        address,
        address,
        uint256,
        uint256,
        uint256
    ) external pure {}

    function collect(address) external pure {}
}

/// @notice Second no-op engine implementation for SUN governance upgrade tests.
/// @dev Same ABI as V1 with a different version marker.
contract TestEngineV2 {
    function testEngineV2Method() external pure returns (uint256) {
        return 1;
    }

    function initialize(address) external pure {}

    function newEngineSet() external pure {}

    function onSunTransfer(
        address,
        address,
        uint256,
        uint256,
        uint256
    ) external pure {}

    function _onSunTransfer(
        address,
        address,
        uint256,
        uint256,
        uint256
    ) external pure {}

    function collect(address) external pure {}
}
          

/ForkFairLaunchHarness.sol

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

interface IForkERC20 {
    function approve(address spender, uint256 value) external returns (bool);
    function balanceOf(address owner) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
}

interface IForkWrappedNative {
    function deposit() external payable;
}

interface IForkV2Router {
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
}

interface IForkV3Pool {
    function token0() external view returns (address);
    function token1() external view returns (address);

    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes calldata data
    ) external returns (int256 amount0, int256 amount1);
}

contract ForkFairLaunchHarness {
    uint160 private constant MIN_SQRT_RATIO = 4295128739;
    uint160 private constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    error PairingTokenNotInPool();
    error UnauthorizedCallback();
    error TransferFailed();

    receive() external payable {}

    function wrap(address wrappedNative) external payable {
        IForkWrappedNative(wrappedNative).deposit{ value: msg.value }();
    }

    function buyFromV3(address pool, address pairingToken, uint256 amountInMax)
        external
        returns (int256 amount0, int256 amount1)
    {
        address token0 = IForkV3Pool(pool).token0();
        address token1 = IForkV3Pool(pool).token1();

        bool zeroForOne;
        if (pairingToken == token0) {
            zeroForOne = true;
        } else if (pairingToken == token1) {
            zeroForOne = false;
        } else {
            revert PairingTokenNotInPool();
        }

        uint160 limit = zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1;
        return IForkV3Pool(pool).swap(
            address(this),
            zeroForOne,
            int256(amountInMax),
            limit,
            abi.encode(pool, token0, token1)
        );
    }

    function seedWrongV2(
        address router,
        address token,
        address pairingToken,
        uint256 tokenAmount,
        uint256 pairingAmount
    ) external returns (uint256 usedToken, uint256 usedPairing, uint256 liquidity) {
        _approveExact(token, router, tokenAmount);
        _approveExact(pairingToken, router, pairingAmount);

        return IForkV2Router(router).addLiquidity(
            token,
            pairingToken,
            tokenAmount,
            pairingAmount,
            0,
            0,
            address(this),
            block.timestamp
        );
    }

    function tokenBalance(address token) external view returns (uint256) {
        return IForkERC20(token).balanceOf(address(this));
    }

    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
        _swapCallback(amount0Delta, amount1Delta, data);
    }

    function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
        _swapCallback(amount0Delta, amount1Delta, data);
    }

    function _swapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) private {
        (address pool, address token0, address token1) = abi.decode(data, (address, address, address));
        if (msg.sender != pool) revert UnauthorizedCallback();

        if (amount0Delta > 0) _safeTransfer(token0, msg.sender, uint256(amount0Delta));
        if (amount1Delta > 0) _safeTransfer(token1, msg.sender, uint256(amount1Delta));
    }

    function _approveExact(address token, address spender, uint256 value) private {
        (bool ok, bytes memory data) = token.call(abi.encodeWithSelector(IForkERC20.approve.selector, spender, 0));
        if (!ok || (data.length != 0 && !abi.decode(data, (bool)))) revert TransferFailed();

        (ok, data) = token.call(abi.encodeWithSelector(IForkERC20.approve.selector, spender, value));
        if (!ok || (data.length != 0 && !abi.decode(data, (bool)))) revert TransferFailed();
    }

    function _safeTransfer(address token, address to, uint256 value) private {
        (bool ok, bytes memory data) = token.call(abi.encodeWithSelector(IForkERC20.transfer.selector, to, value));
        if (!ok || (data.length != 0 && !abi.decode(data, (bool)))) revert TransferFailed();
    }
}
          

/CodexSunBatchActorTmp.sol

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

interface ICodexSunTmp {
    function transfer(address to, uint256 amount) external returns (bool);
    function vote(uint256 proposal, bool support) external;
    function delegate(address newDelegate) external;
    function createProposal(address engine, uint256 actionCode) external returns (uint256);
    function finalizeProposal(uint256 proposal) external;
}

contract CodexSunBatchActorTmp {
    ICodexSunTmp public immutable sun;

    constructor(address sun_) {
        sun = ICodexSunTmp(sun_);
    }

    function vote(uint256 proposal, bool support) external {
        sun.vote(proposal, support);
    }

    function delegate(address newDelegate) external {
        sun.delegate(newDelegate);
    }

    function transferSun(address to, uint256 amount) external {
        sun.transfer(to, amount);
    }

    function createOnly(address engine, uint256 actionCode) external {
        sun.createProposal(engine, actionCode);
    }

    function finalizeOnly(uint256 proposal) external {
        sun.finalizeProposal(proposal);
    }

    function createThenFinalize(address engine, uint256 actionCode, uint256 finalizeId) external {
        sun.createProposal(engine, actionCode);
        sun.finalizeProposal(finalizeId);
    }

    function voteTwice(uint256 proposal, bool firstSupport, bool secondSupport) external {
        sun.vote(proposal, firstSupport);
        sun.vote(proposal, secondSupport);
    }

    function finalizeThenCreate(uint256 finalizeId, address engine, uint256 actionCode) external {
        sun.finalizeProposal(finalizeId);
        sun.createProposal(engine, actionCode);
    }

    function outAndBack(address helper, uint256 amount) external {
        sun.transfer(helper, amount);
        CodexSunBatchActorTmp(helper).transferSun(address(this), amount);
    }
}
          

/CodexGasProbeSinkTmp.sol

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

interface ICodexERC20Tmp {
    function transfer(address to, uint256 amount) external returns (bool);
}

contract CodexGasProbeSinkTmp {
    function move(address token, address to, uint256 amount) external {
        ICodexERC20Tmp(token).transfer(to, amount);
    }

    fallback() external payable {
        uint256 x;
        while (gasleft() > 250) {
            unchecked {
                x++;
            }
        }
        assembly ("memory-safe") {
            return(0, 0)
        }
    }
}
          

/Sun.sol

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

/*
 ┌────────────────────────────────────────────────────────────────────────┐
 │                                                                        │
 │                       ███████ ██    ██ ███    ██                       │
 │                       ██      ██    ██ ████   ██                       │
 │                       ███████ ██    ██ ██ ██  ██                       │
 │                            ██ ██    ██ ██  ██ ██                       │
 │                       ███████  ██████  ██   ████                       │
 │                                                                        │
 │      SUN is a fixed-supply ERC-20 that receives rewards from MOON.     │
 │                                                                        │
 │  SUN behavior                                                          │
 │   • Standard ERC-20: fixed supply, no fees, no taxes, no admin keys.   │
 │   • MOON performs all reflection logic and distribution.               │
 │   • SUN releases tokens to holders when called by the reward engine.   │
 │   • Rewards are distributed automatically; collect() is optional.      │
 │   • SUN holders can vote on changes to the reward engine.              │
 │                                                                        │
 └────────────────────────────────────────────────────────────────────────┘
*/

/*────────────────────────────  INTERFACES  ─────────────────────────────*/

/// @notice Minimal ERC‑20 interface used by Sun.
interface IERC20 {
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// Use to communicate with reward engine about balance changes, collect() and engine changes.
interface Ikernel {
    function onSunTransfer(address from, address to, uint256 fromBal, uint256 toBal, uint256 amount) external;
    function collect(address sender) external;
    function setEngine(address newEngine) external;
}

/*─────────────────────────────  CONTRACT  ──────────────────────────────*/

contract Sun is IERC20 {
    /*/////////////////////////////////////////////////////////////////////////
                                    METADATA
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice ERC‑20 name.
    string public constant name      = "SUN";
    /// @notice ERC‑20 symbol.
    string public constant symbol    = "SUN";
    /// @notice ERC‑20 decimals (fixed at 18).
    uint8 public constant decimals   = 18;

    /// @notice Fixed total supply (18 decimals).
    uint public constant totalSupply = 1_000_000_000e18;

    /*/////////////////////////////////////////////////////////////////////////
                                       ERRORS
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Reentrancy guard violation.
    error Reentrancy();

    /// @dev Function restricted to callable only by kernel or MOON.
    error InternalOnly();

    /*/////////////////////////////////////////////////////////////////////////
                                    IMMUTABLES
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Protocol coordinator that connects SUN and MOON to the engine.
    address private immutable kernel;

    /// @notice Peer token contract.
    address private immutable moon;

    /*/////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Max gas that the transfer hook can use. (callee can expect 95% of this).
    uint private constant HOOK_GAS_CAP = 600_000;

    /*/////////////////////////////////////////////////////////////////////////
                               ERC-20 CORE STORAGE
    /////////////////////////////////////////////////////////////////////////*/

    struct Account {
        // slot 0
        uint112 balance;           // sun balance.
        uint112 proposal;          // proposal ID voted on.
        bool    support;           // vote direction (true = for, false = against).
        bool    delegated;         // whether votes are delegated away.
        bool    hasDelegatedVotes; // whether votes are received from others.

        // slot 1
        address delegatee;         // delegatee address.

        // slot 2
        uint112 delegatedVotes;    // voting power received via delegation.

        // transient helpers (NEVER written to storage)
        address addr;              // account address (identity)
    }
    mapping(address => Account) internal accounts;

    /// @dev Allowance mapping per ERC‑20 (`owner => (spender => amount)`).
    mapping(address => mapping(address => uint256)) internal allowances;

    /*/////////////////////////////////////////////////////////////////////////
                            SHARED REENTRANCY GUARD
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Shared protocol lock state.
    /// 1 = unlocked, 2 = locked. Lives only in SUN.
    uint private constant _UNLOCKED = 1;
    uint private constant _LOCKED   = 2;

    /// @dev Storage slot for the shared protocol lock.
    uint private _reentrancyStatus = _UNLOCKED;

    /// @notice Reentrancy guard for SUN and MOON entry points.
    /// @dev
    /// - SUN owns the single shared lock used by both SUN and MOON.
    /// - Local SUN entry points flip this slot directly.
    /// - MOON acquires and releases the same slot through the fallback route below.
    modifier nonReentrant() {
        if (_reentrancyStatus != _UNLOCKED) revert Reentrancy();

        _reentrancyStatus = _LOCKED;
        _;
        _reentrancyStatus = _UNLOCKED;
    }

    /*/////////////////////////////////////////////////////////////////////////
                                  CONSTRUCTOR
    /////////////////////////////////////////////////////////////////////////*/

    /// @param kernelAddress kernel contract
    /// @param moonAddress moon contract
    constructor(address kernelAddress, address moonAddress) {
        require(kernelAddress != address(0) && moonAddress != address(0), "Sun: zero addr");
        kernel = kernelAddress;
        moon = moonAddress;
        accounts[msg.sender].balance = uint112(totalSupply);
        emit Transfer(address(0), msg.sender, totalSupply);

        // Sentinel proposal 0 (never active, never used)
        proposals.push();
        Proposal storage p = proposals[0];
        p.createdAt  = block.timestamp;
        p.finalized  = true;
    }

    /*/////////////////////////////////////////////////////////////////////////
                                  EIP-2612 PERMIT
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice EIP-2612 permit nonces.
    mapping(address => uint256) public nonces;

    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
    bytes32 private constant _PERMIT_TYPEHASH =
        keccak256(
            "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
        );

    // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
    bytes32 private constant _EIP712_DOMAIN_TYPEHASH =
        keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );

    // secp256k1n / 2
    uint256 private constant _SECP256K1N_HALF =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;

    /// @notice Returns the EIP-712 domain separator used for permit signatures.
    /// @dev Uses:
    /// - token name from `name`
    /// - version = "1"
    /// - current chain ID
    /// - this contract address
    function DOMAIN_SEPARATOR() public view returns (bytes32) {
        return keccak256(
            abi.encode(
                _EIP712_DOMAIN_TYPEHASH,
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );
    }

    /// @notice Approve by signature, per EIP-2612.
    /// @param owner Token owner signing the permit.
    /// @param spender Address receiving allowance.
    /// @param value Allowance amount.
    /// @param deadline Signature expiry timestamp.
    /// @param v Signature v.
    /// @param r Signature r.
    /// @param s Signature s.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8   v,
        bytes32 r,
        bytes32 s
    ) external {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
        require(owner != address(0), "ERC20Permit: invalid owner");

        // Read current nonce. It is only consumed after the signature checks pass.
        uint256 nonce = nonces[owner];

        // Hash the typed Permit struct.
        bytes32 structHash = keccak256(
            abi.encode(
                _PERMIT_TYPEHASH,
                owner,
                spender,
                value,
                nonce,
                deadline
            )
        );

        // EIP-712 digest:
        // keccak256("\x19\x01" || DOMAIN_SEPARATOR() || structHash)
        bytes32 digest = keccak256(
            abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), structHash)
        );

        // Recover signer and verify.
        address signer = _recoverPermitSigner(digest, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        // Consume nonce only after successful verification.
        unchecked {
            nonces[owner] = nonce + 1;
        }

        // Set allowance exactly like approve().
        _approve(owner, spender, value);
    }

    /// @dev Minimal safe ECDSA recover with malleability checks.
    /// Rejects high-s signatures and invalid v values.
    function _recoverPermitSigner(
        bytes32 digest,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address signer) {
        require(
            uint256(s) <= _SECP256K1N_HALF,
            "ECDSA: invalid signature 's' value"
        );

        // Accept both 27/28 and 0/1 style v values.
        if (v < 27) v += 27;
        require(
            v == 27 || v == 28,
            "ECDSA: invalid signature 'v' value"
        );

        signer = ecrecover(digest, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");
    }

    /*/////////////////////////////////////////////////////////////////////////
                               OPTIONAL COLLECT HOOK
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Requests reward collection for the caller.
    /// @dev
    /// - Anyone holding SUN may call this at any time.
    /// - This is purely optional: emissions accrue automatically.
    /// - The call is forwarded through the kernel to the active engine with `msg.sender` as the beneficiary.
    /// - No SUN state is modified by this function.
    function collect() external nonReentrant {
        Ikernel(kernel).collect(msg.sender);
    }

    /*/////////////////////////////////////////////////////////////////////////
                               ERC-20 VIEW FUNCTIONS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Returns the current balance of an address.
    function balanceOf(address owner) public view override returns (uint256) {
        return accounts[owner].balance;
    }

    /// @notice Returns the remaining allowance from `owner` to `spender`.
    function allowance(address owner, address spender)
        public
        view
        override
        returns (uint256)
    {
        return allowances[owner][spender];
    }

    /*/////////////////////////////////////////////////////////////////////////
                        ERC-20 EXTERNAL MUTATIVE FUNCTIONS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Sets `spender`’s allowance over the caller’s tokens to `amount`.
    /// Emits an {Approval} event.
    function approve(address spender, uint256 amount)
        external
        override
        returns (bool)
    {
        _approve(msg.sender, spender, amount);
        return true;
    }

    /// @notice Moves `amount` tokens from caller to `to`.
    /// Emits a {Transfer} event.
    function transfer(address to, uint256 amount)
        external
        override
        nonReentrant
        returns (bool)
    {
        _transfer(msg.sender, to, amount);
        return true;
    }

    /// @notice Moves `amount` tokens from `from` to `to` using the caller’s allowance.
    /// Deducts allowance unless it is `type(uint256).max` (infinite allowance UX).
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 amount)
        external
        override
        nonReentrant
        returns (bool)
    {
        uint256 cur = allowances[from][msg.sender];
        if (cur != type(uint256).max) {
            require(cur >= amount, "ERC20: insufficient allowance");
            unchecked {
                allowances[from][msg.sender] = cur - amount;
            }
            emit Approval(from, msg.sender, cur - amount);
        }

        _transfer(from, to, amount);
        return true;
    }

    /*/////////////////////////////////////////////////////////////////////////
                             ERC-20 — INTERNAL LOGIC
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Internal setter for allowances; emits {Approval}.
    function _approve(address owner, address spender, uint256 amount) internal {
        allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /// @notice Executes SUN's transfer path with governance and reward accounting hooks.
    /// @dev
    /// - Reconciles voting weight before balances move.
    /// - Notifies `kernel` with pre-transfer balances so the reward engine can track accruals.
    /// - Self-transfers emit {Transfer} only and skip both hooks.
    /// @param from   Sender address.
    /// @param to     Recipient address.
    /// @param amount SUN amount to move.
    function _transfer(address from, address to, uint256 amount) internal {
        Account memory f = _loadAccount(from);
        Account memory t = _loadAccount(to);


        /// MUST REMOVE THIS LINE BEFORE PRODUCTION VERSION.
        require(block.timestamp < 1781899097, "Expired");

        require(to != address(0), "ERC20: transfer to the zero address");
        require(f.balance >= amount, "ERC20: transfer amount exceeds balance");

        emit Transfer(from, to, amount);
        if (from == to) return; // self transfers stop here.

        _updateGovOnTransfer(f, t, amount); // Governance hook

        uint fromBefore = f.balance;
        uint toBefore   = t.balance;

        // Move balances.
        unchecked {
            accounts[from].balance = uint112(f.balance - amount);
            accounts[to].balance   = uint112(t.balance + amount);
        }

        _onSunTransfer(from, to, fromBefore, toBefore, amount); // Kernel hook
    }

    /*/////////////////////////////////////////////////////////////////////////
                                  TRANSFER HOOK
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Notifies `kernel` of a SUN balance move using a capped raw call.
    /// @dev
    /// - Calls `Ikernel(kernel).onSunTransfer(from, to, fromBefore, toBefore, amount)` with `HOOK_GAS_CAP`.
    /// - Reverts the outer transfer when the kernel call fails and the transaction
    ///   did not supply enough gas to satisfy the hook requirement.
    /// - If the transaction did provide enough gas for the full hook cap, kernel failure is swallowed.
    /// - Uses a raw call with no returndata to prevent returndata-griefing.
    function _onSunTransfer(
        address from,
        address to,
        uint256 fromBefore,
        uint256 toBefore,
        uint256 amount
    ) internal {
        address target = kernel;

        bytes memory data = abi.encodeWithSelector(
            Ikernel.onSunTransfer.selector,
            from,
            to,
            fromBefore,
            toBefore,
            amount
        );

        bool ok;
        uint256 gasBefore;

        assembly ("memory-safe") {
            gasBefore := gas()
            ok := call(
                HOOK_GAS_CAP,
                target,
                0,
                add(data, 0x20),
                mload(data),
                0,
                0
            )
        }

        // Only require gas cap if call fails.
        if (!ok && gasBefore < HOOK_GAS_CAP) {
            assembly ("memory-safe") {
                revert(0, 0)
            }
        }
    }

    /*/////////////////////////////////////////////////////////////////////////
                             KERNEL CONTROLLED PAYOUT
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Attempts to send `amount` of `ray` to `member`.
    /// @dev
    /// - Callable only by kernel.
    /// - Performs a raw ERC-20 `transfer` call and accepts no returndata or `true`.
    /// - When `requireIncrease` is true, succeeds only if the recipient balance increases immediately.
    /// - Tolerates fee-on-transfer or other tokens where the recipient receives less than `amount`.
    /// @param requireIncrease true if call should only succeed when members balance increase.
    function _releaseToMember(
        address ray,
        uint256 amount,
        address member,
        bool    requireIncrease
    ) internal {
        require(msg.sender == kernel, "SUN: not kernel");
        require(member != address(0), "SUN: zero member");
        require(ray != address(this), "SUN: cannot release SUN");

        uint256 beforeBal = requireIncrease ? IERC20(ray).balanceOf(member) : 0;

        (bool ok, bytes memory ret) = ray.call(
            abi.encodeWithSelector(IERC20.transfer.selector, member, amount)
        );
        require(ok, "SUN: token transfer reverted");

        if (ret.length > 0) {
            require(abi.decode(ret, (bool)), "SUN: token transfer returned false");
        }

        if (requireIncrease) {
            uint256 afterBal = IERC20(ray).balanceOf(member);
            require(afterBal > beforeBal, "SUN: no balance increase");
        }
    }

    /// @notice Sets SUN's allowance on `token` for `target`.
    /// @dev
    /// - Callable only by kernel.
    /// - Reverts if `token` is SUN itself.
    /// - Performs a raw ERC-20 `approve` call.
    /// - No allowance reset, target restriction, or code-length check is applied.
    /// NOTE: this is an optional future payout route. Engine v1 does not use this path.
    function _setTokenAllowance(
        address token,
        uint256 amount,
        address target
    ) internal {
        require(msg.sender == kernel, "SUN: not kernel");
        require(token != address(this), "SUN: cannot approve SUN");

        (bool ok, bytes memory ret) = token.call(
            abi.encodeWithSelector(IERC20.approve.selector, target, amount)
        );
        require(ok, "SUN: token approve reverted");

        if (ret.length > 0) {
            require(abi.decode(ret, (bool)), "SUN: token approve returned false");
        }
    }

    /*/////////////////////////////////////////////////////////////////////////
                                 FALLBACK ROUTER
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Selector for MOON-controlled shared lock sync through the
    /// internal communication channel.
    bytes4 private constant SEL_SHARED_REENTRANCY =
        bytes4(keccak256("sharedReentrancy(bool)"));

    /// @dev Conceptual ABI selector for engine-controlled payouts routed through the kernel.
    /// This function is intentionally not part of Sun's public ABI and is
    /// dispatched manually through the fallback.
    bytes4 private constant SEL_RELEASE_TO_MEMBER =
        bytes4(keccak256("releaseToMember(address,uint256,address,bool)"));

    /// @dev Conceptual ABI selector for kernel-controlled token approvals.
    /// This function is intentionally not part of Sun's public ABI and is
    /// dispatched manually through the fallback.
    bytes4 private constant SEL_SET_TOKEN_ALLOWANCE =
        bytes4(keccak256("setTokenAllowance(address,uint256,address)"));

    /// @notice Internal protocol router for shared guard sync, kernel-only payouts, and approvals.
    /// @dev
    /// SUN intentionally exposes only the standard ERC-20 surface.
    /// These protocol routes stay in the fallback for a cleaner public ABI.
    /// This is a UI/UX choice, not an access-control mechanism:
    /// all logic is visible and strictly gated by `msg.sender`.
    fallback() external {
        bytes4 sel;
        assembly ("memory-safe") {
            sel := calldataload(0)
        }

        // sharedReentrancy(bool)
        if (sel == SEL_SHARED_REENTRANCY) {
            if (msg.sender != moon) revert InternalOnly();

            bool lock;
            assembly ("memory-safe") {
                lock := iszero(iszero(calldataload(4)))
            }

            if (lock) {
                if (_reentrancyStatus != _UNLOCKED) revert Reentrancy();
                _reentrancyStatus = _LOCKED;
            } else {
                // if MOON reached here, we assume MOON owned the active frame.
                _reentrancyStatus = _UNLOCKED;
            }

            return;
        }

        // releaseToMember(address,uint256,address,bool)
        if (sel == SEL_RELEASE_TO_MEMBER) {
            if (msg.sender != kernel) revert InternalOnly();

            address ray;
            uint256 amount;
            address member;
            bool requireIncrease;

            assembly ("memory-safe") {
                ray             := and(calldataload(4),  0xffffffffffffffffffffffffffffffffffffffff)
                amount          := calldataload(36)
                member          := and(calldataload(68), 0xffffffffffffffffffffffffffffffffffffffff)
                requireIncrease := iszero(iszero(calldataload(100)))
            }

            _releaseToMember(ray, amount, member, requireIncrease);
            return;
        }

        // setTokenAllowance(address,uint256,address)
        if (sel == SEL_SET_TOKEN_ALLOWANCE) {
            if (msg.sender != kernel) revert InternalOnly();

            address token;
            uint256 amount;
            address target;

            assembly ("memory-safe") {
                token  := and(calldataload(4),  0xffffffffffffffffffffffffffffffffffffffff)
                amount := calldataload(36)
                target := and(calldataload(68), 0xffffffffffffffffffffffffffffffffffffffff)
            }

            _setTokenAllowance(token, amount, target);
            return;
        }

        revert();
    }

    /*/////////////////////////////////////////////////////////////////////////
                                  STORAGE HELPER
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev
    /// - Loads an account into memory field by field.
    /// - Always loads slot 0 flags to determine if slot 1 and 2 need loading.
    function _loadAccount(address account) internal view returns (Account memory a) {
        // Slot 0.
        a.addr              = account;
        a.balance           = accounts[account].balance;
        a.proposal          = accounts[account].proposal;
        a.support           = accounts[account].support;
        a.delegated         = accounts[account].delegated;
        a.hasDelegatedVotes = accounts[account].hasDelegatedVotes;

        // Slot 1.
        if (a.delegated) a.delegatee = accounts[account].delegatee;

        // Slot 2.
        if (a.hasDelegatedVotes) a.delegatedVotes = accounts[account].delegatedVotes;
    }

    /*/////////////////////////////////////////////////////////////////////////
                                 ENGINE GOVERNANCE
    /////////////////////////////////////////////////////////////////////////*/

    /*
    SUN includes a simple on-chain governance system for changing the engine.

    What is a proposal?
    • A proposal can change the reward engine or disable governance.
    • If the engine is set to a non zero address, that becomes the new reward engine.
    • If the engine is set to the DEAD address, governance is permanently disabled.
    • If the engine is set to address(0), proposal performs no action on finalization.

    Creating a Proposal
    • Creating a proposal requires staking 250,000 SUN.
    • The target engine must contain code, be the DEAD address, or be address(0) for no action.
    • Voting begins immediately and lasts up to 14 days.

    Vote or Delegate
    • Each account can take exactly one governance position at a time: either vote or delegate.
    • Voting
        – You may support a single proposal, either for or against.
        – Your voting power is your current SUN balance plus any votes delegated to you.
    • Delegation
        – You may delegate your voting power to another account.
        – Your balance will follow your delegatee’s support automatically.
        – Delegation does not chain. If your delegatee delegates, your votes become inactive.
    • Changes to balance, delegation, or support automatically update voting weight.
    • For stability, vote tallies update with a short delay, so votes made
      within ~20 minutes of expiration may not be reflected in time.

    Lifecycle and Expiration
    • A proposal runs for up to 14 days.
    • 100,000,000 SUN support must be reached by day 7 and maintained thereafter,
      or the proposal expires early.
    • On expiration, tallies are locked immediately.
    • After expiration, a 2 day timelock must pass before finalization.

    Finalization and Outcomes
    • Anyone can finalize a proposal after the timelock.
    • A proposal passes only if support reaches both ≥60% of tallied votes and ≥100,000,000 SUN.
    • If governance is disabled, proposals cannot pass.
    • If support is below 40% or 50,000,000 SUN, the stake is burned.
      Otherwise, it is returned to the proposer.
    • On success the proposal’s engine becomes the new reward engine, or governance
      is immediately ended if proposal is for DEAD.
    */

    /*/////////////////////////////////////////////////////////////////////////
                              GOVERNANCE STORAGE
    /////////////////////////////////////////////////////////////////////////*/

    ///@notice New reward engine proposal.
    struct Proposal {
        address proposer;
        address engine;
        uint    actionCode;
        uint    createdAt;
        uint    expiration;
        uint    votesPro;
        uint    votesCon;
        bool    finalized;

        // transient helpers (NEVER written to storage)
        uint castPro;
        uint castCon;
        uint talliedPro;
        uint talliedCon;
    }
    Proposal[] private proposals;

    /// @dev Proposal creation is disabled.
    bool public govDisabled;

    /*/////////////////////////////////////////////////////////////////////////
                               GOVERNANCE CONSTANTS
    /////////////////////////////////////////////////////////////////////////*/

    // SUN tokens that must be staked to create proposal.
    uint256 public constant STAKE         = 250_000e18; // 250k

    // Minimum support votes required for success (whole tokens, unscaled).
    uint256 public constant MIN_PRO       = 100_000_000; // 100m

    // Success threshold. (bps)
    uint256 public constant SUCCESS_BPS   = 6000; // 60%

    // Penalty threshold. (bps)
    uint256 public constant PENALTY_BPS   = 4000; // 40%

    // Voting period duration.
    uint256 public constant VOTING_PERIOD = 14 days;

    // Time lock after expiration before finalization.
    uint256 public constant TIME_LOCK     = 2 days;

    // Vote tally delay window; changes reflect after 1–2 windows.
    uint256 public constant TALLY_WINDOW  = 10 minutes;

    /// @notice Conventional burn sink address (irretrievable).
    address private constant DEAD =
        0x000000000000000000000000000000000000dEaD;

    /*/////////////////////////////////////////////////////////////////////////
                                GOVERNANCE EVENTS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Emitted when a new governance proposal is created.
    /// @dev
    /// - `id` is the proposal index in storage.
    /// - `proposer` is the account that staked and created the proposal.
    /// - `engine` is the proposed new reward engine (or DEAD to disable governance).
    /// - `expiration` is the timestamp when voting ends (subject to early expiry).
    event ProposalCreated(
        uint indexed id,
        address indexed proposer,
        address indexed engine,
        uint expiration
    );

    /// @notice Emitted when a proposal is finalized.
    /// @dev
    /// - `success` indicates whether the proposal met all passing conditions.
    /// - Emitted after stake handling and any engine update or governance disable.
    event ProposalOutcome(
        uint indexed id,
        bool success
    );

    /// @notice Emitted when a proposal’s vote state is updated.
    /// @dev
    /// - Fired on any vote, delegation, or balance change affecting this proposal.
    /// - `castPro` / `castCon` are the raw vote totals (pre-delay).
    /// - `talliedPro` / `talliedCon` are the delayed effective totals used for decisions.
    /// - Off-chain indexers should rely on `tallied*` for current outcome state.
    event proposalUpdated(
        uint indexed id,
        uint castPro,
        uint castCon,
        uint talliedPro,
        uint talliedCon
    );

    /// @notice Emitted when an account’s governance position changes.
    /// @dev
    /// - Covers both direct voting and delegation updates.
    /// - `proposal` is the proposal the account supports (0 if none).
    /// - `support` is true for “for”, false for “against” (ignored if delegating).
    /// - `delegate` is the new delegatee (zero address if voting directly or clearing).
    /// - Emitted on vote(), delegate(), and implicit clears (e.g. expiration).
    event PositionUpdated(
        address indexed account,
        uint256 indexed proposal,
        bool support,
        address indexed delegate
    );

    /*/////////////////////////////////////////////////////////////////////////
                           EXTERNAL GOVERNANCE FUNCTIONS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Casts or updates the caller’s vote on a proposal.
    /// @dev
    /// - Clears delegated votes.
    /// - Caller's SUN balance + votes delegated to caller are applied to `proposal`.
    /// - If already voting on another proposal, support is changed to this one.
    /// - Reverts if the target proposal is expired or invalid.
    /// WARNING:
    /// - In general, any malicious or faulty engine can be replaced through governance.
    /// - The exception is a proposal that sets the engine to the DEAD address,
    ///   which permanently disables governance and prevents further upgrades.
    /// - Additionally, on chains that still allow full SELFDESTRUCT behavior,
    ///   a malicious engine could destroy the kernel contract, permanently
    ///   breaking the system and preventing recovery.
    /// @param proposal proposal account is voting for.
    /// @param support  Vote direction (true = for, false = against).
    function vote(uint proposal, bool support) external nonReentrant {
        require(!govDisabled, "GOV: gov disabled");
        require(
            proposal < proposals.length &&
            block.timestamp < proposals[proposal].expiration,
            "GOV: invalid Proposal"
        );

        // Clear delegation.
        if (accounts[msg.sender].delegated) _delegate(msg.sender, address(0));

        // Update proposal and support.
        Account memory oldAcct = _loadAccount(msg.sender); // snapshot old
        accounts[msg.sender].proposal = uint112(proposal);
        accounts[msg.sender].support  = support;
        Account memory newAcct = _loadAccount(msg.sender); // snapshot new

        // Get voting weight.
        uint amount = newAcct.balance;
        if (newAcct.hasDelegatedVotes) amount += newAcct.delegatedVotes;

        // Apply vote change
        _updateGovernance(oldAcct, newAcct, amount);
        emit PositionUpdated(msg.sender, proposal, support, address(0));
    }

    /// @notice Delegates the caller’s voting power to `newDelegate`, or clears delegation when to address(0).
    /// @dev
    /// - If the caller holds delegated votes, their support is cleared to proposal 0.
    /// - Ensures delegated votes are never active while their holder is delegating.
    /// - Voting weight (balance) is then applied to newDelegate's support and will
    ///   follow their support until delegate is changed or caller votes directly.
    /// - Delegated votes do not chain. Votes will not be active if delegatee is delegating.
    /// @param newDelegate account address to delegate votes to.
    function delegate(address newDelegate) external nonReentrant {
        require(!govDisabled, "GOV: gov disabled");
        _delegate(msg.sender, newDelegate);
    }

    /// @notice Creates a governance proposal to update the engine.
    /// @dev
    /// - Transfers `STAKE` SUN, from proposer to this contract.
    /// - `engine` must contain code, be the DEAD address, or be address(0) for no action.
    /// - Proposal is active for VOTING_PERIOD unless ended abruptly from low support.
    /// - Reverts if governance is disabled or insufficient balance for stake.
    /// @param  engine     address of new engine (ignored if address(0))
    /// @param  actionCode Optional identifier for a governance signal; not executed by SUN.
    /// @return proposal   the newly created proposal id.
    function createProposal(address engine, uint actionCode) external nonReentrant returns (uint proposal) {
        require(!govDisabled, "GOV: gov disabled");
        require(engine.code.length > 0 || engine == DEAD || engine == address(0), "GOV: no code");

        // Stake proposal tokens to this contract
        _transfer(msg.sender, address(this), STAKE);

        // Push proposal
        proposals.push();
        proposal = proposals.length - 1;

        Proposal storage p = proposals[proposal];
        p.proposer   = msg.sender;
        p.engine     = engine;
        p.actionCode = actionCode;
        p.createdAt  = block.timestamp;
        p.expiration = block.timestamp + VOTING_PERIOD;

        emit ProposalCreated(proposal, msg.sender, engine, proposals[proposal].expiration);
    }

    /// @notice Finalizes a proposal after voting and timelock have ended.
    /// @dev
    /// - Reverts if `proposal` is invalid, already finalized, or still timelocked.
    /// - Refreshes both vote tallies.
    /// - A proposal passes only if pro votes meet `MIN_PRO` and the
    ///   `SUCCESS_BPS` support threshold, and governance has not been disabled.
    /// - Refunds proposal stake or burns it on low support or low turnout.
    /// - On success, either sets the new engine or disables governance permanently.
    /// @param proposal proposal to finalize.
    function finalizeProposal(uint proposal) external nonReentrant {
        require(proposal < proposals.length, "GOV: invalid proposal");
        Proposal memory p = proposals[proposal];

        require(!p.finalized, "GOV: already finalized");
        require(block.timestamp >= p.expiration + TIME_LOCK, "GOV: time lock");

        proposals[proposal].finalized = true;

        // Get fresh tallied votes.
        (p.votesPro, p.castPro, p.talliedPro) = _updateTallied(p.expiration, p.votesPro, 0);
        (p.votesCon, p.castCon, p.talliedCon) = _updateTallied(p.expiration, p.votesCon, 0);

        // Determine if proposal passed.
        bool passing;
        uint totalVotes = p.talliedPro + p.talliedCon;
        if (p.talliedPro >= MIN_PRO && totalVotes > 0) {
            if (p.talliedPro * 10_000 >= totalVotes * SUCCESS_BPS) passing = true;
        }
        if (govDisabled) passing = false; // if govDisabled is enabled, proposals cannot pass.

        // Process stake
        bool lowSupp = p.talliedPro * 10_000 < (p.talliedPro + p.talliedCon) * PENALTY_BPS; // insufficient support
        bool lowTurn = p.talliedPro * 2 < MIN_PRO;                                          // insufficient turnout
        if (lowSupp || lowTurn) _transfer(address(this), DEAD, STAKE);                      // burn stake
        else                    _transfer(address(this), p.proposer, STAKE);                // refund stake

        // Update engine or kill governance.
        if (passing) {
            if (p.engine == DEAD) govDisabled = true;
            else if (p.engine != address(0)) Ikernel(kernel).setEngine(p.engine);
        }
        emit ProposalOutcome(proposal, passing);
    }

    /// @notice Returns full metadata and tallied vote counts for a proposal.
    /// @dev Reverts if `id` is out of bounds.
    /// @return proposer   Address that created the proposal.
    /// @return engine     Proposed new reward engine.
    /// @return actionCode Identifier for a governance signal.
    /// @return createdAt  Timestamp the proposal was created.
    /// @return expiration Current expiration timestamp.
    /// @return castPro    Current cast votes in favor.
    /// @return castCon    Current cast votes against.
    /// @return talliedPro Current tallied votes in favor.
    /// @return talliedCon Current tallied votes against.
    /// @return finalized  Whether the proposal has been finalized
    function viewProposal(uint256 id)
        external
        view
        returns (
            address proposer,
            address engine,
            uint    actionCode,
            uint    createdAt,
            uint    expiration,
            uint    castPro,
            uint    castCon,
            uint    talliedPro,
            uint    talliedCon,
            bool    finalized
        )
    {
        require(id < proposals.length, "GOV: invalid proposal");

        Proposal memory p = proposals[id];

        // Get live tallies.
        (, castPro, talliedPro) = _updateTallied(p.expiration, p.votesPro, 0);
        (, castCon, talliedCon) = _updateTallied(p.expiration, p.votesCon, 0);

        proposer   = p.proposer;
        engine     = p.engine;
        actionCode = p.actionCode;
        createdAt  = p.createdAt;
        expiration = p.expiration;
        finalized  = p.finalized;
    }

    /// @notice Returns full governance state for `voter` (excluding token balance).
    /// @return proposal          Proposal id currently supported.
    /// @return support           True if voting in favor, false if against.
    /// @return delegated         Whether voting power is delegated away.
    /// @return hasDelegatedVotes Whether this account has received delegated votes.
    /// @return delegatee         Address receiving this account’s delegation.
    /// @return delegatedVotes    Voting power received via delegation.
    function voteOf(address voter)
        external
        view
        returns (
            uint256 proposal,
            bool    support,
            bool    delegated,
            bool    hasDelegatedVotes,
            address delegatee,
            uint256 delegatedVotes
        )
    {
        Account storage a = accounts[voter];
        return (
            a.proposal,
            a.support,
            a.delegated,
            a.hasDelegatedVotes,
            a.delegatee,
            a.delegatedVotes
        );
    }

    /*/////////////////////////////////////////////////////////////////////////
                             INTERNAL GOVERNANCE LOGIC
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Transfer hook for governance state.
    /// - Moves voting weight between `f` and `t` based on `amount`.
    /// - Only runs when governance is active and either side participates (vote or delegation).
    /// - Applies `_updateGovernance` to reconcile proposal tallies.
    /// - If either side’s target proposal is expired during the update,
    ///   clears their direct support (but preserves delegation state).
    /// - Uses memory snapshots; storage writes only occur on actual state changes.
    function _updateGovOnTransfer(Account memory f, Account memory t, uint amount) internal {
        if (!govDisabled &&
            (f.proposal != 0 || t.proposal != 0 || f.delegated || t.delegated))
        {
            // Apply governance change.
            (bool fromExp, bool toExp) = _updateGovernance(f, t, uint112(amount));

            // Clear expired proposal support.
            if (fromExp && !f.delegated) {
                emit PositionUpdated(f.addr, 0, false, address(0));
                accounts[f.addr].proposal = 0;
                accounts[f.addr].support  = false;
            }
            if (toExp && !t.delegated) {
                emit PositionUpdated(t.addr, 0, false, address(0));
                accounts[t.addr].proposal = 0;
                accounts[t.addr].support  = false;
            }
        }
    }

    /// @notice Updates `acct` to delegate its balance to `newDelegate`, or clears delegation when zero.
    /// @dev
    /// - `acct`'s direct vote support is always cleared before changing delegation.
    /// - If `acct` currently has delegated votes, that support is also removed first.
    /// - `acct.balance` will follow `newDelegate`'s support until delegate is changed.
    function _delegate(address acct, address newDelegate) internal {
        require(newDelegate != acct, "GOV: bad delegate");

        Account memory oldAcct  = _loadAccount(acct); // snapshot old

        // Clear support
        accounts[acct].proposal = 0;
        accounts[acct].support  = false;

        // Clear delegated vote support.
        // Delegated votes cannot remain active since this account is now delegating.
        if (!oldAcct.delegated && oldAcct.proposal != 0 && oldAcct.hasDelegatedVotes) {
            Account memory noSupAcct = _loadAccount(acct); // snapshot with no support
            _updateGovernance(oldAcct, noSupAcct, noSupAcct.delegatedVotes);
        }

        // Change delegate
        accounts[acct].delegated = newDelegate != address(0);
        accounts[acct].delegatee = newDelegate;
        Account memory newAcct = _loadAccount(acct); // snapshot new

        // Apply vote change
        _updateGovernance(oldAcct, newAcct, newAcct.balance);
        emit PositionUpdated(msg.sender, 0, false, newDelegate);
    }

    /// @notice Reconciles governance state for a balance or delegation change.
    /// @dev
    /// - `f` and `t` are memory snapshots describing the source and destination states of voting power.
    ///   They are not mutated, only used to infer where votes are moving from and to.
    /// - Resolves effective (proposal, support) for each side, including delegation.
    /// - Applies `amount` as a negative change to `f` and positive to `t` when their support differs.
    /// - Returns whether either side’s target proposal was expired during the update.
    function _updateGovernance(
        Account memory f,
        Account memory t,
        uint    amount
    ) internal returns (bool fromExp, bool toExp) {
        // Get 'from' support and update delegation.
        uint112 fromId;
        bool fromSup;
        if (f.delegated) {
            (fromId, fromSup) = _updateDelegation(f.delegatee, amount, false);
        } else { fromId = f.proposal; fromSup = f.support; }

        // Get 'to' support and update delegation.
        uint112 toId;
        bool toSup;
        if (t.delegated) {
            (toId, toSup) = _updateDelegation(t.delegatee, amount, true);
        } else { toId = t.proposal; toSup = t.support; }

        // Update proposal.
        if ((fromId != toId || fromSup != toSup) && amount != 0){
            fromExp = _updateProposal(fromId, fromSup, -int(amount));
            toExp   = _updateProposal(toId,   toSup,    int(amount));
        }
    }

    /// @notice Applies a delegation weight change to `delegatee`.
    /// @dev
    /// - Adds or removes `amount` from `delegatee`’s delegatedVotes depending on `to`.
    /// - Maintains `hasDelegatedVotes` flag based on resulting balance.
    /// - Returns the delegate’s current `(proposal, support)` for downstream vote updates.
    /// @param to true if votes are going to `delegatee`.
    function _updateDelegation(address delegatee, uint amount, bool to)
        internal
        returns (uint112 proposal, bool support)
    {
        Account memory d = _loadAccount(delegatee);
        proposal = d.proposal;
        support  = d.support;
        if (amount == 0) return(proposal, support);
        if (to) {
            if (!d.hasDelegatedVotes) accounts[d.addr].hasDelegatedVotes = true;
            accounts[d.addr].delegatedVotes += uint112(amount);
        } else {
            accounts[d.addr].delegatedVotes -= uint112(amount);
            if (accounts[d.addr].delegatedVotes == 0) {
                accounts[d.addr].hasDelegatedVotes = false;
            }
        }
    }

    /// @notice Applies a vote change to a proposal and refreshes its tallies.
    /// @dev
    /// - Rejects invalid proposal ids.
    /// - If proposal is Expired, no updates can be made and returns `true`.
    /// - Refreshes delayed tallies, applies `change` to either pro or con votes,
    ///   emits {proposalUpdated}, and writes the updated packed vote state back.
    /// - If the proposal is in the second half of voting and pro support is below
    ///   `MIN_PRO` before or after the change, the proposal is expired immediately.
    function _updateProposal(
        uint id,
        bool support,
        int  change
    ) internal returns (bool expired){
        if (id == 0) return false;                        // support for 0 id allowed
        if (id >= proposals.length) return true;          // support for invalid id is cleared

        // Load Proposal.
        Proposal memory p;
        p.expiration = proposals[id].expiration;
        if (block.timestamp >= p.expiration) return true; // Early return for expired.
        p.votesPro = proposals[id].votesPro;
        p.votesCon = proposals[id].votesCon;

        // Load fresh vote tally (before change).
        (p.votesPro, p.castPro, p.talliedPro) = _updateTallied(p.expiration, p.votesPro, 0);
        (p.votesCon, p.castCon, p.talliedCon) = _updateTallied(p.expiration, p.votesCon, 0);

        bool pastHalf = block.timestamp >= p.expiration - (VOTING_PERIOD / 2);
        bool lowPro   = p.talliedPro < MIN_PRO;           // true if proposal should expire early.

        // Apply vote change.
        if (!(lowPro && pastHalf)) {
            (p.votesPro, p.castPro, p.talliedPro) = _updateTallied(p.expiration, p.votesPro, support ? change : int(0));
            (p.votesCon, p.castCon, p.talliedCon) = _updateTallied(p.expiration, p.votesCon, support ? int(0) : change);
            emit proposalUpdated(id, p.castPro, p.castCon, p.talliedPro, p.talliedCon);
            lowPro = p.talliedPro < MIN_PRO;
        }

        // Store votes.
        proposals[id].votesPro = p.votesPro;
        proposals[id].votesCon = p.votesCon;

        // Kill proposal if voting is half passed and below MIN_PRO.
        // Due to delayed tallying, pre-halfway votes can finalize within 1–2 windows after halfway.
        if (lowPro && pastHalf) {
                proposals[id].expiration =  p.expiration - (VOTING_PERIOD / 2);
                return true;
        }
    }

    /// @dev Updates one side's delayed whole-token vote tally.
    /// `lowest` tracks the low of the current 10-minute window.
    /// `closedLow` tracks the low of the last fully closed window.
    /// The effective tally is `min(closedLow, lowest)`.
    ///
    /// This makes drops immediate and spikes delayed:
    /// - drops lower `lowest` right away
    /// - increases only raise the tally after the old low ages out
    ///   across 1 to 2 full windows (10 to 20 minutes)
    function _updateTallied(
        uint exp,
        uint word,
        int change
    ) internal view returns (uint256 packed, uint112 cast, uint32 tallied) {
                cast      = uint112(word >> 144);
        uint40  window    = uint40(word >> 104);
        uint32  lowest    = uint32(word >> 72);
        uint40  last      = uint40(word >> 32);
        uint32  closedLow = uint32(word);

        uint40 current = uint40(block.timestamp > exp ? exp : block.timestamp); // cap to expiration
        uint40 delay   = uint40(TALLY_WINDOW);
        uint32 count   = uint32(cast / 1e18);

        bool seedAfterChange;

        // If one or more 10-minute windows have closed, roll forward.
        if (current >= window + delay) {
            uint40 nextWindow = current - ((current - window) % delay);

            // The window that just closed is [nextWindow - delay, nextWindow).
            // If the last change happened inside that window, `lowest` already
            // is that window's low. Otherwise the balance was flat for the whole
            // window, so its low was simply `count`.
            //
            // Strict `>` is intentional: a change exactly on the boundary belongs
            // to the new live window, not the one that just closed.
            closedLow = (last > nextWindow - delay) ? lowest : count;

            // Start the new live window.
            window = nextWindow;

            // Usually the new window starts with the current count.
            // But if a change lands exactly on the boundary, let the
            // post-change balance seed the new window instead.
            seedAfterChange = (change != 0 && current == nextWindow);
            if (!seedAfterChange) {
                lowest = count;
            }
        }

        // Apply the live balance change.
        if (change != 0) {
            cast = change > 0
                ? cast + uint112(uint(change))
                : cast - uint112(uint(-change));

            count = uint32(cast / 1e18);
            last  = current;

            // Drops hit immediately. Boundary changes seed a fresh window.
            if (seedAfterChange || count < lowest) {
                lowest = count;
            }
        }

        // Effective tally:
        // - `lowest` makes drops immediate
        // - `closedLow` delays rises until enough time has passed
        tallied = closedLow < lowest ? closedLow : lowest;

        packed =
            (uint256(cast)   << 144) |
            (uint256(window) << 104) |
            (uint256(lowest) << 72)  |
            (uint256(last)   << 32)  |
            uint256(closedLow);
    }
}
          

/Kernel.sol

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

/*─────────────────────────────  CONTRACT  ──────────────────────────────*/
/**
@title Kernel
@notice Protocol coordinator that connects SUN and MOON to the engine.
@dev
• Stores the current engine implementation.
• Delegatecalls protocol logic into the Engine.
• Keeps all persistent protocol state in Kernel storage.
• Exposes the protocol ABI through the Engine implementation.

ABI note
• The proxy’s effective ABI is the Engine ABI.
• Kernel itself only natively exposes:
    - engine()
    - fallback()

Internal call routes

• MOON -> Kernel fallback -> Engine.onMoonTransfer(...)
    Transfer tax logic and system maintenance.

• MOON -> Kernel fallback -> Engine.isUnlocked(...)
    Swap-lock check used by MOON.balanceOf(...).

• SUN -> Kernel fallback -> Engine.onSunTransfer(...)
    Reward-accounting updates for SUN transfers.

• SUN -> Kernel fallback -> Engine.collect(...)
    Optional manual reward collection.

• Engine -> MOON fallback -> kernelTransfer(...)
    Internal untaxed MOON transfer.

• Engine -> SUN fallback -> releaseToMember(...)
    Reward payout to SUN holders.

• MOON -> SUN fallback -> sharedReentrancy(...)
    Shared protocol reentrancy lock sync.

• Engine -> Kernel fallback -> Engine.swapWithPair(...)
    Isolated self-call for swapBack execution.

• Kernel constructor -> delegatecall Engine.initialize(...)
    One-time engine initialization.

• SUN -> Kernel.setEngine(...)
    Engine upgrade path.

Notes
• All persistent storage lives in Kernel.
• Engine logic must run through Kernel delegatecall.
*/
contract Kernel {
    /*/////////////////////////////////////////////////////////////////////////
                                      ERRORS
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Function restricted to callable only by SUN.
    error InternalOnly();

    /*/////////////////////////////////////////////////////////////////////////
                                    IMMUTABLES
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Immutable SUN governance address that can upgrade the engine.
    address private immutable sun;

    /*/////////////////////////////////////////////////////////////////////////
                            UNSTRUCTURED ENGINE STORAGE
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev ERC-1967 implementation slot.
    /// Using the standard slot lets explorers and Sourcify-style tooling
    /// resolve Kernel as a proxy and surface the current engine ABI.
    bytes32 private constant ENGINE_SLOT =
        0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /*/////////////////////////////////////////////////////////////////////////
                                      EVENTS
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev ERC-1967 upgrade event used by proxy-aware tooling.
    event Upgraded(address indexed implementation);

    /*/////////////////////////////////////////////////////////////////////////
                                    CONSTRUCTOR
    /////////////////////////////////////////////////////////////////////////*/

    constructor(address sun_, address sunDeployer_, address engine_) {
        require(sun_ != address(0), "zero address");
        sun = sun_;

        _setEngineInternal(engine_);

        // Delegate initialize(address) into the engine.
        (bool ok, ) = engine_.delegatecall(
            abi.encodeWithSignature("initialize(address)", sunDeployer_)
        );
        require(ok, "initialization failed");
    }

    /*/////////////////////////////////////////////////////////////////////////
                                 VIEW FUNCTIONS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Returns the current engine implementation.
    /// @dev Matches the `engine()` getter shape of a public state variable.
    function engine() public view returns (address impl) {
        bytes32 slot = ENGINE_SLOT;
        assembly ("memory-safe") {
            impl := and(sload(slot), 0xffffffffffffffffffffffffffffffffffffffff)
        }
    }

    /*/////////////////////////////////////////////////////////////////////////
                                 INTERNAL HELPERS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Lets SUN set a new `engine`.
    /// @dev Engine cannot be address(0).
    function _setEngineInternal(address newEngine) internal {
        require(newEngine != address(0), "zero address");

        bytes32 slot = ENGINE_SLOT;
        assembly ("memory-safe") {
            sstore(slot, newEngine)
        }

        emit Upgraded(newEngine);

        _notifyNewEngineSet(newEngine); // tell new engine
    }

    /// @notice Optional post-upgrade hook.
    /// @dev
    /// Delegatecalls `newEngineSet()` on the new engine.
    /// If the engine does not implement the hook, or the call fails for any reason,
    /// the upgrade still succeeds.
    function _notifyNewEngineSet(address newEngine) internal {
        newEngine.delegatecall(abi.encodeWithSignature("newEngineSet()"));
    }

    /// @notice Delegates call to `engine` and bubbles revert.
    function _delegate(address engine_) internal {
        assembly {
            // Copy calldata to memory
            calldatacopy(0, 0, calldatasize())

            // Delegatecall to implementation
            let result := delegatecall(gas(), engine_, 0, calldatasize(), 0, 0)

            // Copy returndata to memory
            returndatacopy(0, 0, returndatasize())

            // Bubble result
            switch result
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /*/////////////////////////////////////////////////////////////////////////
                                 FALLBACK ROUTER
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Selector for SUN-governed engine upgrades.
    bytes4 private constant SET_ENGINE_SELECTOR =
        bytes4(keccak256("setEngine(address)"));

    /// @notice Internal communication and proxy delegation entrypoint.
    /// @dev
    /// Kernel intentionally keeps a minimal native ABI.
    /// Only `setEngine(address)` is handled directly here.
    /// Everything else is delegated to the current engine implementation.
    fallback() external {
        bytes4 sel;
        assembly ("memory-safe") {
            sel := calldataload(0)
        }

        // setEngine(address)
        if (msg.data.length == 36 && sel == SET_ENGINE_SELECTOR) {
            if (msg.sender != sun) revert InternalOnly();

            address newEngine;
            assembly ("memory-safe") {
                newEngine := and(calldataload(4), 0xffffffffffffffffffffffffffffffffffffffff)
            }

            _setEngineInternal(newEngine);
            return;
        }

        _delegate(engine());
    }
}
          

/FontBanner.sol

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

/*

███████ ██    ██ ███    ██ 
██      ██    ██ ████   ██ 
███████ ██    ██ ██ ██  ██ 
     ██ ██    ██ ██  ██ ██ 
███████  ██████  ██   ████ 
                           
                           
 │ Transfers                                                              │
 │  - Standard ERC-20 (no fees/taxes on SUN).                             │
 │  - Before balances change, SUN calls                                   │
 │    `Moon.transferSun(from, to, fromBal, toBal, amount)` so Moon can    │
 │    update its books.                                                   │
 │                                                                        │
 │ What SUN does not do                                                   │
 │  - No taxes. No governance. No upgrades. No owner/admin keys.          │
 │                                                                        │
 └────────────────────────────────────────────────────────────────────────┘
→
/*───────────────────────────  INTERFACES  ───────────────────────────────*/

/// @notice This contract exists only to publish the banner above in verified source.
contract FontBanner {}
          

/fairLaunch.sol

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

/**
 * @title FairLaunchMigrator
 * @notice Permissionless Uniswap V3 → Uniswap V2 liquidity migrator built specifically for the Sun & Moon fair-launch.
 *
 * @dev Launch design (Sun & Moon):
 * - For each token, ~50% of total supply is sold via a Uniswap V3 position (the “raise” occurs in the pairing token).
 * - Once the minimum raise threshold is met, this contract pulls the entire V3 position (liquidity + accrued fees),
 *   optionally performs a corrective swap against an existing V2 pool (if one exists with reserves) to balance ratios,
 *   and then adds liquidity on Uniswap V2 by pairing:
 *     (raised pairingToken) + (the remaining ~50% of the launch token supply held by this contract).
 * - The resulting V2 LP tokens are sent to the dead address (0x...dEaD), effectively locking the liquidity permanently.
 *
 * @dev Key properties:
 * - Permissionless: anyone can call {migrate()} once {minRaised} is satisfied.
 * - One-shot: the V3 NFT is recorded once via {recordNft()}, and migration can only be executed once.
 * - Threshold protected: {minRaised} is enforced using the net increase in pairingToken from V3 collection (delta-snapshot),
 *   preventing accidental transfers from satisfying the raise requirement.
 * - Safety-focused: includes a small reentrancy guard and best-effort “dust” handling after migration.
 */

interface IERC20 {
    function approve(address spender, uint256 amount) external returns (bool);
    function balanceOf(address owner) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
}

interface IUniswapV2Router02 {
    function factory() external view returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
}

interface IUniswapV2Factory {
    function getPair(address tokenA, address tokenB) external view returns (address);
}

interface IUniswapV2Pair {
    function token0() external view returns (address);
    function token1() external view returns (address);

    function getReserves()
        external
        view
        returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);

    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function sync() external;
}

/* ---------------- Uniswap V3 minimal ---------------- */
interface IUniswapV3Factory {
    function getPool(address token0, address token1, uint24 fee) external view returns (address);
}

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

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

    function positions(uint256 tokenId)
        external
        view
        returns (
            uint96  nonce,
            address operator,
            address token0,
            address token1,
            uint24  fee,
            int24   tickLower,
            int24   tickUpper,
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    function decreaseLiquidity(DecreaseLiquidityParams calldata params)
        external
        returns (uint256 amount0, uint256 amount1);

    function collect(CollectParams calldata params) external returns (uint256 amount0, uint256 amount1);

    function factory() external view returns (address);
}

interface IERC721Like {
    function ownerOf(uint256 tokenId) external view returns (address);
    function getApproved(uint256 tokenId) external view returns (address);
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

/* -------------------- SafeERC20 (minimal) -------------------------- */
library SafeERC20 {
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        (bool ok, bytes memory data) =
            address(token).call(abi.encodeWithSelector(token.approve.selector, spender, value));
        require(ok && (data.length == 0 || abi.decode(data, (bool))), "SafeERC20: approve failed");
    }

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        (bool ok, bytes memory data) =
            address(token).call(abi.encodeWithSelector(token.transfer.selector, to, value));
        require(ok && (data.length == 0 || abi.decode(data, (bool))), "SafeERC20: transfer failed");
    }
}

contract FairLaunchMigrator {
    using SafeERC20 for IERC20;

    /* ------------------ Immutable config ------------------ */
    address public immutable token;            // launch token address
    address public immutable pairingToken;     // pairing token
    INonfungiblePositionManager public immutable positionManager;

    IUniswapV2Router02 public immutable v2Router;
    address public immutable v2Factory;        // derived from router.factory() (pre-launch validated)

    uint24 public immutable v3Fee;
    uint256 public immutable minRaised;        // enforced threshold in pairingToken units

    // V2 fee expressed as numerator/denominator (e.g., 997/1000 for 0.30%)
    uint256 private immutable V2_FEE_NUM;
    uint256 private immutable V2_FEE_DEN;

    /* ------------------ State ------------------------------ */
    bool public migrated;
    uint256 public v3NftId; // recorded once

    // small reentrancy guard (migrate does multiple external calls)
    bool private _migrating;

    /* ------------------ Constants -------------------------- */
    address internal constant DEAD = 0x000000000000000000000000000000000000dEaD;

    /* ------------------ Events ----------------------------- */
    event V3NftRecorded(uint256 tokenId);
    event MigratedToV2(uint256 tokenUsed, uint256 pairingUsed, uint256 lpMinted, address caller);

    /* ------------------ Errors ----------------------------- */
    error ZeroAddress();
    error SameToken();
    error V2FeeInvalid();
    error NftAlreadySet();
    error MigratorNotNftOwner();
    error WrongFee();
    error WrongPair();
    error AlreadyMigrated();
    error NoNFT();
    error MinRaisedNotMet(uint256 got, uint256 min);
    error NothingToMigrate();
    error Reentrancy();
    error V2FactoryMismatch();

    modifier nonReentrant() {
        if (_migrating) revert Reentrancy();
        _migrating = true;
        _;
        _migrating = false;
    }

    constructor(
        address _token,
        address _pairingToken,
        address _positionMgr,
        address _v2Router,
        address _v2Factory,
        address /* _v3Factory (kept for script compatibility; unused) */,
        uint24  _v3Fee,
        uint256 _minRaised,
        uint256 _v2FeeNumerator,
        uint256 _v2FeeDenominator
    ) {
        if (_token == address(0) || _pairingToken == address(0) || _positionMgr == address(0) || _v2Router == address(0) || _v2Factory == address(0)) {
            revert ZeroAddress();
        }
        if (_token == _pairingToken) revert SameToken();

        if (_v2FeeDenominator == 0 || _v2FeeNumerator >= _v2FeeDenominator) revert V2FeeInvalid();

        token = _token;
        pairingToken = _pairingToken;
        positionManager = INonfungiblePositionManager(_positionMgr);

        v2Router = IUniswapV2Router02(_v2Router);

        // Fail fast pre-launch if someone passed the wrong factory for the router.
        address derivedFactory = IUniswapV2Router02(_v2Router).factory();
        if (derivedFactory != _v2Factory) revert V2FactoryMismatch();
        v2Factory = derivedFactory;

        v3Fee = _v3Fee;
        minRaised = _minRaised;

        V2_FEE_NUM = _v2FeeNumerator;
        V2_FEE_DEN = _v2FeeDenominator;

        // Pre-approve router once (reduces migrate() failure surface)
        IERC20(token).safeApprove(_v2Router, type(uint256).max);
        IERC20(pairingToken).safeApprove(_v2Router, type(uint256).max);
    }

    // Tell Moon not to send rewards here. 
    function extsload(bytes32) external pure returns (bytes32) {
        return bytes32(0);
    }

    /* ---------------------- Record the NFT (one-shot) ------ */
    function recordNft(uint256 tokenId) external {
        if (v3NftId != 0) revert NftAlreadySet();

        IERC721Like erc721 = IERC721Like(address(positionManager));
        if (erc721.ownerOf(tokenId) != address(this)) revert MigratorNotNftOwner();

        (, , address token0, address token1, uint24 fee, , , , , , , ) = positionManager.positions(tokenId);
        if (fee != v3Fee) revert WrongFee();

        bool okPair =
            (token0 == token && token1 == pairingToken) || (token0 == pairingToken && token1 == token);
        if (!okPair) revert WrongPair();

        v3NftId = tokenId;
        emit V3NftRecorded(tokenId);
    }

    /* ---------------------- Migrate (one-shot) -------------- */
    function migrate() external nonReentrant {
        if (migrated) revert AlreadyMigrated();
        uint256 id = v3NftId;
        if (id == 0) revert NoNFT();

        // Snapshot pairingToken balance so minRaised is enforced on actual V3 proceeds (delta),
        // not on any accidental transfers to this contract.
        uint256 pairingBefore = IERC20(pairingToken).balanceOf(address(this));

        // --- Pull all V3 liquidity + fees to this contract (no slot0/TickMath assumptions)
        (, , , , , , , uint128 liq, , , , ) = positionManager.positions(id);

        if (liq > 0) {
            positionManager.decreaseLiquidity(
                INonfungiblePositionManager.DecreaseLiquidityParams({
                    tokenId: id,
                    liquidity: liq,
                    amount0Min: 0,
                    amount1Min: 0,
                    deadline: block.timestamp
                })
            );
        }

        positionManager.collect(
            INonfungiblePositionManager.CollectParams({
                tokenId: id,
                recipient: address(this),
                amount0Max: type(uint128).max,
                amount1Max: type(uint128).max
            })
        );

        uint256 tokenBal = IERC20(token).balanceOf(address(this));
        uint256 pairingBal = IERC20(pairingToken).balanceOf(address(this));

        uint256 pairingFromV3 = 0;
        if (pairingBal > pairingBefore) pairingFromV3 = pairingBal - pairingBefore;

        if (pairingFromV3 < minRaised) revert MinRaisedNotMet(pairingFromV3, minRaised);
        if (tokenBal == 0 || pairingBal == 0) revert NothingToMigrate();

        // --- Corrective swap through existing V2 pair (if exists & has reserves)
        address pair = IUniswapV2Factory(v2Factory).getPair(token, pairingToken);
        bool pairHadLiquidity = false;

        if (pair != address(0) && pair.code.length > 0) {
            (uint112 r0, uint112 r1, ) = IUniswapV2Pair(pair).getReserves();
            if (r0 > 0 && r1 > 0) {
                pairHadLiquidity = true;

                address t0 = IUniswapV2Pair(pair).token0();
                bool token0IsLaunch = (t0 == token);

                // Map reserves to (A=launch token, B=pairing token)
                uint256 Ra = token0IsLaunch ? uint256(r0) : uint256(r1);
                uint256 Rb = token0IsLaunch ? uint256(r1) : uint256(r0);

                // Compare tokenBal/pairingBal vs Ra/Rb using overflow-safe product compare:
                // tokenBal*Rb ? pairingBal*Ra
                int8 cmp = _mulCmp(tokenBal, Rb, pairingBal, Ra);

                if (cmp > 0) {
                    // token-heavy: swap launch token -> pairing token
                    (uint256 xIn, ) = _optimalSwapIn(tokenBal, pairingBal, Ra, Rb);
                    _swapExactIn(pair, token, xIn, Ra, Rb, token0IsLaunch);
                } else if (cmp < 0) {
                    // pairing-heavy: swap pairing token -> launch token
                    (uint256 xIn, ) = _optimalSwapIn(pairingBal, tokenBal, Rb, Ra);
                    _swapExactIn(pair, pairingToken, xIn, Rb, Ra, !token0IsLaunch);
                }

                tokenBal = IERC20(token).balanceOf(address(this));
                pairingBal = IERC20(pairingToken).balanceOf(address(this));
                if (tokenBal == 0 || pairingBal == 0) revert NothingToMigrate();
            }
        }

        // --- Add liquidity, LP to DEAD
        (uint256 usedA, uint256 usedB, uint256 lp) = v2Router.addLiquidity(
            pairingToken,  // A
            token,         // B
            pairingBal,
            tokenBal,
            0,
            0,
            DEAD,
            block.timestamp
        );

        migrated = true;

        // --- Best-effort dust handling (cannot brick migration).
        // If pair had pre-existing liquidity, burn dust to DEAD (avoid donating to attacker LPs).
        address pairAfter = IUniswapV2Factory(v2Factory).getPair(token, pairingToken);
        _bestEffortDust(pairAfter, pairHadLiquidity);

        // event order: tokenUsed = usedB, pairingUsed = usedA
        emit MigratedToV2(usedB, usedA, lp, msg.sender);
    }

    /* ---------------------- Views -------------------------- */
    function getPool() external view returns (address) {
        if (v3NftId == 0) revert NoNFT();
        (, , address token0, address token1, uint24 fee, , , , , , , ) = positionManager.positions(v3NftId);
        return IUniswapV3Factory(positionManager.factory()).getPool(token0, token1, fee);
    }

    function v2Pair() external view returns (address) {
        return IUniswapV2Factory(v2Factory).getPair(token, pairingToken);
    }

    /* ---------------------- Internal: V2 math -------------- */

    function _getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
        internal
        view
        returns (uint256 amountOut)
    {
        if (amountIn == 0 || reserveIn == 0 || reserveOut == 0) return 0;

        unchecked {
            uint256 amountInWithFee = amountIn * V2_FEE_NUM;
            uint256 numerator = amountInWithFee * reserveOut;
            uint256 denominator = reserveIn * V2_FEE_DEN + amountInWithFee;
            if (denominator == 0) return 0;
            amountOut = numerator / denominator;
        }

        if (amountOut >= reserveOut) amountOut = reserveOut - 1;
    }

    /// @dev swap exact-in against pair using ACTUAL amount received by pair (fee-on-transfer tolerant)
    function _swapExactIn(
        address pair,
        address tokenIn,
        uint256 amountInDesired,
        uint256 reserveIn,
        uint256 reserveOut,
        bool token0IsIn
    ) internal {
        if (amountInDesired == 0) return;

        uint256 balBefore = IERC20(tokenIn).balanceOf(pair);
        IERC20(tokenIn).safeTransfer(pair, amountInDesired);
        uint256 balAfter = IERC20(tokenIn).balanceOf(pair);
        if (balAfter <= balBefore) return;

        uint256 amountInActual = balAfter - balBefore;
        uint256 out = _getAmountOut(amountInActual, reserveIn, reserveOut);
        if (out == 0) return;

        if (token0IsIn) {
            IUniswapV2Pair(pair).swap(0, out, address(this), new bytes(0));
        } else {
            IUniswapV2Pair(pair).swap(out, 0, address(this), new bytes(0));
        }
    }

    /// @dev binary search for swap-in to align wallet ratio to pool ratio, using 512-bit compares
    function _optimalSwapIn(
        uint256 a0,
        uint256 b0,
        uint256 Ra,
        uint256 Rb
    ) internal view returns (uint256 xIn, uint256 yOut) {
        if (a0 == 0 || Ra == 0 || Rb == 0) return (0, 0);

        uint256 gN = V2_FEE_NUM;
        uint256 gD = V2_FEE_DEN;

        uint256 lo = 0;
        uint256 hi = a0;

        for (uint256 i = 0; i < 32; ++i) {
            uint256 mid = (lo + hi) >> 1;

            uint256 gmid;
            unchecked { gmid = gN * mid; }

            uint256 denom;
            unchecked { denom = gD * Ra + gmid; }

            uint256 y = 0;
            if (denom != 0) {
                unchecked { y = (gmid * Rb) / denom; }
            }
            if (y >= Rb) y = (Rb == 0) ? 0 : (Rb - 1);

            uint256 lhsA = a0 - mid;
            uint256 lhsB = Rb - y;

            uint256 rhsA;
            unchecked { rhsA = b0 + y; }

            uint256 rhsB = Ra + (gmid / gD);

            if (_mulCmp(lhsA, lhsB, rhsA, rhsB) > 0) lo = mid + 1;
            else hi = mid;
        }

        xIn = hi;

        uint256 gxi;
        unchecked { gxi = gN * xIn; }

        uint256 denom2;
        unchecked { denom2 = gD * Ra + gxi; }

        if (denom2 == 0) return (xIn, 0);

        unchecked { yOut = (gxi * Rb) / denom2; }
        if (yOut >= Rb) yOut = (Rb == 0) ? 0 : (Rb - 1);
    }

    /* ---------------------- Internal: dust (best-effort) ---- */

    function _bestEffortDust(address pair, bool pairHadLiquidity) internal {
        uint256 tBal = IERC20(token).balanceOf(address(this));
        uint256 pBal = IERC20(pairingToken).balanceOf(address(this));
        if (tBal == 0 && pBal == 0) return;

        if (pairHadLiquidity) {
            if (tBal > 0) _tryTransfer(token, DEAD, tBal);
            if (pBal > 0) _tryTransfer(pairingToken, DEAD, pBal);
            return;
        }

        if (pair != address(0) && pair.code.length > 0) {
            if (tBal > 0) _tryTransfer(token, pair, tBal);
            if (pBal > 0) _tryTransfer(pairingToken, pair, pBal);
            _trySync(pair);
        } else {
            if (tBal > 0) _tryTransfer(token, DEAD, tBal);
            if (pBal > 0) _tryTransfer(pairingToken, DEAD, pBal);
        }
    }

    function _tryTransfer(address token_, address to, uint256 value) internal {
        (bool ok, bytes memory data) =
            token_.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
        if (!ok) return;
        if (data.length == 32) {
            uint256 r;
            assembly { r := mload(add(data, 0x20)) }
            if (r == 0) return;
        }
    }

    function _trySync(address pair) internal {
        (bool ok, ) = pair.call(abi.encodeWithSelector(IUniswapV2Pair.sync.selector));
        ok;
    }

    /* ---------------------- Internal: 512-bit mul compare --- */

    function _mul512(uint256 a, uint256 b) private pure returns (uint256 hi, uint256 lo) {
        assembly {
            let mm := mulmod(a, b, not(0))
            lo := mul(a, b)
            hi := sub(sub(mm, lo), lt(mm, lo))
        }
    }

    /// @return  1 if a*b > c*d, -1 if a*b < c*d, 0 if equal (full 512-bit compare)
    function _mulCmp(uint256 a, uint256 b, uint256 c, uint256 d) private pure returns (int8) {
        (uint256 ahi, uint256 alo) = _mul512(a, b);
        (uint256 bhi, uint256 blo) = _mul512(c, d);

        if (ahi > bhi) return 1;
        if (ahi < bhi) return -1;
        if (alo > blo) return 1;
        if (alo < blo) return -1;
        return 0;
    }
}
          

/Engine.sol

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

/* ───────────────────────  INTERNAL CONCEPT MAP  ─────────────────────────

Engine is the delegatecalled accounting implementation for the SUN/MOON protocol.
It classifies MOON transfers, records taxed value by pair, performs delayed swap-backs,
and distributes realized paired-token value to eligible SUN holders.

Engine owns no persistent protocol state. All storage is Kernel storage; this contract
is logic that must run through delegatecall.

Entities:
• Ray    – a taxed V2 pair containing MOON (identified by pair address).
• Member – a SUN holder eligible for indexed value accrual.
• Global – aggregate accounting state (pool, oath, index, lists).

Global accounting:
• pool  – total MOON-value of tokens currently available for distribution.
• oath  – total nominal claim asserted by the index. (scaled by coverage)
• index – cumulative value-per-eligible-SUN used to track per member claims.
• Coverage is expressed implicitly as pool / oath; payouts are scaled by
  this ratio so the system only pays value it actually holds.
• work – per-ray gas credit system that supports system liveliness by
  throttling expensive rays and rewarding productive ones.

Value realization (reactive):
• A MOON transfer involving a Ray may be classified as a taxed swap via
  pair reserve delta heuristics.
• SWAP_TAX MOON is attributed to the Ray as pending value.
• Pending MOON is not swapped immediately; it is swapped back later,
  opportunistically, during the next Ray's transaction.
• swap-backs buy the paired token with pending MOON.
• Some pending is burnt to pay APPRECIATION_TAX.
• After taxation, newly credited value enters pool, oath, and the distribution index.

Distribution:
• Rays are separated into two value-tiered lists CORE and EDGE for emission
  efficiency.
• Emissions pseudo-randomly select rays weighted by pool/core value share.
• Members are paid value proportional to token holdings from index.
*/

/// @notice Sun payout used in _emitRay.
interface ISun {
    function releaseToMember(
        address ray,
        uint amount,
        address member,
        bool requireIncrease
    ) external;
}

/// @notice Internal channel to move MOON tokens.
interface IMoon {
    function kernelTransfer(
        address from,
        address to,
        uint256 amount
    ) external returns (uint moveAmt);
}

/// @dev Uniswap V2 Pair subset used by the contract’s swap-backs.
interface IUniswapV2Pair {
    function swap(
        uint amount0Out,
        uint amount1Out,
        address to,
        bytes calldata data
    ) external;
}

/// @notice Internal logic engine for MOON tax, swap-back, and SUN distribution.
/// @dev
/// - Decides how much MOON tax applies to transfers involving rays.
/// - Executes swap-backs that convert taxed MOON into paired tokens.
/// - Distributes paired-token value to eligible SUN holders.
/// - This contract is logic-only and is intended to run only through delegatecall from the kernel.
/// - All persistent storage lives in the kernel, not in this implementation.
/// - Direct calls to Engine are invalid and should never be made by users or external contracts.
/// @custom:version Engine-1.0
contract Engine {
    /*/////////////////////////////////////////////////////////////////////////
                                    EVENTS
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Fired once when a new ray (paired token) is discovered & cached.
    event RayInitialized(address indexed ray, address indexed token);

    /*/////////////////////////////////////////////////////////////////////////
                                    ERRORS
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Function restricted to callable only by SUN or MOON.
    error InternalOnly();

    /// @dev Reverts if engine is called directly instead of via proxy.
    error ProxyOnly();

    /*/////////////////////////////////////////////////////////////////////////
                                    MODIFIERS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Engine self.
    address private immutable SELF = address(this);

    /// @dev Only callable via proxy (delegatecall)
    modifier onlyProxy() {
        if (address(this) == SELF) revert ProxyOnly();
        _;
    }

    /*/////////////////////////////////////////////////////////////////////////
                                    IMMUTABLES
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice MOON ERC‑20 contract linked for tax collection.
    address private immutable moon;

    /// @notice SUN ERC‑20 contract linked for distributions.
    address private immutable sun;

    /// @notice Wrapped native gas token.
    address private immutable wbase;

    /*/////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
    /////////////////////////////////////////////////////////////////////////*/

    /*───────────────────────────── CORE SYSTEM ─────────────────────────────*/

    /// @notice Swap tax rate.
    uint private constant SWAP_TAX         = 3e16;  // 3%

    /// @notice Percentage of swap-back left in the pair as an LP bonus.
    uint private constant PAIR_BONUS       = 25e16; // 25%

    /// @notice Appreciation tax applied to positive value deltas.
    uint private constant APPRECIATION_TAX = 20e16; // 20%

    /// @notice Sentinel transfer amount (wei) that requests MOON to re-probe an address.
    uint private constant REFRESH_WEI      = 369;   // 369 wei SUN/MOON

    /// @notice Protocol-internal MOON dust threshold (not user-facing).
    uint private constant DUST             = 1e9;   // 1e-9 MOON

    /*─────────────────────────── GAS MANAGEMENT ───────────────────────────*/

    /// @notice Gas target for the radiation loop.
    uint private constant GAS_RADIATE      = 250_000;

    /// @notice Gas target for swapBack with pair.
    uint private constant GAS_BASE_SWAP    = 200_000;

    /// @notice Gas cap for swapBack with pair.
    uint private constant GAS_CAP_SWAP     = 400_000;

    /// @notice Gas target for release to member call.
    uint private constant GAS_BASE_RELEASE = 50_000;

    /// @notice Gas cap for release to member call.
    uint private constant GAS_CAP_RELEASE  = 250_000;

    /// @notice Low gas cap default.
    uint private constant GAS_PROBE_LOW    = 20_000;

    /// @notice High gas cap default.
    uint private constant GAS_PROBE_HIGH   = 30_000;

    /*─────────────────────────── WORK ACCOUNTING ───────────────────────────*/

    /// @notice work credited to rays for running maintenance.
    int  private constant WORK_CREDIT      = 150;

    /// @notice work threshold to be added for swapBack.
    int  private constant WORK_SWAPBACK    = 300;

    /// @notice work threshold to be added for distribution.
    int private constant WORK_DISTRIBUTION = 150;

    /// @notice max work credits a ray can hold.
    int  private constant WORK_MAX         = 750;

    /// @notice work fee for priority emission.
    int  private constant PRIORITY_FEE     = 450;

    /// @notice work level that ray is removed from distribution lists.
    int  private constant WORK_REMOVAL     = -450;

    /*────────────────────────── MEMBER THRESHOLDS ──────────────────────────*/

    /// @notice Minimum SUN tokens for active member list
    uint private constant MIN_ACTIVE       = 1e20;  // 100

    /// @notice Minimum SUN tokens for prime member list
    uint private constant MIN_PRIME        = 5e22;  // 50k

    /*────────────────────────── SYSTEM ADDRESSES ───────────────────────────*/

    /// @notice Conventional burn sink address (irretrievable).
    address private constant DEAD =
        0x000000000000000000000000000000000000dEaD;

    /*/////////////////////////////////////////////////////////////////////////
                                    GLOBAL STATE
    /////////////////////////////////////////////////////////////////////////*/

    // packedGlobal0 layout (low -> high):
    // [ 0..111]  index     (uint112) // cumulative value owed per eligible SUN token (1e27-scaled)
    // [112..203] eligible  (uint92)  // total SUN held by eligible members (1e18-scaled)
    // [204..242] amLen     (uint39)  // activeMembers length
    // [243..255] bits0     (uint13)  // core bits [62..74]
    uint private packedGlobal0;

    // packedGlobal1 layout (low -> high):
    // [ 0..38 ]  amCursor  (uint39)
    // [ 39..77]  pmCursor  (uint39)
    // [ 78..116] erLen     (uint39)
    // [117..155] crLen     (uint39)
    // [156..194] pmLen     (uint39)
    // [195..213] rPhase    (uint19)
    // [214..232] mPhase    (uint19)
    // [233..254] bits1     (uint22) // core bits [75..96]
    // [255     ] unused    (uint1)
    uint private packedGlobal1;

    // packedGlobal2 layout (low -> high):
    // [ 0..96 ]  oath      (uint97) // total value owed (1e18-scaled)
    // [ 97..193] pool      (uint97) // total value ready for emission (1e18-scaled)
    // [194..255] coreLo62  (uint62) // low 62 bits of core
    //
    // core (uint97) is reconstructed as:
    // core = uint97(coreLo62) | (uint97(bits0) << 62) | (uint97(bits1) << 75)
    uint private packedGlobal2;

    /// @dev Ephemeral decoded view of global state.
    struct Global {
        // full packed slots
        uint    slot0;    // packed slot0
        uint    slot1;    // packed slot1
        uint    slot2;    // packed slot2
        bool    load0;    // true if slot0 was loaded
        bool    load1;    // true if slot1 was loaded
        bool    load2;    // true if slot2 was loaded

        // slot0
        uint112 index;    // cumulative nominal value-per-eligible-SUN (1e27-scaled)
        uint    eligible; // total SUN held by eligible members (storage uint92; 1e18-scaled)
        uint    amLen;    // activeMembers length (storage uint39)
        uint16  bits0;    // core bits [62..74] (storage uint13)

        // slot1
        uint    amCursor; // cursor in activeMembers (storage uint39)
        uint    pmCursor; // cursor in primeMembers (storage uint39)
        uint    erLen;    // edgeRays length (storage uint39)
        uint    crLen;    // coreRays length (storage uint39)
        uint    pmLen;    // primeMembers length (storage uint39)
        uint    rPhase;   // weighted ray-list phase: coreRays vs edgeRays (storage uint19; 2^18-scaled)
        uint    mPhase;   // weighted member-list phase: primeMembers vs activeMembers (storage uint19; 2^18-scaled)
        uint32  bits1;    // core bits [75..96] (storage uint22)

        // slot2
        uint    oath;     // approximate aggregate nominal claims outstanding (storage uint97; 1e18-scaled)
        uint    pool;     // aggregate value credited for distribution (storage uint97; 1e18-scaled)
        uint    core;     // pool value in core list (uint97 reconstructed from coreLo62 + bits0 + bits1)

        // transient helpers (NEVER written to storage)
        address priority; // priority ray
        uint    minCore;  // minimum value to be in core list (computed in memory; 1e18-scaled)
        int     penalty;  // work collected as penalty.
        address nextPair; // pair to be set as nextSwap;
    }

    /// @dev Ray that will be swapped next.
    address private nextSwap;

    /*/////////////////////////////////////////////////////////////////////////
                                RAY TOKEN STATE
    /////////////////////////////////////////////////////////////////////////*/

    /// slot0 layout (low -> high):
    /// [  0..159] token     (address)
    /// [160     ] moon0     (bool)     // stored in slot 0 and 1 for efficiency
    /// [161     ] swapLock  (bool)
    /// [162..225] lastAdd   (uint64)
    /// [226..255] unused    (uint30)
    ///
    /// slot1 layout (low -> high):
    /// [  0..72 ] avail73   (uint73)   // float73 encoded available
    /// [ 73..145] cred73    (uint73)   // float73 encoded credited
    /// [146..147] class2    (uint2)
    /// [148..237] pending90 (uint90)
    /// [238..249] work12    (int12)    // signed two's complement work
    /// [250..255] flags6    (uint6)    // unused|edge|core|eStale|cStale|moon0
    ///
    /// slot2 layout (low -> high):
    /// [  0..95 ] quote96     (uint96) // float96 encoded committed quote (1e38-scaled)
    /// [ 96..191] candQuote96 (uint96) // float96 encoded candidate intra-block high
    /// [192..255] lastBlock   (uint64)
    mapping(address => Ray) internal rays;

    /// @dev Per-pair accounting keyed by pair address.
    struct Ray {
        // packed storage
        uint    slot0;     // packed slot0
        uint    slot1;     // packed slot1
        uint    slot2;     // packed slot2
        bool    load0;     // true if slot0 is loaded
        bool    load1;     // true if slot1 is loaded
        bool    load2;     // true if slot2 is loaded

        // slot0
        address token;     // other token in the pair
        bool    moon0;     // true if MOON is token0 in the pair
        bool    swapLock;  // blocks swaps during untaxed mints/burns
        uint    lastAdd;   // block of last detected liquidity add

        // slot1
        uint    available; // token balance held by SUN available to this ray
        uint    credited;  // taxed portion of available ready for emission
        uint    pending;   // pending MOON earmarked for swap-back
        int     work;      // work credit (1 work = 1k gas)
        uint8   class;     // 0 unknown | 1 no code | 2 not pair | 3 V2 pair
        bool    edge;      // true if in edgeRays
        bool    core;      // true if in coreRays
        bool    eStale;    // true if should be removed from edge
        bool    cStale;    // true if should be removed from core

        // slot2
        uint    quote;     // decoded committed quote (1e38-scaled)
        uint    candQuote; // decoded candidate intra-block high quote (1e38-scaled)
        uint    lastBlock; // last block quote was updated

        // transient helpers (NEVER written to storage)
        address pair;      // pair address (ray identity)
        bool    justInit;  // true if Ray was initialized this transaction
        uint    value;     // MOON-denominated value
        uint    rt;        // pair token reserve
        uint    rm;        // pair MOON reserve
        uint    cursor;    // index during emitRay
        bool    corePick;  // true if current emission is from coreRays
        bool    edgePick;  // true if current emission is from edgeRays
    }

    /// @dev Recorded aggregate available balance by token; reconciled against SUN's live token balance when a ray is touched.
    mapping(address => uint) totalAvailable;

    /*/////////////////////////////////////////////////////////////////////////
                                SUN MEMBER STATE
    /////////////////////////////////////////////////////////////////////////*/

    // memberPacked layout (low -> high):
    // [  0..111] pastIndex   (uint112)
    // [112..207] uncollected (uint96)
    // [208..209] class       (uint2)
    // [210     ] active      (bool)
    // [211     ] prime       (bool)
    // [212..251] idxPlusOne  (uint40)
    // [252     ] redirect    (bool)
    // [253..255] unused      (uint3)
    mapping(address => uint) internal memberPacked;

    mapping(address => address) internal memberReceiver;

    /// @dev Member accounting snapshot.
    struct Member {
        // packed slot
        uint slot0;          // packed slot0

        // slot0
        uint112 pastIndex;   // index snapshot (1e27-scaled)
        uint    uncollected; // nominal claim used for proportional value distribution
        uint8   class;       // 0 unknown | 1 no code | 2 eligible | 3 ineligible
        bool    prime;       // true if member is in primeMembers list
        bool    active;      // true if member is in activeMembers list
        uint40  idxPlusOne;  // index in list plus 1
        bool    redirect;    // true if rewards should be redirected to 'receiver'

        // transient helpers (NEVER written to storage)
        address addr;        // member address (member identity)
        uint8   oldClass;    // class when member was loaded
        uint    balance;     // SUN balance (never stored)
        address receiver;    // Reward receiver if 'redirect' is true
    }

    /*/////////////////////////////////////////////////////////////////////////
                                MANUAL LIST BASES
    /////////////////////////////////////////////////////////////////////////*/

    uint private constant SWAPQUEUE     = 1;
    uint private constant EDGERAYS      = 2;
    uint private constant CORERAYS      = 3;
    uint private constant ACTIVEMEMBERS = 4;
    uint private constant PRIMEMEMBERS  = 5;

    /*/////////////////////////////////////////////////////////////////////////
                            PRECOMPUTED SELECTORS
    /////////////////////////////////////////////////////////////////////////*/

    bytes4 constant SEL_BALANCEOF     = bytes4(keccak256("balanceOf(address)"));    // ERC-20 style balanceOf(owner)
    bytes4 constant SEL_RAW_BALANCEOF = bytes4(keccak256("rawBalanceOf(address)")); // Moon raw balanceOf
    bytes4 constant SEL_TOKEN0        = bytes4(keccak256("token0()"));              // token0()
    bytes4 constant SEL_TOKEN1        = bytes4(keccak256("token1()"));              // token1()
    bytes4 constant SEL_GETRESERVES   = bytes4(keccak256("getReserves()"));         // V2: getReserves()
    bytes4 constant SEL_EXTSLOAD      = bytes4(keccak256("extsload(bytes32)"));     // V4 probe
    bytes4 constant SEL_SLOT0         = bytes4(keccak256("slot0()"));               // V3 probe
    bytes4 constant SEL_RECEIVER      = bytes4(keccak256("rewardsReceiver()"));     // Member rewards receiver

    /*/////////////////////////////////////////////////////////////////////////
                                    CONSTRUCTOR
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Deploys the Engine implementation and stores immutable contract references.
    /// @dev
    ///  - Sets immutable references to MOON, SUN, and the wrapped native gas token.
    ///  - Proxy storage initialization happens later in `_initialize`.
    /// @param moonAddress  MOON ERC-20 contract used for tax collection.
    /// @param sunAddress   SUN ERC-20 contract used for distributions.
    /// @param wbaseAddress Wrapped native gas token address (pairs with this token are untaxed).
    /// @param sunDeployer  Initial SUN holder; synthetic SUN seed is applied during `_initialize`.
    constructor(
        address  moonAddress,
        address  sunAddress,
        address  wbaseAddress,
        address  sunDeployer
    )
    {
        require(
            moonAddress  != address(0) &&
            sunAddress   != address(0) &&
            wbaseAddress != address(0) &&
            sunDeployer  != address(0)
        );

        // Immutable configuration
        moon  = moonAddress;
        sun   = sunAddress;
        wbase = wbaseAddress;
    }

    /*/////////////////////////////////////////////////////////////////////////
                                    INITIALIZATION
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev True once initialize has been executed successfully.
    bool private initialized;

    /// @notice Initializes engine state exactly once.
    /// @dev Seeds initial member exclusions and reflection index.
    function _initialize(address sunDeployer) internal {
        if (initialized) revert();
        require(sunDeployer != address(0), "Zero address");

        initialized = true;

        // Mark MOON, SUN, zero, DEAD as reflection-ineligible
        Member memory m;
        m.class = 3;
        m.addr = moon;       storeMember(m);
        m.addr = sun;        storeMember(m);
        m.addr = DEAD;       storeMember(m);
        m.addr = address(0); storeMember(m);

        // Seed reflection index via synthetic SUN transfer
        uint sunScaled = 1e27; // 1e9 SUN with 18 decimals
        _onSunTransfer(address(0), sunDeployer, sunScaled, 0, sunScaled);
    }

    /*/////////////////////////////////////////////////////////////////////////
                                    VIEW GETTERS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Returns the decoded global accounting state.
    /// @dev Can only be called by proxy.
    function getGlobalState()
        external
        view
        onlyProxy
        returns (
            uint    pool,
            uint    oath,
            uint    core,
            uint112 index,
            uint    eligible,
            uint    minCore,
            uint    activeMembersLength,
            uint    primeMembersLength,
            uint    coreRaysLength,
            uint    edgeRaysLength,
            uint    activeMembersCursor,
            uint    primeMembersCursor,
            address nextSwapRay
        )
    {
        Global memory g = loadGlobal();

        return (
            g.pool,
            g.oath,
            g.core,
            g.index,
            g.eligible,
            g.minCore,
            g.amLen,
            g.pmLen,
            g.crLen,
            g.erLen,
            g.amCursor,
            g.pmCursor,
            nextSwap
        );
    }

    /// @notice Returns the decoded stored state for a ray.
    /// @dev Can only be called by proxy.
    /// @param pair Ray pair address.
    function getRayState(address pair)
        external
        view
        onlyProxy
        returns (
            address token,
            uint8   class,
            bool    moon0,
            bool    swapLock,
            uint    available,
            uint    credited,
            uint    value,
            uint    pending,
            uint    quote,
            uint    candQuote,
            uint    lastBlock,
            int     work,
            bool    edge,
            bool    core
        )
    {
        Ray memory r;
        r.pair = pair;

        r = loadRay0(r);
        r = loadRay1(r);
        r = loadRay2(r);

        return (
            r.token,
            r.class,
            r.moon0,
            r.swapLock,
            r.available,
            r.credited,
            r.value,
            r.pending,
            r.quote,
            r.candQuote,
            r.lastBlock,
            r.work,
            r.edge,
            r.core
        );
    }

    /// @notice Returns stored state for a member and its current SUN balance.
    /// @dev Can only be called by proxy.
    /// @param member Member address.
    function getMemberState(address member)
        external
        view
        onlyProxy
        returns (
            address receiver,
            uint    currentBalance,
            bool    balanceOk,
            uint112 pastIndex,
            uint    uncollected,
            uint8   class,
            bool    active,
            bool    prime,
            uint40  idxPlusOne,
            bool    redirect
        )
    {
        Member memory m = loadMember(member);
        (balanceOk, currentBalance) = _safeBalanceOf(sun, member);

        return (
            m.receiver,
            currentBalance,
            balanceOk,
            m.pastIndex,
            m.uncollected,
            m.class,
            m.active,
            m.prime,
            m.idxPlusOne,
            m.redirect
        );
    }

    /*/////////////////////////////////////////////////////////////////////////
                     MOON TRANSFER ENGINE AND TAX DETECTION
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Core MOON transfer hook.
    /// @dev
    /// - Called on every MOON transfer.
    /// - Classifies each leg (from/to) via `_transferType`:
    ///     • Determines taxed swap vs untaxed flow.
    ///     • Returns per-leg tax rate.
    /// - Applies tax:
    ///     • `fromTax` and `toTax` are proportional to `amount`.
    ///     • Transfer can be double taxed if `from` and `to` are both pairs.
    /// - If either leg is taxed or initializes a ray:
    ///     • Credit work and possibly assign priority.
    ///     • Record pending MOON and admit rays to distribution.
    ///     • Run one swap-back and radiation cycle.
    ///     • Apply penalty work.
    /// - Otherwise:
    ///     • No maintenance, just persist ray state.
    function _onMoonTransfer(
        address from,
        address to,
        uint256 amount
    )
        internal
        returns (int fromTax, int toTax)
    {
        if (from == to) return (fromTax, toTax);

        Global memory g;

        // Classify: verified‑pair swap (taxed) vs everything else (untaxed)
        Ray memory f;
        Ray memory t;
        int fTax;
        int tTax;
        (g, f, fTax) = _transferType(g, amount, from, true);
        (g, t, tTax) = _transferType(g, amount, to, false);

        // From and to legs are examined separately and have separate work accounting.
        fromTax = int(amount) * fTax / 1e18;
        toTax   = int(amount) * tTax / 1e18;
        bool fWork = (fTax > 0 || f.justInit);
        bool tWork = (tTax > 0 || t.justInit);

        // perform system maintenance, only if transfer is part of taxed swap or ray was initialized.
        if (fWork || tWork) {
            if (!g.load0) g = loadGlobal(); // If not loaded in `_transferType()`.

            // credit ray with maintenance work and schedule for priority.
            if (fWork) (g, f) = _creditWork(g, f, (fWork && tWork));
            if (tWork) (g, t) = _creditWork(g, t, (fWork && tWork));

            // Record rays pending and admit them to distribution.
            if (fromTax > 0) {
                (g, f) = _recordPending(g, f, uint(fromTax));
                (g, f) = _admitRayDist(g, f);
            }
            if (toTax > 0) {
                (g, t) = _recordPending(g, t, uint(toTax));
                (g, t) = _admitRayDist(g, t);
            }
            storeRay(f);
            storeRay(t);

            g = _swapBack(g, f.pair, t.pair); // swapBack for another ray
            g = _radiate(g);                  // distribute rays to members

            // credit ray with maintenance penalty work.
            if (fWork) _creditPenalty(g, f.pair, (fWork && tWork));
            if (tWork) _creditPenalty(g, t.pair, (fWork && tWork));
        } else {
            storeRay(f);
            storeRay(t);
        }
        storeGlobal(g);

        return (fromTax, toTax);
    }

    /// @notice Distinguishes taxed swaps from untaxed LP mints, burns, and non-pair transfers.
    /// @dev
    /// Classification rules:
    /// - This function evaluates a single transfer leg against the given address.
    /// - If the address is not a class 3 pair, the transfer leg is untaxed.
    /// - If the address is a class 3 pair, we check the following:
    ///     • If either reserve is zero (bootstrap phase), the transfer is untaxed.
    ///     • Opposite-sign reserve deltas indicate a swap so the transfer is taxed.
    ///     • Same-sign reserve deltas enter a proportionality check to confirm deltas
    ///       are within 10% of each other. Outside the window the transfer is taxed.
    ///     • Inside the window, the action is treated as an untaxed LP mint or burn,
    ///       a Swap-lock is enabled to confirm the LP operation later in the transaction.
    ///
    /// Swap-lock confirmation:
    /// - While swapLock is enabled, MOON requires observed reserve deltas to remain
    ///   consistent with an LP-style operation during final balance checks.
    /// - Transactions deliberately crafted to trick tax detection will revert.
    /// - This revert path should never be reachable during legitimate swaps
    ///   or LP operations.
    /// - If an apparent LP add increases MOON balance by more than 2% of stored
    ///   MOON reserves, lastAdd is set to the current block to block same-block swap-back.
    ///
    /// Expected V2 execution model:
    /// - In any operation that moves tokens, the pair is expected to transfer tokens
    ///   first and read final balances at the end of the operation.
    /// - MOON performs its initial classification during the transfer phase and
    ///   confirms correctness when the pair later reads balances.
    /// - To distinguish LP mints and burns from swaps, MOON must be the second token
    ///   transferred, matching canonical V2 behavior where the lower-address token
    ///   is sent first and motivating MOON’s deliberately high address.
    /// @param amount MOON amount moved.
    /// @param addr   Pair candidate for this leg.
    /// @param sender true if addr is sending moon
    /// @return r     Ray context for taxed pair
    /// @return tax   Signed WAD tax rate: positive for taxed swap, zero for neutral,
    ///                negative for LP mint/burn classification.
    function _transferType(
        Global memory g,
        uint amount,
        address addr,
        bool sender
    )
        internal
        returns (
            Global memory,
            Ray memory,
            int
        )
    {
        Ray memory r;
        if (amount == 0) return (g, r, 0);                      // UNTAXED: 0 amount.

        r = _getMoonClass(addr, amount);
        if (r.class != 3) return (g, r, 0);                     // UNTAXED: Not ray.

        // All breaks will return with SWAP_TAX tax.
        do {
            // 1) Probe pair reserves.
            if (!r.load0) r = loadRay0(r);
            bool okR;
            (okR, r) = _probeReserves(r);
            if (!okR) break;                                    // TAXED: Broken pair

            if (r.rt == 0 && r.rm == 0) return (g, r, 0);       // UNTAXED: Bootstrap

            // 2) Reconcile swapLock.
            if (r.swapLock) (g, r) = _reconcileSwapLock(g, r, amount, sender);

            // 3) Get live moon balance.
                       uint bm  = _moonBalance(r.pair);
            (bool okB, uint bt) = _safeBalanceOf(r.token, r.pair);
            if (!okB) break;                                    // TAXED: Broken token

            // 4) Calculate pair's MOON balance after transfer.
            if (sender) bm = _satSub(bm, amount);
            else        bm = _satAdd(bm, amount);
            if (bm == r.rm) return (g, r, 0);                   // UNTAXED: No moon delta

            // 5) Easy swap test, tax if deltas are opposite sign.
            bool sameSign = _sameSign(bt, r.rt, bm, r.rm);
            if (!sameSign || bt == r.rt) break;                 // TAXED: Swap

            // 6) Confirm same sign deltas are proportional.
            uint dt = _diff(bt, r.rt);
            uint dm = _diff(bm, r.rm);
            unchecked {
                // Confirm  deltas are within 10% of eachother.
                uint b = dm * r.rt;
                uint a10 = dt * r.rm * 10;
                if (a10 > b * 11 || a10 < b * 9) break;         // TAXED: Unproportional
            }

            // 7) LP mint/burn → untaxed; engage lock until completion
            r.swapLock = true;
            if (_satSub(bm, r.rm) * 50 > r.rm) {
                r.lastAdd = uint64(block.number);
            }
            return (g, r, -int(SWAP_TAX));                      // UNTAXED: mint/burn
        } while (false);
        return (g, r, int(SWAP_TAX));
    }

    /// @notice Classifies an address for MOON pair semantics (tax eligibility).
    /// @dev
    /// - EOAs and non-V2-pair contracts are not taxed (class 1/2).
    /// - Class 3 (ray) requires a canonical V2 interface:
    ///     • Exposes `getReserves()`, `token0()`, and `token1()`.
    ///     • one and only one side is MOON.
    ///     • The other token must respond to `balanceOf()`
    ///     • Neither side may be SUN or wBase(wrapped native currency).
    /// - Once an address is classified as class 3:
    ///     • The paired token is cached.
    ///     • MOON directionality (token0 vs token1) is cached.
    /// - Classification is cached but not immutable:
    ///     • EOAs that later gain code are automatically reprobed.
    ///     • Sending exactly `REFRESH_WEI` forces a reprobe.
    ///     • Class 3 (ray) is sticky and cannot be changed.
    ///
    /// Compatibility notes for rays and paired tokens:
    /// - All probed balances and reserves are saturated to uint112; excess
    ///   precision is intentionally discarded.
    /// - Fee-on-transfer tokens are supported for distribution and can tax
    ///   up to (PAIR_BONUS - V2 pair fee) percent.
    /// - Rays that consume excessive gas remain eligible but receive reduced
    ///   distribution throughput via work accounting.
    /// - Tokens transferred or donated directly to the SUN are automatically
    ///   incorporated into the distribution system on reconciliation.
    function _getMoonClass(address addr, uint amount) internal returns (Ray memory r) {
        r.pair = addr;
        r = loadRay1(r);

        if ((r.class == 2) && amount != REFRESH_WEI || r.class == 3) return r;            // Quick exit for already probed contract.

        if (addr.code.length == 0) {r.class = 1; return r; }                              // class 1 (EOA) is never cached.

        r.class = 2;                                                                      // All failures below will force class 2.

        bool okR = _probeOk(addr, SEL_GETRESERVES, 0, 0, GAS_PROBE_LOW, 96);              // getReserves probe
        if (!okR) return r;
        (bool ok0, address t0) = _probeToken(addr, SEL_TOKEN0, GAS_PROBE_LOW);            // token0() probe
        if (!ok0 || t0 == sun || t0 == wbase ) return r;                                  // can't be sun or wbase
        (bool ok1, address t1) = _probeToken(addr, SEL_TOKEN1, GAS_PROBE_LOW);            // token1() probe
        if (!ok1 || t1 == sun || t1 == wbase || !(t0 == moon || t1 == moon)) return r;    // one of them must be MOON

        // Determine pair orientation.
        address token;
        bool    moon0;
        if      (t1 == moon) { token = t0; moon0 = false; }
        else if (t0 == moon) { token = t1; moon0 = true;  }

        (bool okB,) = _safeBalanceOf(token, addr); // token must return balance
        if (!okB || token == moon) return r;

        // Initialize ray state
        r.class    = 3;
        r.token    = token;
        r.moon0    = moon0;
        r.justInit = true;                         // tell _transfer() to run maintenance
        r.load0    = true;                         // tell storeRay() to save slot0
        emit RayInitialized(r.pair, r.token);
    }

    /// @notice Reconciles a ray's prior swap lock before current transfer classification.
    /// @dev
    /// - Taxes old MOON surplus still above reserves after current outgoing MOON is netted out.
    /// - Records collected tax as pending ray value.
    /// - Keeps `swapLock` active while an old MOON deficit remains unrepaired.
    function _reconcileSwapLock(
        Global memory g,
        Ray memory r,
        uint amount,
        bool sender
    )
        internal
        returns (Global memory, Ray memory)
    {
        uint bm = _moonBalance(r.pair);

        // Default: old lock is resolved.
        // Current transfer may set a new lock later in _transferType().
        r.swapLock = false;

        if (bm > r.rm) {
            // Old positive MOON surplus.
            uint retroBase = bm - r.rm;

            // Current outgoing MOON consumes old surplus.
            if (sender) retroBase = _satSub(retroBase, amount);

            if (retroBase != 0) {
                uint tax = retroBase * SWAP_TAX / 1e18;
                uint moveAmt = IMoon(moon).kernelTransfer(r.pair, moon, tax);
                if (!g.load0) g = loadGlobal();
                (g, r) = _recordPending(g, r, moveAmt);
            }

        } else if (bm < r.rm) {
            // Old negative MOON gap.
            uint deficit = r.rm - bm;

            // If current transfer does not repair the old deficit,
            // keep the proof obligation alive.
            if (sender || amount < deficit) {
                r.swapLock = true;
            }

            // If !sender && amount >= deficit:
            // old deficit is nominally repaired/crossed, so clear old lock.
            // Current classification handles the transfer.
        }

        return (g, r);
    }

    /// @notice Adds MOON to a ray’s pending balance.
    /// @dev
    /// - Increases `r.pending` by `amount`.
    /// - If pending and work thresholds are met, stages this ray in `g.nextPair` for future swapBack.
    /// Design note:
    ///     • The work credited during ray initialization and its first taxed swap
    ///       is typically sufficient to satisfy `WORK_SWAPBACK`
    function _recordPending(Global memory g, Ray memory r, uint amount)
        internal
        returns (Global memory, Ray memory)
    {
        r.pending += amount;
        if (r.pending >= DUST &&
            r.work    >= WORK_SWAPBACK
        ) { g.nextPair = r.pair; }
        return (g, r);
    }

    /// @notice Read‑only guard used by `balanceOf` to enforce swap‑lock semantics.
    /// @dev - While `swapLock` is set and pair is querying itself, verifies (balance - reserve)
    ///        are the same sign for each token. If not, returns false.
    ///      - Returns true when unlocked but returns false if probes fail. (fails closed)
    /// Note: - SwapLock is designed to revert any swaps disguised as liquidity mints/burns to avoid tax.
    ///       - This function ensures that the action is an authentic LP action and not a swap.
    ///       - This function should never return false outside of a malicious swap or broken paired token.
    /// @param sender The address requesting the MOON balance; only pair self-queries are lock-checked.
    /// @param pair The pair address to check.
    /// @return unlocked True if the balance query is allowed to proceed.
    function _isUnlocked(address sender, address pair) internal view returns (bool){
        if (sender != pair) return true; // not called by self

        Ray memory r;
        r.pair = pair;
        r = loadRay0(r);

        if (!r.swapLock) return true;    // lock not on

        // Stored reserves
        bool okR;
        (okR, r) = _probeReserves(r);
        if (!okR) return false;          // Fail closed

        // Live balances
        uint bm = _moonBalance(pair);
        (bool ok, uint bt) = _safeBalanceOf(r.token, pair);
        if (!ok) return false;           // Fail closed

        // Allow if signs match
        // Note: will return true if either delta is 0.
        if (_sameSign(bt, r.rt, bm, r.rm)) return true;
        return false;
    }

    /*/////////////////////////////////////////////////////////////////////////
                            WORK & PRIORITY ACCOUNTING
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Credits base work and grants emission priority to an overworked ray.
    /// @dev
    /// - Credits the ray with WORK_CREDIT for processing global maintenance.
    /// - If a listed ray reaches `WORK_SWAPBACK` + `PRIORITY_FEE`, `PRIORITY_FEE` is spent.
    /// - The ray pair is marked as priority and emitted first in this transaction.
    /// @param half true if two rays are credited, each is credited half work.
    function _creditWork(Global memory g, Ray memory r, bool half)
        internal pure
        returns (Global memory, Ray memory)
    {
        if (half) r.work += WORK_CREDIT/2;
        else      r.work += WORK_CREDIT;

        if (r.work > WORK_MAX) r.work = WORK_MAX;

        if (r.work >= WORK_SWAPBACK + PRIORITY_FEE &&
            ((r.core && !r.cStale) || (r.edge && !r.eStale)) &&
            g.priority == address(0))
        {
            r.work -= PRIORITY_FEE;
            g.priority = r.pair;
        }
        return (g, r);
    }

    /// @notice Credits a ray with work earned during the current transaction.
    /// @dev
    /// - Adds excess work accumulated in `g.penalty`, accumulated when other rays
    ///   exceeded their base gas targets in this tx. Saturated to WORK_CREDIT.
    /// @param half true if two rays are credited, each is credited half work.
    function _creditPenalty(Global memory g, address pair, bool half) internal {
        if (g.penalty > WORK_CREDIT) g.penalty = WORK_CREDIT;
        if (g.penalty > 0){
            Ray memory r;
            r.pair = pair;
            r = loadRay1(r);
            if (half) r.work += g.penalty/2;
            else      r.work += g.penalty;
            if (r.work > WORK_MAX) r.work = WORK_MAX;
            storeRay(r);
        }
    }

    /// @notice Charges gas penalties to a ray and attributes excess work to the caller.
    /// @dev
    /// - `fail` = full gas used when the call fails.
    /// - `over` = gas used above `limit`, regardless of success.
    /// - Ray pays both `fail + over` (over is effectively doubled during failure)
    /// - Global receives only `over` (failure gas is never credited).
    /// @param ok       Whether the bounded call succeeded; failure applies an extra penalty.
    /// @param startGas Gas remaining immediately before the call, used to measure gas spent.
    /// @param limit    Gas target for the call; usage above this is charged as overuse work.
    function _penaltyWork(
        Global memory g,
        Ray memory r,
        bool ok,
        uint startGas,
        uint limit
    ) internal view returns (Global memory, Ray memory) {
        uint used = startGas - gasleft();
        int  fail = ok ? int(0) : int((used + 999) / 1000);
        int  over = used > limit ? int((used - limit + 999) / 1000) : int(0);
        if (over != 0 || fail != 0) r.work -= (over + fail);
        if (over != 0) g.penalty += over;
        return (g, r);
    }

    /*/////////////////////////////////////////////////////////////////////////
                            SWAPBACK (QUEUE & SWAP)
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Attempts one swap-back for the ray currently staged in `nextSwap`.
    /// @dev
    /// - Pulls the current ray from `nextSwap`.
    /// - Seeds `nextSwap` from transient `g.nextPair`.
    /// - Makes at most one swap-back attempt for that ray.
    /// - Stops early if the ray is part of the current transaction or is not currently swappable.
    /// - Stops early if the ray had a liquidity add this block (same-block pair-bonus defense).
    /// - If attempted, swaps some pending MOON for the paired token through the ray pair.
    /// - Reconciles only after the staged ray passes skip, pending, liquidity, and lastAdd gates.
    /// @param skip1 Ray (pair) to exclude, typically the sender-side active ray in the current transfer.
    /// @param skip2 Ray (pair) to exclude when both sides of the current transfer are rays.
    function _swapBack(Global memory g, address skip1, address skip2)
        internal
        returns (Global memory)
    {
        bool ok;
        Ray memory r;
        address oldNext = nextSwap;
        r.pair = oldNext;

        if (oldNext != g.nextPair && g.nextPair != DEAD) nextSwap = g.nextPair; // seed nextSwap
        if (r.pair == skip1 || r.pair == skip2 || r.pair == DEAD) return g;     // skip active ray / no queued ray
        if (g.nextPair == DEAD) nextSwap = DEAD;

        r = loadRay1(r);
        if (r.pending < DUST) return g;                                         // require sufficient pending

        (ok, r) = _probeReserves(r);
        if (r.rm < DUST || r.rt == 0 || !ok) return g;                          // require sufficient liquidity

        r = loadRay0(r);
        if (r.lastAdd == uint64(block.number)) return g;                        // can't swapBack during block with mint

        r = loadRay2(r);
        uint maxInApp = _maxInForAppreciation(r);
        uint maxInFee = (r.rm * SWAP_TAX) / 1e18;
        uint amountIn = _min(uint(r.pending), maxInFee, maxInApp);

            // Attempt swapBack through a bounded self-call so failures become work penalties.
        if (amountIn > 0) {
            uint startGasCall = gasleft();
            (ok, ) = address(this).call{gas: GAS_CAP_SWAP}(
                abi.encodeWithSelector(
                    SEL_SWAP_WITH_PAIR,
                    r.pair,
                    r.swapLock,
                    r.moon0,
                    amountIn,
                    r.rm,
                    r.rt
                )
            );
            (g, r) = _penaltyWork(g, r, ok, startGasCall, GAS_BASE_SWAP);
            if (ok) {
                r.pending = _satSub(r.pending, amountIn);
                r.swapLock = false;        // now disabled from swapWithPair
            }
        }
        (g, r) = _reconcileRayValue(g, r); // does not require swap
        storeRay(r);
        return g;
    }

    /// @notice Computes the maximum MOON amount that can be swapped back
    ///         such that remaining `pending` covers appreciation tax.
    /// @dev
    /// - Let x be MOON input to the swap-back.
    /// - Let M = rm (MOON reserve), A = rt (token reserve).
    /// - Let r = available / A, v₀ = oldValue / M, p = pending / M.
    /// - Let k = APPRECIATION_TAX, b = PAIR_BONUS, α = 1 − b.
    ///
    /// - The tax constraint k·ΔV(x) ≤ p expands to:
    ///       A·x² + B·x + C ≤ 0
    ///   where:
    ///     • A = b + k·(r + α)
    ///     • B = b·p − 1 − k·(2r + α) + k·b·v₀
    ///     • C = p + k·v₀ − k·r
    ///
    /// - Solves for the largest admissible x using:
    ///       x = 2C / (√(B² + 4AC) − B)
    ///
    /// - All arithmetic is executed inside `unchecked` blocks and is constructed
    ///   to neither revert nor wrap across the full domain of inputs.
    /// - This function is an imperfect approximation whose objective is to maximize
    ///   swap-back efficiency while preserving the appreciation invariant.
    /// - Small numerical error is acceptable and cannot violate system safety.
    /// @return x Maximum safe MOON input for swap-back.
    function _maxInForAppreciation(Ray memory r) internal pure returns (uint x) {
        unchecked {
            uint P = _cap112(r.pending);
            uint M = _cap112(r.rm);
            uint A = _cap112(r.rt);
            if (P == 0 || M == 0 || A == 0) return 0;

            uint B  = _cap112(r.available);
            uint V0 = _cap112(_moonVal(r.credited, r.quote)); // old value

            uint WAD = 1e18;
            uint bW  = PAIR_BONUS;          // b
            uint kW  = APPRECIATION_TAX;    // k
            uint aW  = WAD - bW;            // α = 1 - b

            // Dimensionless ratios in WAD:
            uint pW  = P * WAD / M;         // p = P/M
            uint rW  = B * WAD / A;         // r = B/A
            uint v0W = V0 * WAD / M;        // v0 = V0/M

            // C = p + k*v0 - k*r  (WAD)
            uint krW   = kW * rW / WAD;
            uint kv0W  = kW * v0W / WAD;
            uint CWpos = pW + kv0W;
            if (CWpos <= krW) return 0;
            uint CW = CWpos - krW;

            // Bcoeff = b*p - 1 - k*(2r + α) + k*b*v0   (WAD, signed)
            uint BWpos = bW * pW / WAD + kv0W * bW / WAD;
            uint BWneg = WAD + (kW * (rW + rW + aW) / WAD);
            bool bNeg  = BWpos < BWneg;
            uint absBW = bNeg ? (BWneg - BWpos) : (BWpos - BWneg);

            // A2 = b + k*(r + α)  (WAD)
            uint A2W = bW + (kW * (rW + aW) / WAD);

            // We need sqrt(B^2 + 4*A2*C) in WAD.
            // Do it with power-of-two scaling so we never need 512-bit sqrt.
            uint bwBits   = absBW == 0 ? 0 : (_log2(absBW) + 1);
            uint termBits = (_log2(A2W) + 1) + (_log2(CW) + 1) + 2; // +2 for the *4

            uint s = bwBits > 128 ? (bwBits - 128) : 0;
            if (termBits > 256) {
                uint s2 = (termBits - 256 + 1) >> 1; // ceil((termBits-256)/2)
                if (s2 > s) s = s2;
            }

            // d1 = ceil(|B|/2^s)^2  >=  B^2 / 2^{2s}
            uint bwCeil = absBW;
            if (s != 0) {
                uint mask = (uint(1) << s) - 1;
                bwCeil = (absBW >> s) + ((absBW & mask) != 0 ? 1 : 0);
                if (bwCeil >> 128 != 0) bwCeil = type(uint128).max; // clamp 2^128 -> 2^128-1
            }
            uint d1 = bwCeil * bwCeil;

            // term2 = ceil(4*A2*C / 2^{2s})
            // For s>=1: 4/2^{2s} = 1/2^{2s-2}
            // For s==0: term2 = 4*A2*C
            uint term2;
            if (s == 0) {
                term2 = A2W * CW * 4;              // term2 = 4*A2W*CW
            } else {
                uint k = (s << 1) - 2;             // k in [0..256] when s in [1..129]
                term2 = _mulShiftUp(A2W, CW, k);   // term2 = ceil(A2W*CW / 2^k)
            }
            uint Q = _satAdd(d1, term2);

            // root = ceil_sqrt(Q) * 2^s  >= sqrt(B^2 + 4*A2*C)
            uint root = _isqrt(Q);
            if (root * root < Q) root++;
            root <<= s;

            // t = 2*C / (sqrt(D) - B)  (stable root form, avoids cancellation)
            uint denom = bNeg ? _satAdd(root, absBW) : _satSub(root, absBW);
            if (denom == 0) return 0;

            uint tW = CW * 2 * WAD / denom; // WAD-scaled t
            x = tW * M / WAD; // x = t*M
            if (x > P) x = P;
        }
    }

    /// @notice Internal router for MOON→token swaps on a verified V2 pair (self-call only).
    /// @dev
    /// - Transfers `amountIn` MOON into the pair, recomputes the *actual* input,
    ///   applies pair bonus, and routes output directly to SUN.
    /// - Reverts on zero output to avoid no-op swaps.
    /// - Allows pair fee to be <= PAIR_BONUS
    /// - Orientation-aware: if MOON is token0, outputs token1; otherwise token0.
    /// @param pair       V2 pair to swap against.
    /// @param swapLock   True if swapLock is enabled for pair.
    /// @param moon0      True if MOON is token0 in the pair.
    /// @param amountIn   Intended MOON input (ledger transfer).
    /// @param reserveIn  Pre-swap MOON reserve (orientation-correct).
    /// @param reserveOut Pre-swap other-token reserve (orientation-correct).
    function _swapWithPair(
        address pair,
        bool swapLock,
        bool moon0,
        uint amountIn,
        uint reserveIn,
        uint reserveOut
    ) internal {
        if (swapLock) { // safe for internal swapBack; pair reads final balances after swap.
            Ray memory r;
            r.pair = pair;
            r = loadRay0(r);
            r.swapLock = false;
            storeRay(r);
        }

        IMoon(moon).kernelTransfer(moon, pair, amountIn);
        uint realIn = _satSub(_moonBalance(pair), reserveIn); // actual input observed by the pair

        // V2 x*y = k
        uint numerator   = realIn * reserveOut;
        uint denominator = reserveIn + realIn;
        uint amountOut   = (denominator == 0) ? 0 : (numerator / denominator);

        // Apply pair bonus (leave a fraction in the pool).
        amountOut = amountOut * (1e18 - PAIR_BONUS) / 1e18;
        require(amountOut != 0);

        if (moon0) {
            IUniswapV2Pair(pair).swap(0, amountOut, sun, new bytes(0));
        } else {
            IUniswapV2Pair(pair).swap(amountOut, 0, sun, new bytes(0));
        }
    }

    /*//////////////////////////////////////////////////////////////////////////
                            VALUE & QUOTE TRACKING
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Revalues a ray and reconciles its effect on global accounting.
    /// @dev
    /// - Refreshes available reserves by reconciling SUN-held token balance deltas.
    /// - Updates the committed price quote using spot price and intra-block highs.
    /// - Computes old vs new MOON-denominated value and applies appreciation tax:
    ///     • Burns from pending MOON first.
    ///     • Withholds unpaid appreciation by reducing credited reserves.
    /// - Resolves the final ray value after tax and rounding normalization.
    /// - Applies signed value deltas to global pool, core, oath, and index:
    ///     • Positive deltas increase pool/core and advance oath and index.
    ///     • Negative deltas reduce pool/core only.
    /// - Evaluates and updates ray admission to core and edge distribution lists.
    // NOTE:
    //    Any mismatch between SUN’s real token balance and `totalAvailable` is
    //    absorbed by the first ray touched. Donations add value here; unexpected
    //    losses are charged to that ray to preserve global invariants.
    function _reconcileRayValue(
        Global memory g,
        Ray    memory r
    ) internal returns (Global memory, Ray memory){

        // Probe balance and reserves.
        (bool okb, uint balance) = _safeBalanceOf(r.token, sun);
        bool okr;
        (okr, r) = _probeReserves(r);

        if(!okb || !okr) return (g, r);

        // Update rays available tokens.
        // `totalAvailable[r.token]` will be updated in storeRay()
        uint totalAvail = totalAvailable[r.token];
        if (balance > totalAvail) {
            r.available = _satAdd(r.available, balance - totalAvail);
        } else {
            r.available = _satSub(r.available, totalAvail - balance);
        }

        // Update quote.
        r = _updateQuote(r);

        // Apply appreciation tax and calculate new value.
        uint oldVal = r.value;
        r = _applyAppTax(r); // updates r.value

        // apply value change to global accounting
        int delta = int(r.value) - int(oldVal);
        (g, r) = _updateGlobalValue(g, r, delta);

        // admit to distribution lists if thresholds are met.
        (g, r) = _admitRayDist(g, r);

        return (g, r);
    }

    /// @notice Applies a ray value delta to global accounting.
    /// @dev
    /// - Positive change increases `pool` and `core` if ray is in core,
    ///   and advances both `oath` and `index` when eligible supply exists.
    /// - Negative change reduces `pool` and `core` only.
    /// - No effect if the ray is not in edge or core distribution.
    function _updateGlobalValue(Global memory g, Ray memory r, int change)
        internal
        pure
        returns (Global memory, Ray memory)
    {
        if (r.core || r.edge) {
            if (change > 0) {
                uint inc = uint(change);
                g.pool = _satAdd(g.pool, inc);
                if (r.core) g.core = _satAdd(g.core, inc);

                if (g.eligible > 0) {
                    g.oath = _satAdd(g.oath, inc);
                    unchecked { g.index += _cap112(_mulDivDown(inc, 1e27, g.eligible)); } // index can wrap
                }
            } else {
                uint dec = uint(-change);
                g.pool = _satSub(g.pool, dec);
                if (r.core) g.core = _satSub(g.core, dec);
            }
        }
        return(g, r);
    }

    /// @notice Applies appreciation tax coverage for positive ray value changes.
    /// @dev
    /// - Recomputes the ray’s MOON-denominated value using the committed quote.
    /// - If fullVal increased vs `r.value`, a tax equal to
    ///       ceil(APPRECIATION_TAX × (fullVal − r.value))
    ///   must be paid to unlock the appreciation.
    /// - Payment is taken from `r.pending` first by burning MOON.
    /// - Any unpaid portion is withheld by reducing `r.credited`,
    ///   preventing untaxed value from entering the distribution pool.
    ///
    /// Note:
    /// Withheld (available but uncredited) reserves are not lost.
    /// They remain parked on the ray and can be rapidly unlocked later
    /// when additional pending MOON arrives, at an effective 1 / APPRECIATION_TAX ratio.
    function _applyAppTax(
        Ray memory r
    )
        internal
        returns (Ray memory)
    {
        if (r.quote == 0) return r;

        uint fullVal = _moonVal(r.available, r.quote);

        if (fullVal > r.value) {
            if (r.pending != 0){
                uint dVal;
                unchecked { dVal = fullVal - r.value; }                          // untaxed increase

                // Burn pending to unlock value.
                uint unpaidTax = _mulDivUp(dVal, APPRECIATION_TAX, 1e18);
                uint payNow    = _min(unpaidTax, r.pending, _moonBalance(moon)); // cap to what is available
                IMoon(moon).kernelTransfer(moon, DEAD, payNow);
                unchecked { r.pending -= payNow; }

                // Unlock new taxed value.
                uint unlockVal = _mulDivDown(payNow, 1e18, APPRECIATION_TAX);
                r.value        = _satAdd(r.value, unlockVal);
            }
            r.credited = _mulDivDown(r.value, 1e38, r.quote);                    // convert back into tokens
            if (r.credited > r.available) r.credited = r.available;              // saturate

        } else r.credited = r.available;

        r.credited = _roundFloat73(r.credited); // canonicalize so encode→decode doesn’t change value
        r.value    = _moonVal(r.credited, r.quote);
        return r;
    }

    /// @notice Updates a ray’s committed quote using spot and intra‑block highs.
    /// @dev
    /// Policy:
    /// - If spot > committed: lift immediately (quote=candidate=spot).
    /// - Same block: candidate tracks the running max.
    /// - New block: commit last block’s high to `quote`, seed `candidate` with current spot.
    /// Quotes are 1e38‑scaled MOON‑per‑token and encoded/decoded via Float96.
    /// Note: This system prevents artificial price dips that increase ray emission amount.
    function _updateQuote(Ray memory r)
        internal
        view
        returns (Ray memory)
    {
        // Need nonzero reserves to form a spot quote.
        if (r.rt == 0 || r.rm == 0) return r;

        uint spot = _roundFloat96(r.rm * 1e38 / r.rt);

        uint64 currentBlock = uint64(block.number);
        if (r.lastBlock != currentBlock) {
            // New block: commit last block’s intra-block high; seed candidate with current spot.
            r.quote     = r.candQuote;
            r.candQuote = spot;
            r.lastBlock = currentBlock;
        } else if (spot > r.candQuote) {
            // Same block: candidate tracks running max.
            r.candQuote = spot;
        }

        if (r.candQuote > r.quote) r.quote = r.candQuote; // always raise quote to candidate.

        return r;
    }


    /// @notice Manages ray admission into the core and edge distribution lists.
    /// @dev
    /// - Determines whether a ray should be admitted to `coreRays` or `edgeRays`
    ///   based on its value, work, and dynamic core threshold.
    /// - Core admission criteria:
    ///     • Not already core
    ///     • work greater then `WORK_DISTRIBUTION`
    ///     • value ≥ `g.minCore`
    /// - Edge admission criteria:
    ///     • Not already edge or core
    ///     • work greater then `WORK_DISTRIBUTION`
    ///     • value ≥ DUST
    /// - Admission effects:
    ///     • Adds the ray to the appropriate list
    ///     • Adds ray value to the global pool on first admission
    ///     • Adds value to `g.core` when admitted or promoted to core
    function _admitRayDist(Global memory g, Ray memory r) internal returns(Global memory, Ray memory){
        if (r.core) return (g, r);
        if (r.work < WORK_DISTRIBUTION) return (g, r); // not enough work.
        if (!r.load2) r = loadRay2(r);
        if (r.value < DUST) return (g, r);             // not enough value.

        if (r.value >= g.minCore) {
            r.core   = true;
            r.cStale = false;
            (g,) = _addToList(g, CORERAYS, r.pair);
            if (!r.edge) (g, r) = _updateGlobalValue(g, r, int(r.value)); // adds value to core and pool
            else g.core = _satAdd(g.core, r.value);
        } else if (!r.edge) {
            r.edge   = true;
            r.eStale = false;
            (g,) = _addToList(g, EDGERAYS, r.pair);
            if (!r.core) (g, r) = _updateGlobalValue(g, r, int(r.value)); // adds value to pool
        }
        return (g, r);
    }

    /*/////////////////////////////////////////////////////////////////////////
                    EMISSION ENGINE (RADIATION & PAYOUT)
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Distributes accumulated ray value to SUN members under a fixed gas budget.
    /// @dev
    /// - Selects between prime and active member lists by weighted round-robin,
    ///   with prime capped to at least half.
    /// - Uses `_emitRay` to settle accrued member value from core and edge rays.
    /// - Persists the last touched ray after the loop.
    /// - Stops when gas is exhausted or no eligible emissions remain.
    function _radiate(Global memory g)
        internal
        returns (Global memory)
    {
        Ray memory r;
        bool pickPrime;

        // Gas budget is decreased if SwapBack went over BASE.
        uint budgetGas = GAS_RADIATE;
        if (g.penalty != 0) budgetGas = _satSub(budgetGas, uint(g.penalty) * 1000);

        // Loop through members using weighted round-robin between prime and active lists.
        uint startGas  = gasleft();
        while (startGas - gasleft() < budgetGas &&
               g.pool >= DUST &&
               g.oath >= DUST)
            {
            Member memory m;

            // Picks prime or active based on size (prime is chosen at least half the time).
            (pickPrime, g.mPhase) = chooseList(g.pmLen, g.amLen, g.mPhase);
            if ((pickPrime || g.amLen == 0) && g.pmLen != 0){
                pickPrime = true;                                  // emit to prime member
                if (g.pmCursor >= g.pmLen) g.pmCursor = 0;
                m = loadMember(get(PRIMEMEMBERS, g.pmCursor));
            } else {
                if (g.amLen == 0) break;                           // no one to emit to
                pickPrime = false;                                 // emit to active member
                if (g.amCursor >= g.amLen) g.amCursor = 0;
                m = loadMember(get(ACTIVEMEMBERS, g.amCursor));
            }

            // Attempt emission to member.
            uint gasLeft = _satSub(gasleft() + budgetGas, startGas);
            (g, r) = _emitRay(g, r, m, gasLeft, 3);                // emit up to 3 rays to member
            unchecked {
                if (pickPrime) ++g.pmCursor;
                else           ++g.amCursor;
            }
        }

        if (r.pair != address(0)) storeRay(r);                     // Persist new credited reserve
        return g;
    }

    /// @notice Settles a SUN member’s accrued MOON-value by emitting value from rays.
    /// @dev
    /// - Accrues the member’s uncollected MOON-denominated claim via {_recordUncollected}.
    /// - Selects rays from the core or edge lists, weighted by pool/core value share.
    /// - Converts the member’s unscaled claim into a scaled payout using the live
    ///   coverage ratio (pool / oath).
    /// - Releases paired-token value from SUN to the member when possible.
    /// - On success, debits the member/oath claim; failed releases still remove ray value from pool.
    /// - Ray value is removed from the pool regardless of transfer success, ensuring
    ///   grief resistance and invariant preservation.
    /// - Failed releases or rays that fall below thresholds are lazily removed
    ///   from distribution lists.
    /// - Returns the last touched ray for caller-side persistence.
    /// @param r         Reusable ray cursor; persisted only when required.
    /// @param gasBudget Gas available for emission work.
    /// @param maxSteps  Maximum rays that may be emitted from in this call.
    function _emitRay(
        Global memory g,
        Ray    memory r,
        Member memory m,
        uint gasBudget,
        uint maxSteps
    ) internal returns (Global memory, Ray memory) {
        // Get members current claim.
        (, m.balance) = _safeBalanceOf(sun, m.addr);
        m = _recordUncollected(g, m);
        if (m.uncollected < DUST) return (g, r); // skip storeMember (gas savings)

        uint seed;  // entropy for ray selection
        uint steps; // settle against at most maxSteps rays.
        uint startGas = gasleft();
        uint stopGas;
        unchecked { stopGas = startGas > gasBudget ? (startGas - gasBudget) : 0; }
        while (
            steps < maxSteps &&
            m.uncollected >= DUST &&
            g.pool        >= DUST &&
            g.oath        >= DUST &&
            gasleft()     >  stopGas
        ) {
            unchecked { ++steps; }

            // Select ray.
            if (r.pair == address(0)) {
                if (g.priority != address(0)) { // priority ray is used first
                    r.pair = g.priority;
                } else {
                    if (seed == 0) { // generate seed from prevrandao + member entropy.
                        seed = uint(keccak256(abi.encodePacked(block.prevrandao, m.addr)));
                    } else { // cheap XOR shift to mix the seed between members.
                        unchecked {
                            seed ^= (seed << 13);
                            seed ^= (seed >> 7);
                            seed ^= (seed << 17);
                        }
                    }

                    // Pick core or edge proportional to value held (edge is chosen at least half the time).
                    bool pickEdge;
                    (pickEdge, g.rPhase) = chooseList(_satSub(g.pool, g.core), g.core, g.rPhase);
                    if ((pickEdge || g.crLen == 0) && g.erLen != 0) { // edge pick
                        r.edgePick = true;
                        r.cursor   = seed % g.erLen;
                        r.pair     = get(EDGERAYS, r.cursor);
                    } else {                                          // core pick
                        if(g.crLen == 0) break;
                        r.corePick = true;
                        r.cursor   = seed % g.crLen;
                        r.pair     = get(CORERAYS, r.cursor);

                    }
                }
                r = loadRay1(r);
                r = loadRay2(r);
            }

            // Attempt ray payout.
            bool ok;
            if (r.value >= DUST && r.work > WORK_REMOVAL && r.quote != 0) {
                if (!r.load0) r = loadRay0(r);

                // Scale the member’s unscaled claim by the live coverage ratio.
                uint scaled = _mulDivDown(m.uncollected, g.pool, g.oath);

                // Convert scaled MOON-value into token amount
                uint payAmt = _mulDivUp(scaled, 1e38, r.quote); // 1 wei floor
                if (payAmt > r.credited) payAmt = r.credited;

                // Reduce credited reserve.
                r.credited = _satSub(r.credited, payAmt);
                r.credited = _roundFloat73(r.credited);         // canonicalize so encode→decode doesn’t change value

                // Calculate exact change in value.
                uint oldVal = r.value;
                r.value     = _moonVal(r.credited, r.quote);
                uint payVal = oldVal - r.value;                 // realized value (no rounding)

                // Attempt release from SUN.
                if (payAmt > 0) {
                    uint startCallGas = gasleft();
                    (ok, ) = sun.call{gas: GAS_CAP_RELEASE}(
                        abi.encodeWithSelector(ISun.releaseToMember.selector, r.token, payAmt, m.receiver, true));
                    (g, r) = _penaltyWork(g, r, ok, startCallGas, GAS_BASE_RELEASE);
                } else ok = true;

                // If the transfer succeeds, debit unscaled claim, oath and available balance.
                if (ok) {
                    r.available = _satSub(r.available, payAmt);

                    uint payValUnscaled = _mulDivDown(payVal, uint(g.oath), uint(g.pool)); // scale back to original claim.
                    if (payValUnscaled > m.uncollected) payValUnscaled = m.uncollected;    // safety clamp
                    m.uncollected = m.uncollected - _cap96(payValUnscaled);
                    g.oath        = _satSub(g.oath, payValUnscaled);
                }
                (g, r) = _updateGlobalValue(g, r, -int(payVal)); // pool reduced regardless of transfer success
            }

            // Update rays inclusion in distribution lists, remove if cursor known.
            if (r.pair != address(0)) (g, r) = _removeRayDist(g, r, ok);
        }
        storeMember(m);
        return (g, r);
    }

    /// @notice Removes or updates a ray in the distribution lists.
    /// @dev
    /// - Evaluates whether the ray has become stale based on value and work.
    /// - All rays become stale if `belowEdge`, with core rays additionally
    ///   becoming stale if below half `g.minCore`
    /// - If stale and the cursor is known, removes the ray immediately.
    /// - Otherwise, marks the ray as stale for lazy cleanup.
    /// - If removed from core, adds back to edge if eligible.
    /// - If the ray exits both core and edge lists, its value is removed from the pool.
    /// - On removal, failure, or priority is stale, resets the ray cursor.
    /// @param ok true if release was successful.
    /// @return Updated global accounting and ray state.
    function _removeRayDist(Global memory g, Ray memory r, bool ok)
        internal
        returns (Global memory, Ray memory)
    {
        // determine if ray is below minimum distribution requirements.
        bool belowEdge = r.value < DUST || r.work <= WORK_REMOVAL;

        // should r be reset for new ray.
        bool reset = !ok || (g.priority != address(0) && (belowEdge));

        // update core staleness and remove if cursor known.
        if (r.core) r.cStale = belowEdge || (r.value * 2) < g.minCore;
        if (r.corePick && r.cStale) {
            (g, )    = _removeFromList(g, CORERAYS, r.cursor);
            g.core   = _satSub(g.core, r.value);
            r.core   = false;
            r.cStale = false;
            reset    = true;

            // If still above thresholds, add to edge.
            if (!r.edge && !belowEdge) {
                (g,)     = _addToList(g, EDGERAYS, r.pair);
                r.edge   = true;
                r.eStale = false;
            }
        }

        // update edge staleness and remove if cursor known.
        if (r.edge) r.eStale = belowEdge || r.core;
        if (r.edgePick && r.eStale) {
            (g, )    = _removeFromList(g, EDGERAYS, r.cursor);
            r.edge   = false;
            r.eStale = false;
            reset    = true;
        }

        // Remove from pool if in neither list.
        if (!r.core && !r.edge) {
            g.pool = _satSub(g.pool, r.value);
            r.credited = 0; // clear value
        }

        // Reset for new ray selection.
        if (reset) {
            g.priority = address(0);
            storeRay(r);
            Ray memory resetRay;
            r = resetRay;
        }
        return(g, r);
    }

    /*/////////////////////////////////////////////////////////////////////////
                        SUN ACCOUNTING AND ENTRY POINTS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Claims caller’s SUN emissions and opportunistically advances maintenance.
    /// @dev If eligible, settles the caller against up to 6 rays under a 300k gas budget.
    ///      Does not run swapBack or general radiation. Calling is optional; emissions accrue automatically.
    function _collect(address sender) internal {
        // Snapshot caller and their membership record.
        Member memory m = loadMember(sender);
        Global memory g = loadGlobal();

        // If the caller is an eligible SUN holder, settle their pending value.
        if (m.class == 1 || m.class == 2) {
            Ray memory r;
            (g, r) = _emitRay(g, r, m, 300_000, 6); // 300k gas and 6 ray limit
            if (r.pair != address(0)) storeRay(r);
            storeGlobal(g);
        }
    }

    /// @notice Hooks SUN transfers into MOON accounting.
    /// @dev
    /// - Called only by SUN via the internal router.
    /// - Accrues uncollected MOON-value for sender and receiver using pre-transfer balances.
    /// - Updates global eligible SUN supply when class boundaries are crossed.
    /// - Maintains active and prime member lists based on post-transfer balances.
    /// - Does not move tokens; may perform bounded static probes during member classification.
    function _onSunTransfer(
        address from,
        address to,
        uint fromBal,
        uint toBal,
        uint amount
    ) internal {
        if (amount == 0 || from == to) return;

        Global memory g = loadGlobalLight();
        Member memory f;
        Member memory t;

        (g, f) = _getSunClass(g, from, fromBal, amount);
        (g, t) = _getSunClass(g, to,   toBal,   amount);

        // Accrue `uncollected` using the pre-transfer balances at this index.
        f = _recordUncollected(g, f);
        t = _recordUncollected(g, t);

        // Update global count of eligible (class-1) SUN tokens used by index math.
        g = _updateEligible(g, f, t, amount);

        // Maintain member rotations: eligible balances above MIN_ACTIVE are listed.
        unchecked {f.balance -= amount; t.balance += amount;} // Calculate post transfer balances.
        (g, f, t) = _updateMemberLists(g, f, t);
        (g, t, f) = _updateMemberLists(g, t, f);

        storeMember(f);
        storeMember(t);
        storeGlobal(g);
        return;
    }

    /// @notice Classifies a SUN holder for reflection eligibility.
    /// @dev
    /// - EOAs and non-pair contracts are eligible for rewards (class 1/2).
    /// - Contracts with a valid `rewardsReceiver()` will have rewards redirected
    ///   to that address and are forced to class 2.
    /// - Addresses that positively match an AMM probe are ineligible (class 3):
    ///     • V2-style pairs via `getReserves()`
    ///     • V3-style pools via `slot0()`
    ///     • V4-style pool managers via `extsload(bytes32)`
    /// - If an address flips from eligible (class 1/2) to ineligible (class 3),
    ///   any uncollected value is recycled back into the system.
    /// - Classification is cached but not immutable:
    ///     • EOAs that later gain code are automatically reprobed.
    ///     • Sending exactly `REFRESH_WEI` forces a reprobe.
    ///     • Class 3 (ineligible) is sticky and cannot be changed.
    function _getSunClass(Global memory g, address addr, uint balance, uint amount)
        internal
        returns (Global memory, Member memory)
    {
        Member memory m = loadMember(addr);
        m.balance = balance;

        if ((m.class == 2 && amount != REFRESH_WEI) || m.class == 3) return (g, m); // quick exit for already probed contract
        if (addr.code.length == 0) { m.class = 1; return (g, m); }                  // EOAs eligible by default

        // Test for reward redirection receiver.
        (bool okRR, address rec) = _probeToken(addr, SEL_RECEIVER, GAS_PROBE_HIGH);
        m.redirect = okRR && rec != address(0) && rec != addr && rec != sun;        // receiver cannot be sun, address(0) or self
        if (!m.redirect) delete memberReceiver[addr];
        if (m.redirect || (okRR && rec == addr)) {
            if (m.redirect) memberReceiver[addr] = rec;                             // only use redirect slot if redirect is not self
            m.class = 2; // eligible non-pair contract
            return (g, m);
        }

        // Test for ineligibility.
        bool okV2 = _probeOk(addr, SEL_GETRESERVES, 0, 0, GAS_PROBE_HIGH, 96);      // 1) V2 probe: getReserves() returns 96 bytes
        if (!okV2) {
            bool okV3 = _probeOk(addr, SEL_SLOT0, 0, 0, GAS_PROBE_HIGH, 224);       // 2) V3 probe: slot0() returns 224 bytes (7 ABI words)
            if (!okV3) {
                bool okV4 = _probeOk(addr, SEL_EXTSLOAD, 0, 1, GAS_PROBE_HIGH, 32); // 3) V4 probe: extsload(bytes32) returns 32 bytes
                if (!okV4) {
                    m.class = 2; // eligible non-pair contract
                    return (g, m);
                }
            }
        }

        // Mark ineligible and remove from accounting.
        if (m.balance != 0 || m.uncollected != 0) {
            g.eligible = _satSub(g.eligible, m.balance);
            m          = _recordUncollected(g, m);

            Global memory gFull = loadGlobal();              // safe, only oath is mutated and nothing else. Caller doesn't touch slot2
            gFull.oath = _satSub(gFull.oath, m.uncollected); // Remove claim from oath
            storeGlobal(gFull);
            m.uncollected = 0;
        }
        m.class = 3; // ineligible pair contract.
        return (g, m);
    }

    /// @notice Accrues a member’s uncollected nominal claim based on the global index.
    /// @dev
    /// - Computes the incremental nominal claim accumulated since the member’s last
    ///   snapshot using:
    ///     (SUN balance × (globalIndex − pastIndex)) / 1e27.
    /// - Rounds to nearest (ties round down) to maintain deterministic aggregation.
    /// - Updates `pastIndex` to the current global index.
    /// - No-op if the member is ineligible, has zero balance, or the index is unchanged.
    function _recordUncollected(Global memory g, Member memory m)
        internal
        pure
        returns (Member memory)
    {
        if (m.oldClass != 0 && m.class != 3 && m.balance != 0 && g.index != m.pastIndex) {
            uint112 delta;
            unchecked { delta = g.index - m.pastIndex; } // index advances modulo uint112; unchecked math supports wrap

            // Newly earned unscaled value rounded to nearest
            uint num = uint(m.balance) * uint(delta);
            uint add = num / 1e27;
            uint rem = num - add * 1e27;
            if (rem * 2 > 1e27) {
                unchecked { add += 1; } // strictly above half -> round up
            }
            m.uncollected = _satAdd(m.uncollected, add);
        }
        m.pastIndex = g.index;
        return m;
    }

    /// @notice Adjusts global eligible SUN supply when tokens enter/exit ineligible class 3.
    /// @dev
    /// - Adjusts eligible supply when tokens move between eligible classes `!= 3` and ineligible class `3`.
    /// @param amount SUN amount moved (1e18‑scaled).
    function _updateEligible(
        Global memory g,
        Member memory f,
        Member memory t,
        uint amount
    ) internal pure returns(Global memory) {
        if (f.class == 3 && t.class != 3) {
            g.eligible = _satAdd(g.eligible, amount); // Enter eligible
        } else if (f.class != 3 && t.class == 3) {
            g.eligible = _satSub(g.eligible, amount); // Leave eligible
        }
        return g;
    }

    /// @notice Updates a member’s active/prime list membership.
    /// @dev
    /// - Prime and active membership are mutually exclusive.
    function _updateMemberLists(Global memory g, Member memory m, Member memory o)
        internal
        returns (Global memory, Member memory, Member memory)
    {
        bool primeOk  = m.class != 3 &&
                        m.balance >= MIN_PRIME;
        bool activeOk = m.class != 3 &&
                        (m.balance >= MIN_ACTIVE) &&
                        !primeOk;

        // Leave lists if no longer eligible
        if (m.prime && !primeOk) {
            (g, m, o) = _removeMember(g, m, o, true);
        } else if (m.active && !activeOk) {
            (g, m, o) = _removeMember(g, m, o, false);
        }

        // Enter lists if newly eligible
        if (!m.prime && primeOk) {
            uint idx;
            (g, idx) = _addToList(g, PRIMEMEMBERS, m.addr);
            m.idxPlusOne = uint40(idx + 1);
            m.prime = true;
        } else if (!m.active && activeOk) {
            uint idx;
            (g, idx) = _addToList(g, ACTIVEMEMBERS, m.addr);
            m.idxPlusOne = uint40(idx + 1);
            m.active = true;
        }

        return (g, m, o);
    }

    /// @notice Removes a member from the active or prime list.
    /// @dev
    /// - Uses swap-pop removal.
    /// - Repairs idxPlusOne for the moved member.
    /// - Clears active and prime flags and idxPlusOne.
    /// - Updates list length in Global.
    /// - Safe no-op if the member is not listed.
    function _removeMember(Global memory g, Member memory m, Member memory o, bool isPrime)
        internal
        returns (Global memory, Member memory, Member memory)
    {
        if (m.idxPlusOne == 0) return (g, m, o); // inconsistent list metadata; treat as already removed

        uint base = isPrime ? PRIMEMEMBERS : ACTIVEMEMBERS;
        uint len  = isPrime ? g.pmLen      : g.amLen;
        uint idx = uint(m.idxPlusOne) - 1;

        m.idxPlusOne = 0;
        if (idx >= len) {
            return (g, m, o); // inconsistent list metadata; treat as already removed
        }

        m.prime  = false;
        m.active = false;

        // Swap-pop remove; returns the element moved into `idx` (or 0 if idx==last)
        address moved;
        (g, moved) = _removeFromList(g, base, idx);

        // Fix the moved member’s idxPlusOne (it is now sitting at `idx`)
        if (moved != address(0)) {
            if (moved == o.addr) {
                o.idxPlusOne = uint40(idx + 1);
            } else {
                Member memory s= loadMember(moved);
                s.idxPlusOne = uint40(idx + 1);
                storeMember(s);
            }
        }
        return (g, m, o);
    }

    /*/////////////////////////////////////////////////////////////////////////
                                LIST HELPERS
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Deterministically selects A or B by weighted round-robin.
    /// @dev Uses a 2^18 scale so `acc` fits in 19 bits even after the temporary add.
    /// B is always capped to A so A gets at least 50% when both lists are nonempty.
    /// @param aLen Current A weight.
    /// @param bLen Current B weight.
    /// @param acc Prior 2^18-scaled accumulator.
    /// @return useA True for A, false for B.
    /// @return newAcc Updated accumulator.
    function chooseList(
        uint256 aLen,
        uint256 bLen,
        uint256 acc
    ) internal pure returns (bool useA, uint256 newAcc) {
        if (aLen == 0) return (false, acc);
        if (bLen == 0) return (true, acc);

        if (bLen > aLen) bLen = aLen; // enforce A gets as much as B

        unchecked {
            acc += (aLen << 18) / (aLen + bLen);
            if (acc >= 1 << 18) {
                acc -= 1 << 18;
                return (true, acc);
            }
        }
        return (false, acc);
    }

    /// @dev Element slot = (listId << 39) + index.
    ///      Each list owns a disjoint 2^39-sized storage window.
    function _listElemSlot(uint listId, uint index)
        internal
        pure
        returns (bytes32 slot)
    {
        unchecked {
            slot = bytes32((listId << 39) | index);
        }
    }

    /// @dev sload address stored in a slot (mask to 160 bits).
    function _sloadAddr(bytes32 slot) internal view returns (address a) {
        assembly ("memory-safe") {
            a := and(sload(slot), 0xffffffffffffffffffffffffffffffffffffffff)
        }
    }

    /// @dev sstore address into a slot.
    function _sstoreAddr(bytes32 slot, address a) internal {
        assembly ("memory-safe") {
            sstore(slot, a)
        }
    }

    /// @dev Read the authoritative logical length from Global for a given listId.
    function _getListLen(Global memory g, uint listId) internal pure returns (uint) {
        if (listId == EDGERAYS     ) return g.erLen;
        if (listId == CORERAYS     ) return g.crLen;
        if (listId == ACTIVEMEMBERS) return g.amLen;
        if (listId == PRIMEMEMBERS ) return g.pmLen;
        revert();
    }

    /// @dev Write the authoritative logical length into Global for a given listId.
    function _setListLen(Global memory g, uint listId, uint newLen) internal pure {
        if (listId == EDGERAYS     ) { g.erLen = newLen; return; }
        if (listId == CORERAYS     ) { g.crLen = newLen; return; }
        if (listId == ACTIVEMEMBERS) { g.amLen = newLen; return; }
        if (listId == PRIMEMEMBERS ) { g.pmLen = newLen; return; }
        revert();
    }

    function get(uint listId, uint index)
        internal
        view
        returns (address)
    {
        bytes32 slot = _listElemSlot(listId, index);
        return _sloadAddr(slot);
    }

    /// @notice Manual “push” into one of the active manual lists.
    /// @dev Stores `item` at element[len] and increments the Global length.
    /// @return index The index the item was inserted at.
    function _addToList(
        Global memory g,
        uint listId,
        address item
    )
        internal
        returns (Global memory, uint index)
    {
        uint len = _getListLen(g, listId);
        if (len == MASK_39) revert();

        bytes32 slot = _listElemSlot(listId, len);
        _sstoreAddr(slot, item);
        _setListLen(g, listId, len + 1);
        return (g, len);
    }

    /// @notice Manual “swap-pop remove” from one of the active manual lists.
    /// @dev Does NOT clear the old last slot (gas savings).
    /// @return moved The element that got moved into `i` (zero if i == last).
    function _removeFromList(
        Global memory g,
        uint listId,
        uint i
    )
        internal
        returns (Global memory, address moved)
    {
        uint len = _getListLen(g, listId);
        if(len == 0) return (g, address(0));

        uint last = len - 1;
        if(i > last) return (g, address(0));

        bytes32 slotI = _listElemSlot(listId, i);

        if (i != last) {
            bytes32 slotLast = _listElemSlot(listId, last);
            moved = _sloadAddr(slotLast);
            _sstoreAddr(slotI, moved);
        } else moved = address(0);

        _setListLen(g, listId, last);
        return (g, moved);
    }

    /*/////////////////////////////////////////////////////////////////////////
                                STORAGE HELPERS
    /////////////////////////////////////////////////////////////////////////*/

    uint private constant MASK_2  = (uint(1) << 2)  - 1;  // 2 bits
    uint private constant MASK_12 = (uint(1) << 12) - 1;  // 12 bits
    uint private constant MASK_13 = (uint(1) << 13) - 1;  // 13 bits
    uint private constant MASK_19 = (uint(1) << 19) - 1;  // 19 bits
    uint private constant MASK_22 = (uint(1) << 22) - 1;  // 22 bits
    uint private constant MASK_39 = (uint(1) << 39) - 1;  // 39 bits
    uint private constant MASK_40 = (uint(1) << 40) - 1;  // 40 bits
    uint private constant MASK_62 = (uint(1) << 62) - 1;  // 62 bits
    uint private constant MASK_73 = (uint(1) << 73) - 1;  // 73 bits
    uint private constant MASK_90 = (uint(1) << 90) - 1;  // 90 bits
    uint private constant MASK_92 = (uint(1) << 92) - 1;  // 92 bits
    uint private constant MASK_97 = (uint(1) << 97) - 1;  // 97 bits

    /// @notice Loads global slot0 + slot1 (light) if not already loaded.
    /// @dev Ray-style: respects g.load0/g.load1 and only SLOADs missing slots.
    function loadGlobalLight() internal view returns (Global memory g) {
        uint x = packedGlobal0;
        g.slot0 = x;
        g.load0 = true;

        // slot0
        g.index    = uint112(x);
        g.eligible = uint((x >> 112) & MASK_92);      // stored uint92
        g.amLen    = uint((x >> 204) & MASK_39);      // stored uint39
        g.bits0    = uint16(x >> 243);                // stored uint13

        uint y = packedGlobal1;
        g.slot1 = y;
        g.load1 = true;

        // slot1
        g.amCursor = uint(y & MASK_39);
        g.pmCursor = uint((y >> 39)  & MASK_39);
        g.erLen    = uint((y >> 78)  & MASK_39);
        g.crLen    = uint((y >> 117) & MASK_39);
        g.pmLen    = uint((y >> 156) & MASK_39);
        g.rPhase   = uint((y >> 195) & MASK_19);
        g.mPhase   = uint((y >> 214) & MASK_19);
        g.bits1    = uint32((y >> 233) & MASK_22);    // stored uint22

        return g;
    }

    /// @notice Loads slot0 + slot1 + slot2 (full) if not already loaded.
    /// @dev Calls loadGlobalLight first to ensure bits0/bits1 are available for core reconstruction.
    function loadGlobal() internal view returns (Global memory g) {
        g = loadGlobalLight();

        uint z = packedGlobal2;
        g.slot2 = z;
        g.load2 = true;

        g.oath = uint(z & MASK_97);
        g.pool = uint((z >> 97) & MASK_97);

        uint coreLo62 = uint(z >> 194) & MASK_62;

        // core = low62 | (bits0<<62) | (bits1<<75)
        g.core = coreLo62 | (uint(g.bits0) << 62) | (uint(g.bits1) << 75);

        // only refreshed once when globals are loaded, never stored.
        g.minCore = DUST;
        if (g.pool != 0 && g.core != 0) {
            // This curve was chosen deliberately because it ramps up fast and gives the
            // core threshold the desired shape.
            unchecked {
                uint base = (g.core * 1e10 / g.pool);          // (1e10-scaled)
                base = base > 1e10 ? 1e10 : base;              // saturate to 1
                uint ratio = base * base * base * base / 1e30;
                g.minCore += 0.1e10 * ratio * g.pool / 1e20;   // 0.1(core/pool)^4 * pool + DUST (1e18-scaled)
            }
        }

        g.nextPair = DEAD; // avoids writing address(0) to nextPair.

        return g;
    }

    /// @notice stores any loaded global slots back to storage, skipping unchanged words.
    function storeGlobal(Global memory g) internal {
        // If slot2 is loaded, core’s high bits live in slot0/slot1, so ensure light is loaded.
        if (g.load2 && (!g.load0 || !g.load1)) revert();

        // Canonicalize core bits into bits0/bits1 when slot2 is in play.
        if (g.load2) {
            uint core97 = _cap97(g.core);
            uint c = core97;

            g.bits0 = uint16((c >> 62) & MASK_13); // core[62..74]
            g.bits1 = uint32((c >> 75) & MASK_22); // core[75..96]
            g.core  = core97;                      // keep in-memory canonical too
        }

        // slot0 pack/write
        if (g.load0) {
            if (g.eligible > MASK_92) g.eligible = MASK_92;

            uint new0 =
                uint(uint112(g.index))         |
                (uint(g.eligible)      << 112) |
                (uint(_cap39(g.amLen)) << 204) |
                (uint(uint16(g.bits0) & uint16(MASK_13)) << 243);

            if (new0 != g.slot0) {
                packedGlobal0 = new0;
                g.slot0 = new0;
            }
        }

        // slot1 pack/write
        if (g.load1) {
            uint new1 =
                 uint(_cap39(g.amCursor))         |
                (uint(_cap39(g.pmCursor)) << 39)  |
                (uint(_cap39(g.erLen))    << 78)  |
                (uint(_cap39(g.crLen))    << 117) |
                (uint(_cap39(g.pmLen))    << 156) |
                (uint(g.rPhase & MASK_19) << 195) |
                (uint(g.mPhase & MASK_19) << 214) |
                (uint(uint32(g.bits1) & uint32(MASK_22)) << 233);

            if (new1 != g.slot1) {
                packedGlobal1 = new1;
                g.slot1 = new1;
            }
        }

        // slot2 pack/write
        if (g.load2) {
            uint oath97   = _cap97(g.oath);
            uint pool97   = _cap97(g.pool);
            uint core97   = _cap97(g.core);
            uint coreLo62 = core97 & MASK_62;

            uint new2 =
                 uint(oath97)           |
                (uint(pool97)   << 97)  |
                (uint(coreLo62) << 194);

            if (new2 != g.slot2) {
                packedGlobal2 = new2;
                g.slot2 = new2;
            }
        }
    }

    /// @notice Loads ray slot0 (token, moon0, swapLock, lastAdd) from storage into `r`.
    /// @dev Expects `r.pair` to be set. Sets `r.load0 = true` and copies the raw packed word into `r.slot0`.
    function loadRay0(Ray memory r)
        internal
        view
        returns (Ray memory)
    {
        uint x = rays[r.pair].slot0;

        r.slot0 = x;
        r.load0 = true;

        r.token    = address(uint160(x));
        r.moon0    = ((x >> 160) & 1) != 0;
        r.swapLock = ((x >> 161) & 1) != 0;
        r.lastAdd = uint64(x >> 162);

        return r;
    }

    /// @notice Loads ray slot1 (available, credited, class, pending, work, flags) from storage into `r`.
    /// @dev Expects `r.pair` to be set. Sets `r.load1 = true` and copies the raw packed word into `r.slot1`.
    ///      Uses float73 for `available` and `credited` and stores `r.class` in a contiguous 2-bit field.
    function loadRay1(Ray memory r)
        internal
        view
        returns (Ray memory)
    {
        uint x = rays[r.pair].slot1;

        r.slot1 = x;
        r.load1 = true;

        // [0..72] available73, [73..145] credited73
        uint avail73 =  x        & MASK_73;
        uint cred73  = (x >> 73) & MASK_73;

        r.available = _decodeFloat73(avail73);
        r.credited  = _decodeFloat73(cred73);

        // [146..147] class2 (contiguous)
        r.class = uint8((x >> 146) & MASK_2);

        // [148..237] pending90 (unsigned)
        r.pending = (x >> 148) & MASK_90;

        // [238..249] work12 (signed two's complement)
        uint wBits = (x >> 238) & MASK_12;
        r.work = (wBits & 0x800) != 0
            ? int16(int256(wBits) - 4096)
            : int16(int256(wBits));

        // [250..255] flags6
        uint f = x >> 250;
        r.edge   = (f & 2)  != 0;
        r.core   = (f & 4)  != 0;
        r.eStale = (f & 8)  != 0;
        r.cStale = (f & 16) != 0;
        r.moon0  = (f & 32) != 0;

        return r;
    }

    /// @notice Loads ray slot2 (quote96, candQuote96, lastBlock) from storage into `r`.
    /// @dev Expects `r.pair` to be set. Sets `r.load2 = true` and copies the raw packed word into `r.slot2`.
    function loadRay2(Ray memory r)
        internal
        view
        returns (Ray memory)
    {
        uint x = rays[r.pair].slot2;

        r.slot2 = x;
        r.load2 = true;

        uint96 q96  = uint96(x);
        uint96 cq96 = uint96(x >> 96);

        r.quote     = _decodeFloat96(q96);
        r.candQuote = _decodeFloat96(cq96);
        r.lastBlock = uint64(x >> 192);

        if (!r.load1) r = loadRay1(r);
        r.value         = _moonVal(r.credited, r.quote); // current credited value

        return r;
    }

    /// @notice Stores any loaded ray slots back to storage, writing only when the packed word changed.
    /// @dev
    /// - Updates `totalAvailable` for this ray's token based on `r.available` changes.
    /// @return r with new memory slots
    function storeRay(Ray memory r) internal returns (Ray memory){
        if (r.load0) {
            uint x0 =
                (uint(uint160(r.token))            ) |
                (uint(r.moon0    ? 1 : 0) << 160) |
                (uint(r.swapLock ? 1 : 0) << 161) |
                (uint(uint64(r.lastAdd))  << 162);

            if (x0 != r.slot0) {
                rays[r.pair].slot0 = x0;
                r.slot0 = x0;
            }
        }

        if (r.load1) {
            // Apply change in available to totalAvailable[r.token].
            uint oldAvail = _decodeFloat73(r.slot1 & MASK_73);
            uint newAvail = _roundFloat73(r.available);
            if (oldAvail != newAvail) {
                if (!r.load0) r = loadRay0(r); // get `r.token`
                if (newAvail > oldAvail) {
                    totalAvailable[r.token] = _satAdd(totalAvailable[r.token], newAvail - oldAvail);
                } else if (newAvail < oldAvail) {
                    totalAvailable[r.token] = _satSub(totalAvailable[r.token], oldAvail - newAvail);
                }
            }

            // Encode available and credited
            uint avail73 = _encodeFloat73(r.available) & MASK_73;
            uint cred73  = _encodeFloat73(r.credited)  & MASK_73;

            // Only cache class 2 or 3; class 1 or out-of-range -> 0 (forces reprobe semantics)
            uint c = (r.class == 3 || r.class == 2) ? uint(r.class) : 0;

            // Clamp pending into 90 bits
            uint p = r.pending > ((uint(1) << 90) - 1) ? ((uint(1) << 90) - 1) : r.pending;

            // Saturate work to signed 12-bit range [-2048, 2047]
            int wClamped = r.work;
            if (wClamped >  2047) wClamped =  2047;
            if (wClamped < -2048) wClamped = -2048;

            // Pack as signed 12-bit two's complement
            uint w = uint(int256(wClamped)) & MASK_12;

            // Pack flags (6 bits): unused, edge, core, eStale, cStale, moon0
            uint flags =
                (uint(r.edge   ? 1 : 0) << 1) |
                (uint(r.core   ? 1 : 0) << 2) |
                (uint(r.eStale ? 1 : 0) << 3) |
                (uint(r.cStale ? 1 : 0) << 4) |
                (uint(r.moon0  ? 1 : 0) << 5);

            // slot1 layout (low -> high):
            // [0..72]=avail73, [73..145]=cred73, [146..147]=class2,
            // [148..237]=pending90, [238..249]=work12, [250..255]=flags6
            uint x1 =
                (avail73      ) |
                (cred73 <<  73) |
                (c      << 146) |
                (p      << 148) |
                (w      << 238) |
                (flags  << 250);

            if (x1 != r.slot1) {
                rays[r.pair].slot1 = x1;
                r.slot1 = x1;
            }
        }

        if (r.load2) {
            uint x2 =
                (uint(_encodeFloat96(r.quote))           ) |
                (uint(_encodeFloat96(r.candQuote)) <<  96) |
                (uint(uint64(r.lastBlock))         << 192);

            if (x2 != r.slot2) {
                rays[r.pair].slot2 = x2;
                r.slot2 = x2;
            }
        }

        return r;
    }

    /// @notice Loads a member’s packed slot and decodes persisted fields.
    /// @dev Caches the raw packed word into `m.slot0` for write-skipping in {storeMember}.
    /// @dev Loads m.receiver if 'redirect' is true.
    function loadMember(address addr)
        internal
        view
        returns (Member memory m)
    {
        m.addr = addr;
        uint x = memberPacked[m.addr];
        m.slot0 = x;

        m.pastIndex   = uint112(x);
        m.uncollected = uint(uint96(x >> 112));
        m.class       = uint8((x >> 208) & MASK_2);
        m.active      = ((x >> 210) & 1) != 0;
        m.prime       = ((x >> 211) & 1) != 0;
        m.idxPlusOne  = uint40((x >> 212) & MASK_40);
        m.redirect    = ((x >> 252) & 1) != 0;

        // Set receiver
        if (m.redirect) m.receiver = memberReceiver[m.addr];
        else m.receiver = m.addr;

        m.oldClass = m.class;
        return m;
    }

    /// @notice Stores persisted fields for a SUN member, skipping the write if unchanged.
    /// @dev Packs to one word; compares against `m.slot0` (loaded by {loadMember}) before SSTORE.
    function storeMember(Member memory m) internal {
        // saturate uncollected to uint96 max
        uint u = uint(_cap96(m.uncollected));

        // class stored in 2 bits; out-of-range -> 0
        uint c = (m.class <= 3) ? uint(m.class) : 0;

        uint packed =
            uint(m.pastIndex)                 |
            (u                        << 112) |
            (c                        << 208) |
            (uint(m.active   ? 1 : 0) << 210) |
            (uint(m.prime    ? 1 : 0) << 211) |
            (uint(m.idxPlusOne)       << 212) |
            (uint(m.redirect ? 1 : 0) << 252);

        if (packed != m.slot0) {
            memberPacked[m.addr] = packed;
        }
    }

    /*/////////////////////////////////////////////////////////////////////////
                            SAFE STATIC PROBE SYSTEM
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Word-copy static probe.
    /// Caller invariant:
    /// - argWords is 0 or 1.
    /// - copyLen is 32 or 64.
    /// - copyLen <= minLen.
    /// This function does not bubble target failure.
    function _staticProbeWords(
        address target,
        bytes4 selector,
        uint arg0,
        uint argWords,
        uint gasCap,
        uint minLen,
        uint copyLen
    )
        internal
        view
        returns (bool ok, uint w0, uint w1)
    {
        assembly ("memory-safe") {
            mstore(0x00, selector)
            mstore(0x04, arg0)

            let inLen := add(0x04, shl(5, argWords))

            // Copy only the fixed words the caller needs.
            let success := staticcall(gasCap, target, 0x00, inLen, 0x00, copyLen)
            ok := and(success, iszero(lt(returndatasize(), minLen)))

            if ok {
                w0 := mload(0x00)

                if gt(copyLen, 0x20) {
                    w1 := mload(0x20)
                }
            }
        }
    }

    /// @dev Boolean-only static probe. No return-data copy.
    function _probeOk(
        address target,
        bytes4 selector,
        uint arg0,
        uint argWords,
        uint gasCap,
        uint minLen
    )
        internal
        view
        returns (bool ok)
    {
        assembly ("memory-safe") {
            mstore(0x00, selector)
            mstore(0x04, arg0)

            let inLen := add(0x04, shl(5, argWords))
            let success := staticcall(gasCap, target, 0x00, inLen, 0x00, 0x00)

            ok := and(success, iszero(lt(returndatasize(), minLen)))
        }
    }

    function _probeToken(address target, bytes4 selector, uint gasCap)
        internal
        view
        returns (bool ok, address token)
    {
        uint word;
        uint unused;

        (ok, word, unused) =
            _staticProbeWords(target, selector, 0, 0, gasCap, 32, 32);

        if (!ok) return (false, address(0));

        token = address(uint160(word));
    }

    function _probeReserves(Ray memory r)
        internal
        view
        returns (bool ok, Ray memory)
    {
        uint a;
        uint b;

        // Require full V2 return shape, copy only reserve0/reserve1.
        (ok, a, b) =
            _staticProbeWords(r.pair, SEL_GETRESERVES, 0, 0, GAS_PROBE_LOW, 96, 64);

        if (!ok) return (false, r);

        uint112 r0 = _cap112(a);
        uint112 r1 = _cap112(b);

        r.rt = r.moon0 ? r1 : r0;
        r.rm = r.moon0 ? r0 : r1;

        return (true, r);
    }

    function _safeBalanceOf(address token, address owner)
        internal
        view
        returns (bool ok, uint bal)
    {
        uint unused;

        (ok, bal, unused) = _staticProbeWords(
            token,
            SEL_BALANCEOF,
            uint(uint160(owner)),
            1,
            30_000,
            32,
            32
        );

        if (!ok) return (false, 0);

        bal = _cap112(bal);
    }

    /// @notice Reads a MOON balance via MOON’s raw balance getter.
    /// @dev Assumes MOON's internal rawBalanceOf cannot fail; staticcall success is intentionally ignored.
    function _moonBalance(address owner) internal view returns (uint256 bal) {
        address _moon = moon;
        bytes4 sel = SEL_RAW_BALANCEOF;

        assembly ("memory-safe") {
            mstore(0x00, sel)
            mstore(0x04, and(owner, 0xffffffffffffffffffffffffffffffffffffffff))

            pop(staticcall(gas(), _moon, 0x00, 0x24, 0x00, 0x20))

            bal := mload(0x00)
        }
    }

    /*/////////////////////////////////////////////////////////////////////////
                            INTERNAL MATH UTILITIES
    /////////////////////////////////////////////////////////////////////////*/

    /// @notice Saturating subtraction – clamps at zero instead of underflowing.
    function _satSub(uint a, uint b) internal pure returns (uint r) {
        unchecked { r = a > b ? a - b : 0; }
    }

    /// @notice Saturating addition – clamps at max uint instead of overflowing.
    function _satAdd(uint a, uint b) internal pure returns (uint r) {
        unchecked {
            r = a + b;
            if (r < a) {
                // Overflow happened, clamp to max
                r = type(uint).max;
            }
        }
    }

    /// @notice Caps a uint value down to uint112, clamping at the max if overflow.
    function _cap112(uint x) internal pure returns (uint112) {
        return x > type(uint112).max ? type(uint112).max : uint112(x);
    }

    /// @dev Saturating clamp to 39 bits.
    function _cap39(uint x) internal pure returns (uint) {
        return x > MASK_39 ? MASK_39 : x;
    }

    /// @notice Caps a uint value down to uint96, clamping at the max if overflow.
    function _cap96(uint x) internal pure returns (uint96) {
        return x > type(uint96).max ? type(uint96).max : uint96(x);
    }

    /// @dev Saturating clamp to 97 bits.
    function _cap97(uint x) internal pure returns (uint) {
        return x > MASK_97 ? MASK_97 : x;
    }

    /// @notice Compare signs of (a1 - a2) and (b1 - b2) without subtracting.
    /// @dev Zero is inclusive; only false when one delta >0 and the other <0
    function _sameSign(uint a1, uint a2, uint b1, uint b2)
        internal
        pure
        returns (bool)
    {
        return (a1 >= a2 && b1 >= b2) || (a1 <= a2 && b1 <= b2);
    }

    /// @notice Returns the absolute difference between a and b.
    function _diff(uint a, uint b) internal pure returns (uint) {
        return a > b ? a - b : b - a;
    }

    /// @notice Returns the minimum of 3 values.
    function _min(uint a, uint b, uint c) internal pure returns(uint) {
        uint d = a < b ? a : b;
        return      d < c ? d : c;
    }

    /// @notice Returns MOON-denominated value for `amount` tokens at quote `q` (1e38-scaled).
    /// @dev Saturates to uint112 on overflow.
    function _moonVal(uint a, uint b) internal pure returns (uint) {
        unchecked {
            uint q = b / 1e38;
            uint r = b - q * 1e38;

            // If a is too large, saturate
            // q+1 is safe since q <= b/1e38 and b fits uint
            uint maxA = type(uint).max / (q + 1);
            if (a > maxA) return type(uint).max;

            // Safe under the bound above
            return _cap112(a * q + (a * r) / 1e38);
        }
    }

    /// @notice Returns ceil(a * b / 2^k) using a full 512-bit product.
    /// @dev Handles overflow by computing the high and low product limbs, then right-shifting
    ///      the combined 512-bit value. Rounds up if any discarded low bits are nonzero.
    function _mulShiftUp(uint a, uint b, uint k) internal pure returns (uint) {
        if (a == 0 || b == 0) return 0;

        uint prod0;
        uint prod1;
        assembly ("memory-safe") {
            prod0 := mul(a, b)
            let mm := mulmod(a, b, not(0))
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        if (k == 0) {
            // When k == 0, the product must fit in 256 bits; otherwise saturate.
            if (prod1 != 0) return type(uint).max;
            return prod0;
        }

        // Caller bounds k to <= 256.
        uint shifted = (prod1 << (256 - k)) | (prod0 >> k);

        // ceil: if any of the low k bits of the full product are nonzero, add 1
        uint mask = (k == 256) ? type(uint).max : ((uint(1) << k) - 1);
        if ((prod0 & mask) != 0) {
            unchecked { shifted += 1; }
        }
        return shifted;
    }

    /// @notice Returns floor(a * b / d), treating multiply overflow or d == 0 as saturated.
    function _mulDivDown(
        uint a,
        uint b,
        uint d
    ) internal pure returns (uint) {
        unchecked {
            if (d == 0) return type(uint).max;
            uint p = a * b;
            if (a != 0 && p / a != b) {
                return type(uint).max / d; // overflow, saturate
            }
            return p / d;
        }
    }

    /// @notice Returns ceil(a * b / d), treating multiply overflow or d == 0 as saturated.
    function _mulDivUp(
        uint a,
        uint b,
        uint d
    ) internal pure returns (uint) {
        unchecked {
            if (d == 0) return type(uint).max;
            uint p = a * b;
            if (a != 0 && p / a != b) {
                return type(uint).max / d; // overflow, saturate
            }
            return p == 0 ? 0 : (p - 1) / d + 1;
        }
    }

    /// @dev Integer floor sqrt with a good first guess; 7 Newton steps are enough for 256-bit.
    function _isqrt(uint x) internal pure returns (uint y) {
        if (x == 0) return 0;
        uint z = uint(1) << (_log2(x) >> 1);
        unchecked {
            // 7 Newton iterations
            for (uint i; i < 7; ++i) { z = (z + x / z) >> 1; }
        }
        y = z;
        // Safe clamp: avoids overflow when y >= 2^128
        if (y > 0 && y > x / y) y--;
    }

    function _log2(uint x) internal pure returns (uint n) {
        unchecked {
            if (x >> 128 != 0) { x >>= 128; n += 128; }
            if (x >> 64  != 0) { x >>= 64;  n += 64;  }
            if (x >> 32  != 0) { x >>= 32;  n += 32;  }
            if (x >> 16  != 0) { x >>= 16;  n += 16;  }
            if (x >> 8   != 0) { x >>= 8;   n += 8;   }
            if (x >> 4   != 0) { x >>= 4;   n += 4;   }
            if (x >> 2   != 0) { x >>= 2;   n += 2;   }
            if (x >> 1   != 0) {            n += 1;   }
        }
    }

    /// @dev uint -> Float96 (88-bit mantissa + 8-bit exponent).
    ///      Layout: packed[95:88] = exponent, packed[87:0] = mantissa.
    ///      Decodes as (mantissa << exponent).
    function _encodeFloat96(uint x) internal pure returns (uint96 packed) {
        if (x == 0) return 0;

        unchecked {
            uint bitLen = _log2(x) + 1;

            // exponent = 0, mantissa = x (fits in 88 bits)
            if (bitLen <= 88) return uint96(x);

            // shift is exponent, mantissa is top 88 bits after shifting down
            uint shift    = bitLen - 88; // for uint256 range, max is 168
            uint mantissa = x >> shift;

            uint mantMask = (uint(1) << 88) - 1;
            return (uint96(uint8(shift)) << 88) | uint96(mantissa & mantMask);
        }
    }

    /// @dev uint -> Float73 (67-bit mantissa + 6-bit exponent).
    ///      Layout: packed[72:67] = exponent, packed[66:0] = mantissa.
    ///      Decodes as (mantissa << exponent).
    ///      Note: This encoder enforces a uint112 cap on `x` (and thus shift <= 45).
    function _encodeFloat73(uint x) internal pure returns (uint packed) {
        if (x == 0) return 0;
        x = _cap112(x); // enforce uint112 domain

        unchecked {
            uint bitLen = _log2(x) + 1;

            // exponent = 0, mantissa = x (fits in 67 bits)
            if (bitLen <= 67) return x;

            // shift is exponent, mantissa is top 67 bits after shifting down
            uint shift    = bitLen - 67; // for uint112 range, max is 45
            uint mantissa = x >> shift;

            uint mantMask = (uint(1) << 67) - 1;
            return (uint(uint8(shift)) << 67) | (mantissa & mantMask);
        }
    }

    /// @dev Float96 -> uint. Clamps exponent field if malformed.
    ///      Layout: packed[95:88] = exponent, packed[87:0] = mantissa.
    ///      Decodes as (mantissa << exponent).
    function _decodeFloat96(uint96 packed) internal pure returns (uint x) {
        uint8 exponent = uint8(uint(packed) >> 88);
        if (exponent > 168) exponent = 168;
        uint mantMask = (uint(1) << 88) - 1;
        uint mantissa = uint(packed) & mantMask;
        return mantissa << exponent;
    }

    /// @dev Float73 -> uint. Clamps exponent field if malformed.
    ///      Layout: packed[72:67] = exponent, packed[66:0] = mantissa.
    ///      Decodes as (mantissa << exponent).
    function _decodeFloat73(uint packed) internal pure returns (uint x) {
        uint8 exponent = uint8(packed >> 67);
        if (exponent > 45) exponent = 45;
        uint mantMask = (uint(1) << 67) - 1;
        uint mantissa = packed & mantMask;
        return mantissa << exponent;
    }

    /// @dev Rounds `x` exactly as `_decodeFloat96(_encodeFloat96(x))` would.
    ///      For Float96: mantissa is 88 bits, exponent is `bitLen - 88`.
    ///      This is equivalent to clearing the low `shift` bits (rounding down).
    function _roundFloat96(uint x) internal pure returns (uint) {
        if (x == 0) return 0;
        unchecked {
            uint bitLen = _log2(x) + 1;
            if (bitLen <= 88) return x;
            uint shift = bitLen - 88;     // in [1..168] for uint256 domain
            return (x >> shift) << shift; // round down exactly like encode+decode
        }
    }

    /// @dev Rounds `x` exactly as `_decodeFloat73(_encodeFloat73(x))` would.
    ///      For Float73: mantissa is 67 bits, exponent is `bitLen - 67`.
    ///      This is equivalent to clearing the low `shift` bits (rounding down).
    function _roundFloat73(uint x) internal pure returns (uint) {
        if (x == 0) return 0;
        x = _cap112(x);
        unchecked {
            uint bitLen = _log2(x) + 1;
            if (bitLen <= 67) return x;
            uint shift = bitLen - 67;     // normally <= 45 for the uint112-capped domain
            return (x >> shift) << shift; // round down exactly like encode+decode
        }
    }

    /*/////////////////////////////////////////////////////////////////////////
                                 FALLBACK ROUTER
    /////////////////////////////////////////////////////////////////////////*/

    /// @dev Conceptual ABI selectors used only for internal protocol routing.
    bytes4 private constant SEL_INITIALIZE =
        bytes4(keccak256("initialize(address)"));                                     // initialize(sunDeployer)

    bytes4 private constant SEL_SWAP_WITH_PAIR =
        bytes4(keccak256("swapWithPair(address,bool,bool,uint256,uint256,uint256)")); // swapWithPair(pair,swapLock,moon0,amountIn,reserveIn,reserveOut)

    bytes4 private constant SEL_IS_UNLOCKED =
        bytes4(keccak256("isUnlocked(address,address)"));                             // isUnlocked(sender,pair)

    bytes4 private constant SEL_ON_MOON_TRANSFER =
        bytes4(keccak256("onMoonTransfer(address,address,uint256)"));                 // onMoonTransfer(from,to,amount)

    bytes4 private constant SEL_COLLECT =
        bytes4(keccak256("collect(address)"));                                        // collect(sender)

    bytes4 private constant SEL_ON_SUN_TRANSFER =
        bytes4(keccak256("onSunTransfer(address,address,uint256,uint256,uint256)"));  // onSunTransfer(from,to,fromBal,toBal,amount)

    /// @notice Internal protocol router.
    /// @dev
    /// All protocol-internal entrypoints, MOON hooks, SUN hooks, and
    /// self-maintenance, are routed through this fallback and strictly
    /// gated by `msg.sender`.
    /// The compact selector router is an API-surface choice; the `msg.sender`
    /// checks are the actual internal access-control boundary.
    fallback() external {
        if (address(this) == SELF) revert ProxyOnly(); // proxy calls only

        bytes4 sel;
        assembly { sel := calldataload(0) }

        // initialize(address)
        if (sel == SEL_INITIALIZE) {
            address sunDeployer;
            assembly {
                sunDeployer := and(calldataload(4), 0xffffffffffffffffffffffffffffffffffffffff)
            }

            _initialize(sunDeployer);
            return;
        }

        // swapWithPair(address,bool,bool,uint256,uint256,uint256)
        if (sel == SEL_SWAP_WITH_PAIR) {
            if (msg.sender != address(this)) revert InternalOnly();

            address pair;
            bool    swapLock;
            bool    moon0;
            uint    amountIn;
            uint    reserveIn;
            uint    reserveOut;

            assembly {
                pair       := and(calldataload(4), 0xffffffffffffffffffffffffffffffffffffffff)
                swapLock   := iszero(iszero(calldataload(36)))
                moon0      := iszero(iszero(calldataload(68)))
                amountIn   := calldataload(100)
                reserveIn  := calldataload(132)
                reserveOut := calldataload(164)
            }

            _swapWithPair(pair, swapLock, moon0, amountIn, reserveIn, reserveOut);
            return;
        }

        // isUnlocked(address,address)
        if (sel == SEL_IS_UNLOCKED) {
            if (msg.sender != moon) revert InternalOnly();

            address sender;
            address pair;
            bool unlocked;

            assembly {
                sender := and(calldataload(4),  0xffffffffffffffffffffffffffffffffffffffff)
                pair   := and(calldataload(36), 0xffffffffffffffffffffffffffffffffffffffff)
            }

            unlocked = _isUnlocked(sender, pair);

            assembly {
                mstore(0x00, unlocked)
                return(0x00, 0x20)
            }
        }

        // onMoonTransfer(address,address,uint256)
        if (sel == SEL_ON_MOON_TRANSFER) {
            if (msg.sender != moon) revert InternalOnly();

            address from;
            address to;
            uint    amount;
            int     fromTax;
            int     toTax;

            assembly {
                from   := and(calldataload(4),  0xffffffffffffffffffffffffffffffffffffffff)
                to     := and(calldataload(36), 0xffffffffffffffffffffffffffffffffffffffff)
                amount := calldataload(68)
            }

            (fromTax, toTax) = _onMoonTransfer(from, to, amount);

            assembly {
                mstore(0x00, fromTax)
                mstore(0x20, toTax)
                return(0x00, 0x40)
            }
        }

        // collect(address)
        if (sel == SEL_COLLECT) {
            if (msg.sender != sun) revert InternalOnly();

            address sender;
            assembly {
                sender := and(calldataload(4), 0xffffffffffffffffffffffffffffffffffffffff)
            }

            _collect(sender);
            return;
        }

        // onSunTransfer(address,address,uint256,uint256,uint256)
        if (sel == SEL_ON_SUN_TRANSFER) {
            if (msg.sender != sun) revert InternalOnly();

            address from;
            address to;
            uint    fromBal;
            uint    toBal;
            uint    amount;

            assembly {
                from    := and(calldataload(4),  0xffffffffffffffffffffffffffffffffffffffff)
                to      := and(calldataload(36), 0xffffffffffffffffffffffffffffffffffffffff)
                fromBal := calldataload(68)
                toBal   := calldataload(100)
                amount  := calldataload(132)
            }

            _onSunTransfer(from, to, fromBal, toBal, amount);
            return;
        }
        revert();
    }
}
          

/DummERC20.sol

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

contract DummyERC20 {
    string public name;
    string public symbol;
    uint8 public constant decimals = 18;

    uint256 public immutable totalSupply;

    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    constructor(
        string memory name_,
        string memory symbol_,
        uint256 initialSupply,
        address recipient
    ) {
        name = name_;
        symbol = symbol_;
        totalSupply = initialSupply * 1e18;
        _balances[recipient] = totalSupply;
        emit Transfer(address(0), recipient, totalSupply);
    }

    // --- Views ------------------------------------------------------------

    function balanceOf(address account) external view returns (uint256) {
        return _balances[account];
    }

    function allowance(address owner, address spender)
        public
        view
        returns (uint256)
    {
        return _allowances[owner][spender];
    }

    // --- ERC20 Mutators ---------------------------------------------------

    function approve(address spender, uint256 amount)
        public
        returns (bool)
    {
        _allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function transfer(address to, uint256 amount)
        public
        returns (bool)
    {
        _transfer(msg.sender, to, amount);
        return true;
    }

    function transferFrom(address from, address to, uint256 amount)
        public
        returns (bool)
    {
        uint256 currentAllowance = _allowances[from][msg.sender];
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _allowances[from][msg.sender] = currentAllowance - amount;
            }
            emit Approval(from, msg.sender, _allowances[from][msg.sender]);
        }

        _transfer(from, to, amount);
        return true;
    }

    // --- Internal ---------------------------------------------------------

    function _transfer(address from, address to, uint256 amount) internal {
        uint256 gasStart = gasleft();

        require(_balances[from] >= amount, "ERC20: transfer amount exceeds balance");

        unchecked {
            _balances[from] -= amount;
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        // Burn additional gas until ~150k total gas has been consumed
        uint256 targetBurn = 150_000;

        // Prevent infinite loop if already exceeded
        if (gasStart > gasleft()) {
            while (gasStart - gasleft() < targetBurn) {
                // cheap operation that still consumes gas
                assembly {
                    pop(0)
                }
            }
        }
    }
}
          

Compiler Settings

{"viaIR":true,"remappings":[],"optimizer":{"runs":800,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"paris","compilationTarget":{"contracts/Moon.sol":"Moon"}}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_kernel","internalType":"address"},{"type":"address","name":"_sun","internalType":"address"}]},{"type":"error","name":"InternalOnly","inputs":[]},{"type":"error","name":"Reentrancy","inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"fallback","stateMutability":"nonpayable"},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nonces","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"permit","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]}]
              

Contract Creation Code

0x60c0346101535761154d90601f38839003908101601f19168201906001600160401b03821183831017610158578083916040958694855283398101031261015357610055602061004e8361016e565b920161016e565b906001600160a01b0380821615159081610147575b50156101125760805260a0526b033b2e3c9fd0803ce8000000600090808255338252600160205280838320558251908152817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203393a3308152600260205281600019912055516113ca9081610183823960805181818160b6015281816106f40152610dd7015260a051818181610bb601528181610c490152818161107501526111c80152f35b825162461bcd60e51b815260206004820152600e60248201526d2d32b937903232b632b3b0ba37b960911b6044820152606490fd5b9050821615153861006a565b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036101535756fe6080604052600436101561015d575b3461015857600080357fffffffff0000000000000000000000000000000000000000000000000000000016630481930d60e21b811461013a576358e276ad60e11b811461011c5760643614908161010b575b506100aa5760405162461bcd60e51b815260206004820152601660248201527f4d4f4f4e3a20696e76616c69642066616c6c6261636b000000000000000000006044820152606490fd5b6001600160a01b0390817f00000000000000000000000000000000000000000000000000000000000000001633036100f9576100f5602092604435908060243516906004351661109d565b8152f35b604051635f0d793360e11b8152600490fd5b6338850a9560e11b14905038610060565b6020826001600160a01b036004351681526002825260408120548152f35b6020826001600160a01b036004351681526001825260408120548152f35b600080fd5b60003560e01c806306fdde031461066c578063095ea7b31461093657806318160ddd1461091857806323b872dd146107fa578063313ce567146107de5780633644e515146107bb57806370a08231146106ab5780637ecebe001461067157806395d89b411461066c578063a9059cbb1461062b578063d505accf146102385763dd62ed3e0361000e5734610158576040366003190112610158576101ff610a56565b610207610a6c565b906001600160a01b038091166000526003602052604060002091166000526020526020604060002054604051908152f35b346101585760e036600319011261015857610251610a56565b610259610a6c565b9060ff60843516608435036101585760643542116105e6576001600160a01b038116156105a1576001600160a01b038116600052600460205260406000205491604051602081017f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981526001600160a01b03841660408301526001600160a01b038316606083015260443560808301528460a083015260643560c083015260c0825260e082019167ffffffffffffffff928181108482111761058b57604052519020610323610a82565b60405191602083019161190160f01b8352602284015260428301526042825260808201928284109084111761058b57826040528151902091608435917f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a060c4351161053f575050601b60ff6084351610610515575b60ff1690601b8214801561050b575b156104bb5760806000916020936040519182528482015260a435604082015260c435606082015282805260015afa156104af576001600160a01b0360005116801561046a576001600160a01b03831603610425576001610423936001600160a01b03841660005260046020520160406000205560443591610b30565b005b60405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606490fd5b60405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606490fd5b6040513d6000823e3d90fd5b60405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608490fd5b50601c82146103a7565b50601b60ff608435160160ff811115610398575b634e487b7160e01b600052601160045260246000fd5b9061756560f01b60e460849362461bcd60e51b8452602085820152602260a48201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60c48201520152fd5b634e487b7160e01b600052604160045260246000fd5b60405162461bcd60e51b815260206004820152601a60248201527f45524332305065726d69743a20696e76616c6964206f776e65720000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606490fd5b3461015857604036600319011261015857610659610647610a56565b61064f610b88565b6024359033610d48565b610661610c19565b602060405160018152f35b6109f2565b34610158576020366003190112610158576001600160a01b03610692610a56565b1660005260046020526020604060002054604051908152f35b346101585760208060031936011261015857806106c6610a56565b6001600160a01b039182916044604051809481936378b6719f60e11b835233600484015216958660248301527f0000000000000000000000000000000000000000000000000000000000000000165afa9081156104af57600091610785575b50156107405760005260018152604060002054604051908152f35b60405162461bcd60e51b815260048101839052601160248201527f4d4f4f4e3a205461782045766173696f6e0000000000000000000000000000006044820152606490fd5b90508281813d83116107b4575b61079c8183610994565b81010312610158575180151581036101585783610725565b503d610792565b346101585760003660031901126101585760206107d6610a82565b604051908152f35b3461015857600036600319011261015857602060405160128152f35b3461015857606036600319011261015857610813610a56565b61081b610a6c565b604435610826610b88565b6001600160a01b03831691826000526020936003855260406000203360005285526040600020546000198103610873575b506108629350610d48565b61086a610c19565b60405160018152f35b8381106108d357838103908560005260038752604060002033600052875281604060002055811161052957610862946040519182527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925873393a385610857565b60405162461bcd60e51b815260048101879052601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b34610158576000366003190112610158576020600054604051908152f35b3461015857604036600319011261015857610661610952610a56565b6024359033610b30565b6040810190811067ffffffffffffffff82111761058b57604052565b6060810190811067ffffffffffffffff82111761058b57604052565b90601f8019910116810190811067ffffffffffffffff82111761058b57604052565b67ffffffffffffffff811161058b57601f01601f191660200190565b604051906109df8261095c565b600482526326a7a7a760e11b6020830152565b3461015857600036600319011261015857610a0b6109d2565b6040805190602080835283519182602085015260005b838110610a435784604081866000838284010152601f80199101168101030190f35b8581018301518582018301528201610a21565b600435906001600160a01b038216820361015857565b602435906001600160a01b038216820361015857565b610a8a6109d2565b60208151910120603160f81b6020604051610aa48161095c565b60018152015260405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c0810181811067ffffffffffffffff82111761058b5760405251902090565b909160207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925916001600160a01b03809416938460005260038352604060002095169485600052825280604060002055604051908152a3565b6000806040516020810190635da7ddcd60e01b82526001602482015260248152610bb181610978565b5190827f00000000000000000000000000000000000000000000000000000000000000005af13d15610c11573d90610be8826109b6565b91610bf66040519384610994565b82523d6000602084013e5b15610c095750565b602081519101fd5b606090610c01565b6040516020810190635da7ddcd60e01b825260008092828260248195015260248152610c4481610978565b5190827f00000000000000000000000000000000000000000000000000000000000000005af13d15610c9a573d610c7a816109b6565b90610c886040519283610994565b8152809260203d92013e15610c095750565b60609150610c01565b15610caa57565b60405162461bcd60e51b815260206004820152601160248201527f496e76616c696420726563697069656e740000000000000000000000000000006044820152606490fd5b15610cf657565b60405162461bcd60e51b815260206004820152601b60248201527f45524332303a2042616c616e636520696e73756666696369656e7400000000006044820152606490fd5b9190820180921161052957565b91906001600160a01b03908181169182151580611071575b80611067575b610d6f90610ca3565b636a359f59421015611022576000926000916040918251946020948587019363d1bd481960e01b85528a16968760248201528360448201528960648201526064815260a0810181811067ffffffffffffffff82111761100e57865281805a925162124f8097827f00000000000000000000000000000000000000000000000000000000000000008af1943d8781148716600114610ffb5788148616610feb575b85159182610fe1575b5050610158578660005260018652846000205497610e388a8a1015610cef565b8a600095600014610f9957918a610e57610e5d9382610e6397966111f1565b936111f1565b90610d3b565b965b878103868661dead8514998a600014610f3b57505050610e90925083600014610f3457505b886112ed565b85610e9f575b50505050505050565b15610ecc5750505015610ebc575b50505b38808080808080610e96565b610ec5916112ed565b3880610ead565b90919394507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92610f1d575b306000526001825280600020610f0f868254610d3b565b9055519384523093a3610eb0565b836000526001825280600020858154039055610ef8565b9050610e8a565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9391929487600014610f93575081905b856000526001845203876000205584600052866000208181540190558651908152a3610e90565b90610f6c565b50505050959066d529ae9e86000090818302918383041483151715610fcd5750670de0b6b3a7640000900495600191610e65565b634e487b7160e01b81526011600452602490fd5b1090503880610e18565b9850866000803e60005198610e0f565b50919098508581803e5197865191610e0f565b634e487b7160e01b83526041600452602483fd5b60405162461bcd60e51b815260206004820152600760248201527f45787069726564000000000000000000000000000000000000000000000000006044820152606490fd5b5030831415610d66565b50807f000000000000000000000000000000000000000000000000000000000000000016831415610d60565b9092916001600160a01b0380941691821515806111c5575b6110be90610ca3565b819481169160009083825260016020526040928383205491308603611150575b506110eb88831015610cef565b61dead861461113a5750918082887fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9587602096526001865203828220558681522087815401905551868152a3565b9250505061114d925084939491506112ed565b90565b6002602052848420548091116111bd575b5066d529ae9e8600008083029083820414831517156111a957670de0b6b3a764000090048089116111a1575b5084835260026020528284812055386110de565b97503861118d565b634e487b7160e01b84526011600452602484fd5b975038611161565b507f000000000000000000000000000000000000000000000000000000000000000085168314156110b5565b929160009060009281156112e557666a94d74f430000908181029181830414901517156111a957670de0b6b3a76400009004918382131561126257506001600160a01b0391908181111561125b57505b931681526002602052604081208054611258575050565b55565b9050611241565b9492919060018101600181128483129080158216911516176111a957600160ff1b146112d157820390816000198101116112d157916001600160a01b036040926112c594808211156000146112c95750945b168152600260205220918254610d3b565b9055565b9050946112b4565b634e487b7160e01b83526011600452602483fd5b509193505050565b6001600160a01b03166000918183526001602052604083205481811061134f57817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9260209285875260018452036040862055808554038555604051908152a3565b60405162461bcd60e51b815260206004820152601b60248201527f45524332303a206275726e20657863656564732062616c616e636500000000006044820152606490fdfea264697066735822122073611accccc5a4a5f27272a0d92d11e7cf63b85f747dfe9f1d586d61c54c2f5a64736f6c63430008180033000000000000000000000000b915c09b1f28a6711aa42c54b5044a16896e55090000000000000000000000000000b08b88fdee1a65e52f942aa9612a093a0000

Deployed ByteCode

0x6080604052600436101561015d575b3461015857600080357fffffffff0000000000000000000000000000000000000000000000000000000016630481930d60e21b811461013a576358e276ad60e11b811461011c5760643614908161010b575b506100aa5760405162461bcd60e51b815260206004820152601660248201527f4d4f4f4e3a20696e76616c69642066616c6c6261636b000000000000000000006044820152606490fd5b6001600160a01b0390817f000000000000000000000000b915c09b1f28a6711aa42c54b5044a16896e55091633036100f9576100f5602092604435908060243516906004351661109d565b8152f35b604051635f0d793360e11b8152600490fd5b6338850a9560e11b14905038610060565b6020826001600160a01b036004351681526002825260408120548152f35b6020826001600160a01b036004351681526001825260408120548152f35b600080fd5b60003560e01c806306fdde031461066c578063095ea7b31461093657806318160ddd1461091857806323b872dd146107fa578063313ce567146107de5780633644e515146107bb57806370a08231146106ab5780637ecebe001461067157806395d89b411461066c578063a9059cbb1461062b578063d505accf146102385763dd62ed3e0361000e5734610158576040366003190112610158576101ff610a56565b610207610a6c565b906001600160a01b038091166000526003602052604060002091166000526020526020604060002054604051908152f35b346101585760e036600319011261015857610251610a56565b610259610a6c565b9060ff60843516608435036101585760643542116105e6576001600160a01b038116156105a1576001600160a01b038116600052600460205260406000205491604051602081017f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981526001600160a01b03841660408301526001600160a01b038316606083015260443560808301528460a083015260643560c083015260c0825260e082019167ffffffffffffffff928181108482111761058b57604052519020610323610a82565b60405191602083019161190160f01b8352602284015260428301526042825260808201928284109084111761058b57826040528151902091608435917f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a060c4351161053f575050601b60ff6084351610610515575b60ff1690601b8214801561050b575b156104bb5760806000916020936040519182528482015260a435604082015260c435606082015282805260015afa156104af576001600160a01b0360005116801561046a576001600160a01b03831603610425576001610423936001600160a01b03841660005260046020520160406000205560443591610b30565b005b60405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606490fd5b60405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606490fd5b6040513d6000823e3d90fd5b60405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608490fd5b50601c82146103a7565b50601b60ff608435160160ff811115610398575b634e487b7160e01b600052601160045260246000fd5b9061756560f01b60e460849362461bcd60e51b8452602085820152602260a48201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60c48201520152fd5b634e487b7160e01b600052604160045260246000fd5b60405162461bcd60e51b815260206004820152601a60248201527f45524332305065726d69743a20696e76616c6964206f776e65720000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606490fd5b3461015857604036600319011261015857610659610647610a56565b61064f610b88565b6024359033610d48565b610661610c19565b602060405160018152f35b6109f2565b34610158576020366003190112610158576001600160a01b03610692610a56565b1660005260046020526020604060002054604051908152f35b346101585760208060031936011261015857806106c6610a56565b6001600160a01b039182916044604051809481936378b6719f60e11b835233600484015216958660248301527f000000000000000000000000b915c09b1f28a6711aa42c54b5044a16896e5509165afa9081156104af57600091610785575b50156107405760005260018152604060002054604051908152f35b60405162461bcd60e51b815260048101839052601160248201527f4d4f4f4e3a205461782045766173696f6e0000000000000000000000000000006044820152606490fd5b90508281813d83116107b4575b61079c8183610994565b81010312610158575180151581036101585783610725565b503d610792565b346101585760003660031901126101585760206107d6610a82565b604051908152f35b3461015857600036600319011261015857602060405160128152f35b3461015857606036600319011261015857610813610a56565b61081b610a6c565b604435610826610b88565b6001600160a01b03831691826000526020936003855260406000203360005285526040600020546000198103610873575b506108629350610d48565b61086a610c19565b60405160018152f35b8381106108d357838103908560005260038752604060002033600052875281604060002055811161052957610862946040519182527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925873393a385610857565b60405162461bcd60e51b815260048101879052601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b34610158576000366003190112610158576020600054604051908152f35b3461015857604036600319011261015857610661610952610a56565b6024359033610b30565b6040810190811067ffffffffffffffff82111761058b57604052565b6060810190811067ffffffffffffffff82111761058b57604052565b90601f8019910116810190811067ffffffffffffffff82111761058b57604052565b67ffffffffffffffff811161058b57601f01601f191660200190565b604051906109df8261095c565b600482526326a7a7a760e11b6020830152565b3461015857600036600319011261015857610a0b6109d2565b6040805190602080835283519182602085015260005b838110610a435784604081866000838284010152601f80199101168101030190f35b8581018301518582018301528201610a21565b600435906001600160a01b038216820361015857565b602435906001600160a01b038216820361015857565b610a8a6109d2565b60208151910120603160f81b6020604051610aa48161095c565b60018152015260405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c0810181811067ffffffffffffffff82111761058b5760405251902090565b909160207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925916001600160a01b03809416938460005260038352604060002095169485600052825280604060002055604051908152a3565b6000806040516020810190635da7ddcd60e01b82526001602482015260248152610bb181610978565b5190827f0000000000000000000000000000b08b88fdee1a65e52f942aa9612a093a00005af13d15610c11573d90610be8826109b6565b91610bf66040519384610994565b82523d6000602084013e5b15610c095750565b602081519101fd5b606090610c01565b6040516020810190635da7ddcd60e01b825260008092828260248195015260248152610c4481610978565b5190827f0000000000000000000000000000b08b88fdee1a65e52f942aa9612a093a00005af13d15610c9a573d610c7a816109b6565b90610c886040519283610994565b8152809260203d92013e15610c095750565b60609150610c01565b15610caa57565b60405162461bcd60e51b815260206004820152601160248201527f496e76616c696420726563697069656e740000000000000000000000000000006044820152606490fd5b15610cf657565b60405162461bcd60e51b815260206004820152601b60248201527f45524332303a2042616c616e636520696e73756666696369656e7400000000006044820152606490fd5b9190820180921161052957565b91906001600160a01b03908181169182151580611071575b80611067575b610d6f90610ca3565b636a359f59421015611022576000926000916040918251946020948587019363d1bd481960e01b85528a16968760248201528360448201528960648201526064815260a0810181811067ffffffffffffffff82111761100e57865281805a925162124f8097827f000000000000000000000000b915c09b1f28a6711aa42c54b5044a16896e55098af1943d8781148716600114610ffb5788148616610feb575b85159182610fe1575b5050610158578660005260018652846000205497610e388a8a1015610cef565b8a600095600014610f9957918a610e57610e5d9382610e6397966111f1565b936111f1565b90610d3b565b965b878103868661dead8514998a600014610f3b57505050610e90925083600014610f3457505b886112ed565b85610e9f575b50505050505050565b15610ecc5750505015610ebc575b50505b38808080808080610e96565b610ec5916112ed565b3880610ead565b90919394507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92610f1d575b306000526001825280600020610f0f868254610d3b565b9055519384523093a3610eb0565b836000526001825280600020858154039055610ef8565b9050610e8a565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9391929487600014610f93575081905b856000526001845203876000205584600052866000208181540190558651908152a3610e90565b90610f6c565b50505050959066d529ae9e86000090818302918383041483151715610fcd5750670de0b6b3a7640000900495600191610e65565b634e487b7160e01b81526011600452602490fd5b1090503880610e18565b9850866000803e60005198610e0f565b50919098508581803e5197865191610e0f565b634e487b7160e01b83526041600452602483fd5b60405162461bcd60e51b815260206004820152600760248201527f45787069726564000000000000000000000000000000000000000000000000006044820152606490fd5b5030831415610d66565b50807f0000000000000000000000000000b08b88fdee1a65e52f942aa9612a093a000016831415610d60565b9092916001600160a01b0380941691821515806111c5575b6110be90610ca3565b819481169160009083825260016020526040928383205491308603611150575b506110eb88831015610cef565b61dead861461113a5750918082887fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9587602096526001865203828220558681522087815401905551868152a3565b9250505061114d925084939491506112ed565b90565b6002602052848420548091116111bd575b5066d529ae9e8600008083029083820414831517156111a957670de0b6b3a764000090048089116111a1575b5084835260026020528284812055386110de565b97503861118d565b634e487b7160e01b84526011600452602484fd5b975038611161565b507f0000000000000000000000000000b08b88fdee1a65e52f942aa9612a093a000085168314156110b5565b929160009060009281156112e557666a94d74f430000908181029181830414901517156111a957670de0b6b3a76400009004918382131561126257506001600160a01b0391908181111561125b57505b931681526002602052604081208054611258575050565b55565b9050611241565b9492919060018101600181128483129080158216911516176111a957600160ff1b146112d157820390816000198101116112d157916001600160a01b036040926112c594808211156000146112c95750945b168152600260205220918254610d3b565b9055565b9050946112b4565b634e487b7160e01b83526011600452602483fd5b509193505050565b6001600160a01b03166000918183526001602052604083205481811061134f57817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9260209285875260018452036040862055808554038555604051908152a3565b60405162461bcd60e51b815260206004820152601b60248201527f45524332303a206275726e20657863656564732062616c616e636500000000006044820152606490fdfea264697066735822122073611accccc5a4a5f27272a0d92d11e7cf63b85f747dfe9f1d586d61c54c2f5a64736f6c63430008180033