{"language":"Solidity","sources":{"neuro/DentateGyrus.sol":{"content":"// NEUROGENESIS . the ENGRAM launchpad . Ethereum\n// engramv4.xyz . x.com/engram_v4\n//\n//   PROTOCOL N-2 . THE DENTATE GYRUS: THE HOOK OF A LAUNCHED POOL\n//\n//   Purpose. The Uniswap v4 hook of one launched token's ETH / token pool (flags 0x20CC). Its\n//   trading logic is ENGRAM's hook (engram/Hippocampus.sol) unchanged: ten features per trade, the\n//   living mind sets the LP fee between 0.05% and 3.00%, every trade of 0.005 ETH or more is\n//   remembered eight trades later in a keccak256 hash chain the Dreams grade minds against.\n//\n//   Materials. The hook fee is the creator's: chosen at launch, between 0.30% and 3.00% of every\n//   trade's ether leg (HOOK_BPS), and fixed for ever. It is minted to this contract as ERC-6909\n//   claims on the PoolManager, and shared at payout, never per swap:\n//       1/26 of it to the trainers' pot (0.04% of a 1.04% fee), paid only to winners the Dreams\n//         name;\n//       LAUNCHPAD_BPS of it, fixed at launch and never above 25%, to the launchpad: half to\n//         LAUNCHPAD, half to LYSOSOME, which buys ENGRAM and burns it (PROTOCOL N-5);\n//       the rest to OWNER, the creator who launched the token.\n//   The launchpad's share is counted from the pot: every booking puts 1/26 of the fee in the pot,\n//   so the launchpad has earned the pot taken in, all time, x 26 x LAUNCHPAD_BPS / 10,000, less\n//   what it has been paid.\n//\n//   Procedure. payOwner pays OWNER what is neither the pot nor the launchpad's; payLaunchpad pays\n//   the launchpad's two halves. Anyone may call either; the ether only ever goes to its owners.\n//   They are separate so that an OWNER that refuses ether cannot hold up the launchpad, nor the\n//   launchpad OWNER.\n//\n//   Hazards.\n//     - The launchpad's share is rounded down with the pot's: at most a few wei a trade short of\n//       exactly LAUNCHPAD_BPS of the hook fee.\n//     - Everything else is ENGRAM's: memory per tx.origin, the fee only ever between its bounds, a\n//       swap that ends with no liquidity in range refused. No owner, no settings, no pause, no\n//       upgrade. OWNER, LAUNCHPAD and LYSOSOME can receive; none of them can command.\n//\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\nimport \"../engram/V4Types.sol\";\nimport \"../engram/Cortex.sol\";\n\n/// @title  The Dentate Gyrus\n/// @notice The hook of a launched ETH / token pool: ENGRAM's hook, with the launchpad's share of the 1.04% hook fee\n///         paid out of it at payout time.\ncontract DentateGyrus is BaseHook, IUnlockCallback {\n    int24 public constant TICK_SPACING = 60;\n    int24 public constant OPENING_SLACK = 50;\n    int24 public constant EDGE = 887220;\n    /// @notice The trainers' pot is 1/POT_DIV of every hook fee: 0.04% of ENGRAM's 1.04%.\n    uint256 public constant POT_DIV = 26;\n    uint256 public constant MIN_HOOK_BPS = 30;          // 0.30%\n    uint256 public constant MAX_HOOK_BPS = 300;         // 3.00%\n    uint256 public constant MAX_LAUNCHPAD_BPS = 2500;   // 25% of the hook fee\n    int256 public constant FEE_BASE = 3000;      // pips: 0.30%\n    int256 public constant FEE_MIN = 500;        // 0.05%\n    int256 public constant FEE_MAX = 30000;      // 3.00%\n    uint256 public constant LAG = 8;             // a trade is labelled after this many more trades\n    uint256 public constant SEG = 512;           // traces per checkpoint\n    uint256 public constant MIN_TRADE = 0.005 ether;   // smaller trades pay their fees but are not remembered\n\n    address public immutable TOKEN;\n    address public immutable DREAMS;\n    address public immutable OWNER;\n    /// @notice Half of the launchpad's 10% of the hook fee goes here.\n    address public immutable LAUNCHPAD;\n    /// @notice The other half goes here: the Lysosome, which buys ENGRAM and burns it.\n    address public immutable LYSOSOME;\n    /// @notice The hook fee, in basis points of every trade's ether leg: the creator's choice at launch.\n    uint256 public immutable HOOK_BPS;\n    /// @notice The launchpad's share of the hook fee, in basis points of that fee, fixed at launch.\n    uint256 public immutable LAUNCHPAD_BPS;\n    int24 public immutable OPENING_TICK;\n\n    /// @notice The one pool this hook serves, bound when it is initialized.\n    bytes32 public poolId;\n    /// @notice The living mind: a data contract holding 109 bytes, or zero for the default mind.\n    address public champion;\n    /// @notice The trace's running hash: keccak256(abi.encode(previous, trace)) over every trace so far.\n    bytes32 public head;\n    /// @notice The hash at the end of each completed segment of SEG traces; ckHead[0] is zero.\n    mapping(uint256 => bytes32) public ckHead;\n    /// @notice When each segment completed (low 64 bits) and the pot taken in so far at that moment (bits 64+).\n    mapping(uint256 => uint256) public ckInfo;\n\n    // the market state, one word:\n    //   0..31 last swap time | 32..63 last swap block | 64..71 swaps in that block | 72..79 streak (int8)\n    //   80..103 tick EMA (int24) | 104..135 flow EMA (int32) | 136..167 vol EMA | 168..175 ring position\n    //   176..191 traces in the open segment | 192 seen a swap | 200..231 completed segments\n    uint256 internal state;\n    // the purse: pot owed to future winners (low 128) | pot taken in, all time (high 128)\n    uint256 internal purse;\n    // the fee tallies, all time, in wei x pips, [0] for buys (ether in) and [1] for sells (tokens in): every swap's input\n    // x the base fee (low 128) | x the LP fee it paid (high 128). The high half of [1] fills at 11 million times the\n    // whole supply sold at the 3.00% fee.\n    uint256[2] internal feeTally;\n    // the launchpad's share paid so far, all time\n    uint256 internal lpadPaid;\n    // the eight trades waiting for their markout:\n    //   0..79 features | 80..103 tick the trade left | 104 buy | 105..112 weight | 113..144 time\n    //   145..208 memory key | 255 occupied\n    uint256[8] internal ring;\n    // memory per address key (low 64 bits of tx.origin): markout average (int16) | trades seen (uint8 at 16)\n    mapping(uint64 => uint256) internal memories;\n\n    uint256 private constant PENDING = uint256(keccak256(\"engram.hippocampus.pending\")) - 1;\n    uint256 private constant PAYING = uint256(keccak256(\"engram.hippocampus.paying\")) - 1;\n\n    event PoolBound(bytes32 indexed poolId, uint160 sqrtPriceX96);\n    event Trace(uint256 indexed segment, uint256 trace);\n    event Checkpoint(uint256 indexed segment, bytes32 head, uint256 cumPot);\n    event Champion(address indexed mind);\n    event Paid(address indexed to, uint256 eth);\n\n    error PoolTaken();\n    error WrongPool();\n    error WrongOpeningPrice(uint160 sqrtPriceX96, uint160 lo, uint160 hi);\n    error EmptyPool();\n    error PartialFill();\n    error OffTheMap(int24 tick);\n    error NotDreams();\n    error NotPaying();\n    error Nothing();\n    error ZeroAddress();\n    error BadFee();\n\n    constructor(\n        IPoolManager pm,\n        address token,\n        address dreams,\n        address owner,\n        int24 openingTick,\n        address launchpad,\n        address lysosome,\n        uint256 hookBps,\n        uint256 launchpadBps\n    ) BaseHook(pm) {\n        if (token == address(0) || dreams == address(0) || owner == address(0) || launchpad == address(0) || lysosome == address(0)) {\n            revert ZeroAddress();\n        }\n        if (hookBps < MIN_HOOK_BPS || hookBps > MAX_HOOK_BPS || launchpadBps > MAX_LAUNCHPAD_BPS) revert BadFee();\n        HOOK_BPS = hookBps;\n        LAUNCHPAD_BPS = launchpadBps;\n        TOKEN = token;\n        DREAMS = dreams;\n        OWNER = owner;\n        LAUNCHPAD = launchpad;\n        LYSOSOME = lysosome;\n        TickMath.getSqrtPriceAtTick(openingTick - OPENING_SLACK);\n        TickMath.getSqrtPriceAtTick(openingTick + OPENING_SLACK);\n        OPENING_TICK = openingTick;\n    }\n\n    /// @notice Five hook calls (flags 0x20CC). The liquidity calls are off: adding and removing liquidity never\n    ///         touches the hook, so anyone may provide liquidity and earn the LP fee the mind sets.\n    function getHookPermissions() public pure override returns (Hooks.Permissions memory p) {\n        p.beforeInitialize = true;\n        p.beforeSwap = true;\n        p.afterSwap = true;\n        p.beforeSwapReturnDelta = true;\n        p.afterSwapReturnDelta = true;\n    }\n\n    // ------------------------------------------------------------------ the pool\n\n    function beforeInitialize(address, PoolKey calldata key, uint160 sqrtPriceX96)\n        external\n        override\n        onlyPoolManager\n        returns (bytes4)\n    {\n        if (poolId != bytes32(0)) revert PoolTaken();\n        if (Currency.unwrap(key.currency0) != address(0) || Currency.unwrap(key.currency1) != TOKEN\n            || key.fee != LPFee.DYNAMIC || key.tickSpacing != TICK_SPACING || address(key.hooks) != address(this))\n        {\n            revert WrongPool();\n        }\n        uint160 lo = TickMath.getSqrtPriceAtTick(OPENING_TICK - OPENING_SLACK);\n        uint160 hi = TickMath.getSqrtPriceAtTick(OPENING_TICK + OPENING_SLACK);\n        if (sqrtPriceX96 < lo || sqrtPriceX96 > hi) revert WrongOpeningPrice(sqrtPriceX96, lo, hi);\n        bytes32 id = PoolState.toId(key);\n        poolId = id;\n        emit PoolBound(id, sqrtPriceX96);\n        return IHooks.beforeInitialize.selector;\n    }\n\n    // ------------------------------------------------------------------ perceive, think, charge\n\n    function beforeSwap(address, PoolKey calldata, SwapParams calldata p, bytes calldata)\n        external\n        override\n        onlyPoolManager\n        returns (bytes4, BeforeSwapDelta, uint24)\n    {\n        // no empty-pool check here: a swap may start where no liquidity is in range (an outside LP may have left), it\n        // must only END in range (afterSwap). Depth zero simply reads as the largest size feature.\n        bytes32 id = poolId;\n        uint128 liq = PoolState.liquidity(poolManager, id);\n        (uint160 sqrtP, int24 tick) = PoolState.slot0(poolManager, id);\n        bool buy = p.zeroForOne;\n        bool ethNamed = p.zeroForOne == (p.amountSpecified < 0);\n        uint256 abs = p.amountSpecified < 0 ? uint256(-p.amountSpecified) : uint256(p.amountSpecified);\n\n        // the trade's ether, exact when named, otherwise the token amount at the current price\n        uint256 eth = ethNamed ? abs : FullMath.mulDiv(FullMath.mulDiv(abs, 1 << 96, sqrtP), 1 << 96, sqrtP);\n        uint256 depth = FullMath.mulDiv(uint256(liq), 1 << 96, sqrtP);\n        uint256 bps = eth >= depth ? 10000 : (eth * 10000) / depth;\n        (uint256 f, uint256 key) = _perceive(buy, bps, tick);\n\n        int256 pred = Cortex.forward(Cortex.load(champion), f);\n        int256 fee = FEE_BASE + pred * 100;\n        if (fee < FEE_MIN) fee = FEE_MIN;\n        if (fee > FEE_MAX) fee = FEE_MAX;\n\n        // held for afterSwap: features | tick before | buy | depth bps (capped 2000) | LP fee | memory key\n        uint256 bpsCap = bps > 2000 ? 2000 : bps;\n        uint256 pending = f | (uint256(uint24(tick)) << 80) | ((buy ? uint256(1) : 0) << 104) | (bpsCap << 105)\n            | (uint256(fee) << 117) | (key << 145);\n        uint256 slot = PENDING;\n        assembly (\"memory-safe\") { tstore(slot, pending) }\n\n        uint24 lpFee = uint24(uint256(fee)) | LPFee.OVERRIDE;\n        if (!ethNamed) return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, lpFee);\n        uint256 hookFee = (abs * HOOK_BPS) / 10000;\n        if (hookFee == 0) return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, lpFee);\n        _book(hookFee);\n        return (IHooks.beforeSwap.selector, toBeforeSwapDelta(SafeCast.toInt128(hookFee), 0), lpFee);\n    }\n\n    function afterSwap(address, PoolKey calldata, SwapParams calldata p, BalanceDelta d, bytes calldata)\n        external\n        override\n        onlyPoolManager\n        returns (bytes4, int128 out)\n    {\n        bytes32 id = poolId;\n        (, int24 tick) = PoolState.slot0(poolManager, id);\n        if (tick < -EDGE || tick >= EDGE) revert OffTheMap(tick);\n        if (PoolState.liquidity(poolManager, id) == 0) revert EmptyPool();\n        int128 a0 = d.amount0();\n        uint256 eth = a0 > 0 ? uint256(uint128(a0)) : uint256(uint128(-a0));\n        bool ethNamed = p.zeroForOne == (p.amountSpecified < 0);\n        if (ethNamed) {\n            // the fee was taken on the ether named, so the swap must have moved all of it: a swap its price limit\n            // stopped early would have paid the fee on ether that never traded\n            uint256 abs = p.amountSpecified < 0 ? uint256(-p.amountSpecified) : uint256(p.amountSpecified);\n            uint256 hookFee = (abs * HOOK_BPS) / 10000;\n            if (eth != (p.amountSpecified < 0 ? abs - hookFee : abs + hookFee)) revert PartialFill();\n        } else {\n            uint256 hookFee = (eth * HOOK_BPS) / 10000;\n            if (hookFee != 0) {\n                _book(hookFee);\n                out = SafeCast.toInt128(hookFee);\n            }\n        }\n        uint256 pending;\n        uint256 slot = PENDING;\n        assembly (\"memory-safe\") { pending := tload(slot) }\n        // the LP fee is charged on the swap's input, ether on a buy and the token on a sell: tally what it charged and\n        // what the base fee would have, so the Niche can pay OWNER the locked band's LP fees at the base rate\n        uint256 side = p.zeroForOne ? 0 : 1;\n        uint256 input = p.zeroForOne ? eth : uint256(uint128(-d.amount1()));\n        uint256 fs = feeTally[side];\n        feeTally[side] = ((fs & type(uint128).max) + input * uint256(FEE_BASE))\n            | (((fs >> 128) + input * ((pending >> 117) & 0xffff)) << 128);\n        // a trade below MIN_TRADE pays its fees but is not remembered: dust cannot fill the queue, shorten other\n        // trades' horizon, move the market state or anyone's memory\n        if (eth >= MIN_TRADE) _remember(pending, eth, tick);\n        return (IHooks.afterSwap.selector, out);\n    }\n\n    /// @dev The ten features of a trade, packed, and the trader's memory key.\n    function _perceive(bool buy, uint256 bps, int24 tick) private view returns (uint256 f, uint256 key) {\n        uint256 s = state;\n        bool seen = (s >> 192) & 1 == 1;\n        int256 sgn = buy ? int256(1) : int256(-1);\n        int256[10] memory x;\n\n        x[0] = _min127(9 * Cortex.log2(1 + bps));\n        x[1] = buy ? int256(64) : int256(-64);\n        uint256 dt = seen ? block.timestamp - (s & 0xffffffff) : type(uint32).max;\n        x[2] = _min127(8 * Cortex.log2(1 + dt));\n        uint256 inBlock = ((s >> 32) & 0xffffffff) == uint256(uint32(block.number)) && seen ? (s >> 64) & 0xff : 0;\n        x[3] = int256(32 * (inBlock > 3 ? 3 : inBlock));\n        x[4] = _clamp127((sgn * int256(int32(uint32(s >> 104)))) / 16);\n        x[5] = _min127(((s >> 136) & 0xffffffff) / 16);\n        x[6] = seen ? _clamp127(sgn * (int256(int24(uint24(s >> 80))) - int256(tick))) : int256(0);\n\n        key = uint256(uint64(uint160(tx.origin)));\n        uint256 mem = memories[uint64(key)];\n        uint256 cnt = (mem >> 16) & 0xff;\n        if (cnt == 0) {\n            x[8] = 100;\n        } else {\n            x[7] = _clamp127((int256(int16(uint16(mem))) * int256(cnt > 8 ? 8 : cnt)) / 8);\n        }\n        int256 streak = sgn * int256(int8(uint8(s >> 72)));\n        x[9] = 25 * (streak > 4 ? int256(4) : (streak < -4 ? int256(-4) : streak));\n\n        for (uint256 i; i < 10; ++i) f |= uint256(uint8(int8(x[i]))) << (8 * i);\n    }\n\n    /// @dev Label the trade eight trades back, fold it into the trace and its trader's memory, then queue this one\n    ///      and move the market state forward.\n    function _remember(uint256 pending, uint256 eth, int24 tick) private {\n        uint256 s = state;\n        uint256 pos = (s >> 168) & 0xff;\n        uint256 old = ring[pos];\n        if (old >> 255 == 1) {\n            // the markout runs from the price the old trade left behind, so its own impact is not counted\n            int256 left = int256(int24(uint24(old >> 80)));\n            int256 y = (old >> 104) & 1 == 1 ? left - int256(tick) : int256(tick) - left;\n            y = Cortex.clip(y);\n            uint256 trace = (old & Cortex.F_MASK) | (uint256(uint16(int16(y))) << 80) | (((old >> 105) & 0xff) << 96)\n                | (((old >> 113) & 0xffffffff) << 104);\n            bytes32 h = head;\n            assembly (\"memory-safe\") {\n                mstore(0x00, h)\n                mstore(0x20, trace)\n                h := keccak256(0x00, 0x40)\n            }\n            head = h;\n            uint256 segs = (s >> 200) & 0xffffffff;\n            emit Trace(segs + 1, trace);\n\n            // the trader's memory moves by the trade's weight (1 to 48 of 64): a big trade counts as much as many small\n            // ones, so a record cannot be washed clean with dust-sized round trips\n            uint64 key = uint64(old >> 145);\n            uint256 mem = memories[key];\n            uint256 cnt = (mem >> 16) & 0xff;\n            int256 wt = int256((old >> 105) & 0xff);\n            int256 rep = cnt == 0 ? y : (int256(int16(uint16(mem))) * (64 - wt) + y * wt) / 64;\n            memories[key] = uint256(uint16(int16(rep))) | ((cnt < 255 ? cnt + 1 : 255) << 16);\n\n            uint256 inSeg = ((s >> 176) & 0xffff) + 1;\n            if (inSeg == SEG) {\n                segs += 1;\n                ckHead[segs] = h;\n                uint256 cum = purse >> 128;\n                ckInfo[segs] = block.timestamp | (cum << 64);\n                emit Checkpoint(segs, h, cum);\n                inSeg = 0;\n                s = (s & ~(uint256(0xffffffff) << 200)) | (segs << 200);\n            }\n            s = (s & ~(uint256(0xffff) << 176)) | (inSeg << 176);\n        }\n        bool buy = (pending >> 104) & 1 == 1;\n        ring[pos] = (pending & Cortex.F_MASK) | (uint256(uint24(tick)) << 80) | ((buy ? uint256(1) : 0) << 104)\n            | (Cortex.weight(eth) << 105) | (block.timestamp << 113) | ((pending >> 145) << 145) | (1 << 255);\n        s = _advance(s, pending, buy, tick, (pos + 1) % LAG);\n        state = s;\n    }\n\n    /// @dev The market state after a swap: time, block count, streak, and the three decaying averages.\n    function _advance(uint256 s, uint256 pending, bool buy, int24 tick, uint256 nextPos) private view returns (uint256) {\n        bool seen = (s >> 192) & 1 == 1;\n        uint256 blockNo = uint256(uint32(block.number));\n        uint256 inBlock = seen && ((s >> 32) & 0xffffffff) == blockNo ? (s >> 64) & 0xff : 0;\n        if (inBlock < 255) inBlock += 1;\n\n        int256 streak = int256(int8(uint8(s >> 72)));\n        if (buy) streak = streak > 0 ? (streak < 100 ? streak + 1 : streak) : int256(1);\n        else streak = streak < 0 ? (streak > -100 ? streak - 1 : streak) : int256(-1);\n\n        int256 t = int256(tick);\n        int256 ema = seen ? int256(int24(uint24(s >> 80))) : t;\n        ema = ema + (t - ema) / 16;\n        int256 pre = int256(int24(uint24(pending >> 80)));\n        int256 move = t > pre ? t - pre : pre - t;\n        if (move > 2000) move = 2000;\n        int256 signedBps = (buy ? int256(1) : int256(-1)) * int256((pending >> 105) & 0xfff);\n        int256 flow = int256(int32(uint32(s >> 104)));\n        flow = flow - flow / 16 + signedBps;\n        uint256 vol = (s >> 136) & 0xffffffff;\n        vol = vol - vol / 16 + uint256(move);\n\n        uint256 keep = s & ((uint256(0xffff) << 176) | (uint256(0xffffffff) << 200));\n        return keep | uint256(uint32(block.timestamp)) | (blockNo << 32) | (inBlock << 64) | (uint256(uint8(int8(streak))) << 72)\n            | (uint256(uint24(int24(ema))) << 80) | (uint256(uint32(int32(flow))) << 104) | ((vol & 0xffffffff) << 136)\n            | (nextPos << 168) | (uint256(1) << 192);\n    }\n\n    /// @dev The hook's credit for `fee` ether is minted to it as a claim; the pot's share is booked.\n    function _book(uint256 fee) private {\n        poolManager.mint(address(this), 0, fee);\n        uint256 share = fee / POT_DIV;\n        uint256 pu = purse;\n        purse = ((pu >> 128) + share) << 128 | ((pu & type(uint128).max) + share);\n    }\n\n    function _min127(uint256 v) private pure returns (int256) {\n        return v > 127 ? int256(127) : int256(v);\n    }\n\n    function _clamp127(int256 v) private pure returns (int256) {\n        return v > 127 ? int256(127) : (v < -127 ? int256(-127) : v);\n    }\n\n    // ------------------------------------------------------------------ the mind and the purse\n\n    /// @notice The Dreams name the living mind. Zero is the default mind: the flat 0.30% LP fee.\n    function setChampion(address mind) external {\n        if (msg.sender != DREAMS) revert NotDreams();\n        champion = mind;\n        emit Champion(mind);\n    }\n\n    /// @notice Pays OWNER its share of the fees taken so far: all that is neither the pot nor the launchpad's. Anyone\n    ///         may call it.\n    function payOwner() external returns (uint256 amount) {\n        amount = _ownerOwed();\n        if (amount == 0) revert Nothing();\n        _pay(OWNER, amount, address(0), 0);\n    }\n\n    /// @notice Pays the launchpad its share of the fees taken so far: half to LAUNCHPAD, half to LYSOSOME. Anyone may\n    ///         call it.\n    function payLaunchpad() external returns (uint256 amount) {\n        amount = _launchpadOwed();\n        if (amount == 0) revert Nothing();\n        lpadPaid += amount;\n        uint256 half = amount / 2;\n        _pay(LAUNCHPAD, half, LYSOSOME, amount - half);\n    }\n\n    /// @dev The launchpad's share not yet paid: the pot taken in, all time, x 26 (every hook fee, the pot being 1/26 of\n    ///      it) x LAUNCHPAD_BPS / 10,000, less what has been paid, and never more than the claims the pot does not own.\n    function _launchpadOwed() private view returns (uint256) {\n        uint256 pu = purse;\n        uint256 earned = ((pu >> 128) * POT_DIV * LAUNCHPAD_BPS) / 10000;\n        uint256 owed = earned > lpadPaid ? earned - lpadPaid : 0;\n        uint256 claims = poolManager.balanceOf(address(this), 0);\n        uint256 held = pu & type(uint128).max;\n        uint256 free = claims > held ? claims - held : 0;\n        return owed < free ? owed : free;\n    }\n\n    /// @dev OWNER's share not yet paid: the claims, less the pot and the launchpad's share.\n    function _ownerOwed() private view returns (uint256) {\n        uint256 claims = poolManager.balanceOf(address(this), 0);\n        uint256 held = (purse & type(uint128).max) + _launchpadOwed();\n        return claims > held ? claims - held : 0;\n    }\n\n    /// @notice The Dreams pay a winner out of the pot.\n    function payPot(address to, uint256 amount) external {\n        if (msg.sender != DREAMS) revert NotDreams();\n        uint256 pu = purse;\n        uint256 held = pu & type(uint128).max;\n        if (amount == 0 || amount > held) revert Nothing();\n        purse = (pu & ~uint256(type(uint128).max)) | (held - amount);\n        _pay(to, amount, address(0), 0);\n    }\n\n    function _pay(address to, uint256 amount, address to2, uint256 amount2) private {\n        uint256 slot = PAYING;\n        assembly (\"memory-safe\") { tstore(slot, 1) }\n        poolManager.unlock(abi.encode(to, amount, to2, amount2));\n        assembly (\"memory-safe\") { tstore(slot, 0) }\n        emit Paid(to, amount);\n        if (amount2 != 0) emit Paid(to2, amount2);\n    }\n\n    /// @dev Burns this contract's ether claims and takes the ether to one recipient, or two.\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        if (msg.sender != address(poolManager)) revert NotPoolManager();\n        uint256 slot = PAYING;\n        uint256 paying;\n        assembly (\"memory-safe\") { paying := tload(slot) }\n        if (paying != 1) revert NotPaying();\n        (address to, uint256 amount, address to2, uint256 amount2) = abi.decode(data, (address, uint256, address, uint256));\n        poolManager.burn(address(this), 0, amount + amount2);\n        if (amount != 0) poolManager.take(Currency.wrap(address(0)), to, amount);\n        if (amount2 != 0) poolManager.take(Currency.wrap(address(0)), to2, amount2);\n        return \"\";\n    }\n\n    // ------------------------------------------------------------------ views\n\n    /// @notice Ether owed to the pot (for future winners) and taken into it all time.\n    function pot() external view returns (uint256 owed, uint256 cumulative) {\n        uint256 pu = purse;\n        return (pu & type(uint128).max, pu >> 128);\n    }\n\n    /// @notice All time, in wei x pips, for buys (ether in) and for sells (tokens in): the sum over every swap of its input\n    ///         x the base fee, and x the LP fee it paid. The Niche pays OWNER the locked band's LP fees in each currency\n    ///         scaled by atBase / charged since its last collect, never above 1.\n    function feeSums() external view returns (uint256 ethAtBase, uint256 ethCharged, uint256 tokAtBase, uint256 tokCharged) {\n        uint256 e = feeTally[0];\n        uint256 t = feeTally[1];\n        return (e & type(uint128).max, e >> 128, t & type(uint128).max, t >> 128);\n    }\n\n    /// @notice Ether OWNER can be paid now.\n    function ownerOwed() external view returns (uint256) {\n        return _ownerOwed();\n    }\n\n    /// @notice Ether the launchpad can be paid now (half to LAUNCHPAD, half to LYSOSOME), and paid so far, all time.\n    function launchpadOwed() external view returns (uint256 owed, uint256 paid) {\n        return (_launchpadOwed(), lpadPaid);\n    }\n\n    /// @notice Completed segments of SEG traces, and traces in the open one.\n    function segments() external view returns (uint256 done, uint256 open) {\n        uint256 s = state;\n        return ((s >> 200) & 0xffffffff, (s >> 176) & 0xffff);\n    }\n\n    /// @notice When segment k completed (0 if it has not).\n    function ckTime(uint256 k) external view returns (uint256) {\n        return ckInfo[k] & type(uint64).max;\n    }\n\n    /// @notice The pot taken in, all time, when segment k completed.\n    function ckPot(uint256 k) external view returns (uint256) {\n        return ckInfo[k] >> 64;\n    }\n\n    /// @notice An address's memory: its decayed average markout in basis points, and how many trades it has had labelled.\n    function memoryOf(address trader) external view returns (int256 markout, uint256 trades) {\n        uint256 mem = memories[uint64(uint160(trader))];\n        return (int256(int16(uint16(mem))), (mem >> 16) & 0xff);\n    }\n\n    /// @notice The raw market state word and the eight queued trades, for clients that mirror the hook.\n    function market() external view returns (uint256 s, uint256[8] memory queue) {\n        return (state, ring);\n    }\n\n    /// @notice The fee the living mind would set for a trade with these features (what beforeSwap computes).\n    function feeFor(uint256 features) external view returns (uint24 lpFee, int256 prediction) {\n        prediction = Cortex.forward(Cortex.load(champion), features);\n        int256 fee = FEE_BASE + prediction * 100;\n        if (fee < FEE_MIN) fee = FEE_MIN;\n        if (fee > FEE_MAX) fee = FEE_MAX;\n        lpFee = uint24(uint256(fee));\n    }\n}\n"},"neuro/Lysosome.sol":{"content":"// NEUROGENESIS . the ENGRAM launchpad . Ethereum\n// engramv4.xyz . x.com/engram_v4\n//\n//   PROTOCOL N-5 . THE LYSOSOME: WHERE THE LAUNCHPAD'S ETHER BECOMES BURNED ENGRAM\n//\n//   Purpose. Half of the launchpad's share of every launched pool's hook fee is paid here, as\n//   ether. digest spends it on ENGRAM in ENGRAM's own pool and burns every ENGRAM held here.\n//\n//   Materials. ENGRAM (0x56C9...7Cc6), its hook the Hippocampus (0x0Ee8...A0cc), and their ETH /\n//   ENGRAM pool: dynamic fee, tick spacing 60. All fixed as constants.\n//\n//   Procedure. Anyone may call digest, at most once a block. It spends the smaller of this\n//   contract's balance and 0.5% of the pool's in-range ether depth (in-range liquidity x 2^96 /\n//   sqrtPrice), in one exact-input buy with no price limit (ENGRAM's hook refuses a buy its limit\n//   would stop short), then burns all the ENGRAM it holds, bought or sent.\n//\n//   Expected result. ENGRAM's supply only falls. The buy pays ENGRAM's fees like any other trade:\n//   1.04% to its hook, and the LP fee its mind sets.\n//\n//   Hazards.\n//     - A digest can be sandwiched. At 0.5% of depth it moves the price about 1%, while a round\n//       trip around it pays at least 2.08% in ENGRAM's hook fee alone, so a sandwich loses money.\n//       Once a block stops one transaction from calling it again and again.\n//     - A large balance takes many blocks to spend. Nobody is paid to call digest.\n//     - No owner, no withdrawal. Ether sent here can only become burned ENGRAM.\n//\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\nimport \"../engram/V4Types.sol\";\n\ninterface IEngramBurn {\n    function balanceOf(address) external view returns (uint256);\n    function burn(uint256) external;\n}\n\n/// @title  The Lysosome\n/// @notice Buys ENGRAM with the ether paid here and burns it, a capped amount at a time, at most once a block.\ncontract Lysosome is IUnlockCallback {\n    address public constant POOL_MANAGER = 0x000000000004444c5dc75cB358380D2e3dE08A90;\n    address public constant ENGRAM = 0x56C915D92E24255fdC52eb114a31399c432B7Cc6;\n    address public constant HIPPOCAMPUS = 0x0Ee851f1fe2f4BDbA79FeE78969E329C136CA0cc;\n    /// @notice The most one digest spends, in parts per 10,000 of the pool's in-range ether depth: 0.5%.\n    uint256 public constant DEPTH_BPS = 50;\n    // TickMath.MIN_SQRT_PRICE + 1: a buy walks the price down, and this limit never stops it\n    uint160 internal constant NO_LIMIT = 4295128740;\n\n    /// @notice The block of the last digest.\n    uint256 public lastBlock;\n    /// @notice Ether spent on ENGRAM, all time.\n    uint256 public totalSpent;\n    /// @notice ENGRAM burned, all time.\n    uint256 public totalBurned;\n\n    event Digested(address indexed by, uint256 eth, uint256 engram);\n\n    error OncePerBlock();\n    error Nothing();\n    error NotPoolManager();\n\n    /// @notice Spends up to 0.5% of the ENGRAM pool's ether depth from this contract's balance on ENGRAM, then burns all\n    ///         the ENGRAM held here. Anyone may call it, once a block.\n    function digest() external returns (uint256 eth, uint256 burned) {\n        if (block.number == lastBlock) revert OncePerBlock();\n        lastBlock = block.number;\n        eth = address(this).balance;\n        uint256 cap = capNow();\n        if (eth > cap) eth = cap;\n        if (eth != 0) IPoolManager(POOL_MANAGER).unlock(abi.encode(eth));\n        burned = IEngramBurn(ENGRAM).balanceOf(address(this));\n        if (burned == 0) revert Nothing();\n        IEngramBurn(ENGRAM).burn(burned);\n        totalSpent += eth;\n        totalBurned += burned;\n        emit Digested(msg.sender, eth, burned);\n    }\n\n    /// @notice The most the next digest may spend: 0.5% of the pool's in-range ether depth, now.\n    function capNow() public view returns (uint256) {\n        bytes32 id = PoolState.toId(_key());\n        (uint160 sqrtP,) = PoolState.slot0(IPoolManager(POOL_MANAGER), id);\n        if (sqrtP == 0) return 0;\n        uint128 liq = PoolState.liquidity(IPoolManager(POOL_MANAGER), id);\n        return (FullMath.mulDiv(uint256(liq), 1 << 96, sqrtP) * DEPTH_BPS) / 10000;\n    }\n\n    /// @dev The buy, inside the PoolManager's lock: `eth` in, exactly; every ENGRAM out, to here.\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        if (msg.sender != POOL_MANAGER) revert NotPoolManager();\n        uint256 eth = abi.decode(data, (uint256));\n        BalanceDelta d = IPoolManager(POOL_MANAGER).swap(_key(), SwapParams(true, -int256(eth), NO_LIMIT), \"\");\n        IPoolManager(POOL_MANAGER).settle{value: uint256(uint128(-d.amount0()))}();\n        IPoolManager(POOL_MANAGER).take(Currency.wrap(ENGRAM), address(this), uint256(uint128(d.amount1())));\n        return \"\";\n    }\n\n    function _key() private pure returns (PoolKey memory) {\n        return PoolKey(Currency.wrap(address(0)), Currency.wrap(ENGRAM), LPFee.DYNAMIC, 60, IHooks(HIPPOCAMPUS));\n    }\n\n    /// @dev The launched hooks pay here through the PoolManager; anyone else may add to it.\n    receive() external payable {}\n}\n"},"neuro/Neuroblast.sol":{"content":"// NEUROGENESIS . the ENGRAM launchpad . Ethereum\n// engramv4.xyz . x.com/engram_v4\n//\n//   PROTOCOL N-1 . THE NEUROBLAST: A TOKEN BORN IN THE NICHE\n//\n//   Purpose. The token of one launch. Its name and symbol are the creator's; everything else is\n//   fixed and identical for every launch.\n//\n//   Materials. 1,000,000,000 tokens, 18 decimals, minted once, in the constructor, to whoever\n//   deploys this contract: the launch's Niche (PROTOCOL N-3), which places them in the pool.\n//\n//   Procedure. A plain ERC-20 with EIP-2612 permit. burn and burnFrom may be called by any\n//   holder, so the supply can only fall. No mint function exists.\n//\n//   Hazards. None designed in: no owner, no admin, no pause, no blacklist, no trading switch, no\n//   proxy, no upgrade. A name or symbol is only what its creator typed; the launchpad records who\n//   launched each token, and only that record says which launch is which. Not advice.\n//\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\n/// @title  Neuroblast\n/// @notice A launch's token: fixed supply, no owner, burnable, permit. Name and symbol set once, by its creator.\ncontract Neuroblast {\n    uint8 public constant decimals = 18;\n    uint256 public constant INITIAL_SUPPLY = 1_000_000_000 ether;\n\n    string public name;\n    string public symbol;\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n    mapping(address => uint256) public nonces;\n\n    bytes32 private constant PERMIT_TYPEHASH =\n        keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n    uint256 private immutable CHAIN_ID;\n    bytes32 private immutable DOMAIN;\n\n    event Transfer(address indexed from, address indexed to, uint256 value);\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    error InsufficientBalance();\n    error InsufficientAllowance();\n    error PermitExpired();\n    error BadSignature();\n\n    constructor(string memory name_, string memory symbol_) {\n        name = name_;\n        symbol = symbol_;\n        CHAIN_ID = block.chainid;\n        DOMAIN = _domain();\n        totalSupply = INITIAL_SUPPLY;\n        balanceOf[msg.sender] = INITIAL_SUPPLY;\n        emit Transfer(address(0), msg.sender, INITIAL_SUPPLY);\n    }\n\n    function transfer(address to, uint256 value) external returns (bool) {\n        _move(msg.sender, to, value);\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 value) external returns (bool) {\n        _spend(from, value);\n        _move(from, to, value);\n        return true;\n    }\n\n    function approve(address spender, uint256 value) external returns (bool) {\n        allowance[msg.sender][spender] = value;\n        emit Approval(msg.sender, spender, value);\n        return true;\n    }\n\n    function burn(uint256 value) external {\n        _burn(msg.sender, value);\n    }\n\n    function burnFrom(address from, uint256 value) external {\n        _spend(from, value);\n        _burn(from, value);\n    }\n\n    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)\n        external\n    {\n        if (block.timestamp > deadline) revert PermitExpired();\n        bytes32 digest = keccak256(abi.encodePacked(\n            \"\\x19\\x01\", DOMAIN_SEPARATOR(),\n            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))\n        ));\n        // the upper half of the curve order is refused, so a signature has one form only\n        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) revert BadSignature();\n        address signer = ecrecover(digest, v, r, s);\n        if (signer == address(0) || signer != owner) revert BadSignature();\n        allowance[owner][spender] = value;\n        emit Approval(owner, spender, value);\n    }\n\n    function DOMAIN_SEPARATOR() public view returns (bytes32) {\n        return block.chainid == CHAIN_ID ? DOMAIN : _domain();\n    }\n\n    function _domain() private view returns (bytes32) {\n        return keccak256(abi.encode(\n            keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n            keccak256(bytes(name)), keccak256(\"1\"), block.chainid, address(this)\n        ));\n    }\n\n    function _spend(address from, uint256 value) private {\n        uint256 a = allowance[from][msg.sender];\n        if (a != type(uint256).max) {\n            if (a < value) revert InsufficientAllowance();\n            unchecked { allowance[from][msg.sender] = a - value; }\n        }\n    }\n\n    function _move(address from, address to, uint256 value) private {\n        uint256 b = balanceOf[from];\n        if (b < value) revert InsufficientBalance();\n        unchecked {\n            balanceOf[from] = b - value;\n            balanceOf[to] += value;\n        }\n        emit Transfer(from, to, value);\n    }\n\n    function _burn(address from, uint256 value) private {\n        uint256 b = balanceOf[from];\n        if (b < value) revert InsufficientBalance();\n        unchecked {\n            balanceOf[from] = b - value;\n            totalSupply -= value;\n        }\n        emit Transfer(from, address(0), value);\n    }\n}\n"},"neuro/Neurogenesis.sol":{"content":"// NEUROGENESIS . the ENGRAM launchpad . Ethereum\n// engramv4.xyz . x.com/engram_v4\n//\n//   PROTOCOL N-4 . NEUROGENESIS: THE LAUNCHPAD\n//\n//   Purpose. Anyone may launch a token here. Each launch is grown in its own Niche (PROTOCOL N-3),\n//   which deploys the token (N-1), its hook (N-2) and its Dreams, then keeps the launch liquidity\n//   for ever. This contract keeps the list of every launch.\n//\n//   Materials. Three blueprints, written once, by this constructor, from the compiled code of the\n//   token, the hook and ENGRAM's Dreams: data contracts, each one STOP followed by an init code,\n//   together too large to live inside this contract. The Beacon is ENGRAM's, already on chain with\n//   drand quicknet's key. The Lysosome (N-5) is deployed here, once.\n//\n//   Procedure. launch(name, symbol, openingTick, hookBps, cutBps, userSalt, hookSalt, openNow):\n//     1. deploys the creator's Niche with CREATE2, at salt keccak256(creator, userSalt);\n//     2. has it deploy the launch, closed, with the creator's hook fee (0.30% to 3.00%) and the\n//        launchpad's cut as it stands now, both fixed in the launch's hook for ever. cutBps must\n//        be that cut exactly, so a change can never land under a launch on its way. hookSalt must\n//        land the hook on an address carrying exactly the flags 0x20CC: the launch page mines it\n//        against that Niche's address, that fee and that cut;\n//     3. with openNow, opens the pool at once, with the one wei the band owes sent along;\n//        without it, the creator opens it later, from the Niche, after verifying the contracts.\n//   Launches are numbered from 1 in the order they are made: niches(number - 1), numberOf(token).\n//\n//   Expected result. Of every launched trade's hook fee, the launch's cut is the launchpad's\n//   (10% when this contract was deployed), paid by the launch's hook: half to this contract, for\n//   the payee, and half to the Lysosome, which buys ENGRAM and burns it.\n//\n//   OWNER. OWNER may set the cut for launches to come, never above 25% of the hook fee, and the\n//   payee: the wallet withdraw sends this contract's half to. Nothing OWNER does reaches a launch\n//   already made, its fee, its cut, its pool or its Lysosome half. OWNER may hand the role on, in\n//   two steps, or renounce it, which fixes the cut and the payee for ever.\n//\n//   Hazards. A name or ticker is whatever its creator typed: two launches may share one, and only\n//   this list and the creator's address tell them apart. Nothing here vets a launch. Not advice.\n//\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\nimport \"./Niche.sol\";\nimport \"./Neuroblast.sol\";\nimport \"./DentateGyrus.sol\";\nimport \"./Lysosome.sol\";\nimport \"../engram/Dreams.sol\";\n\n/// @title  Neurogenesis\n/// @notice The ENGRAM launchpad: launches a token with its own mind-run pool, Dreams and locked liquidity, in one\n///         Niche per launch, and lists every launch.\ncontract Neurogenesis {\n    uint256 public constant MIN_HOOK_BPS = 30;          // 0.30% of a trade's ether leg\n    uint256 public constant MAX_HOOK_BPS = 300;         // 3.00%\n    uint256 public constant MAX_CUT_BPS = 2500;         // the launchpad's cut, at most 25% of the hook fee\n\n    /// @notice The token, hook and Dreams init codes every launch is built from, each behind one STOP byte.\n    address public immutable GENOME_TOKEN;\n    address public immutable GENOME_HOOK;\n    address public immutable GENOME_DREAMS;\n    /// @notice ENGRAM's Beacon, shared by every launch's Dreams.\n    address public immutable BEACON;\n    /// @notice Paid half of the launchpad's cut of every launch's hook fee, to buy ENGRAM and burn it.\n    address public immutable LYSOSOME;\n\n    /// @notice The launchpad's cut of a new launch's hook fee, in basis points of that fee.\n    uint256 public cutBps = 1000;\n    /// @notice May set the cut for launches to come, and the payee.\n    address public owner;\n    /// @notice The owner named next, until it accepts.\n    address public pendingOwner;\n    /// @notice Where withdraw sends the launchpad's half.\n    address public payee;\n\n    /// @notice One launch as the launch page lists it: position is zero while the pool is closed, born is the block of\n    ///         its launch.\n    struct Launch {\n        uint256 number;\n        address niche;\n        address token;\n        address hook;\n        address dreams;\n        address creator;\n        int24 openingTick;\n        uint256 position;\n        uint256 born;\n        uint160 sqrtPriceX96;\n        uint256 hookBps;\n        uint256 cutBps;\n        string name;\n        string symbol;\n    }\n\n    // every launch, in order: its Niche (low 160 bits) and the block it was born in (above them)\n    uint256[] internal entries;\n    /// @notice A token's launch number, from 1; zero for a token not launched here.\n    mapping(address => uint256) public numberOf;\n\n    event Born(\n        uint256 indexed number, address indexed creator, address indexed token, address niche, address hook,\n        address dreams, int24 openingTick, uint256 hookBps, uint256 cutBps, string name, string symbol\n    );\n    event CutSet(uint256 cutBps);\n    event PayeeSet(address indexed payee);\n    event OwnerOffered(address indexed to);\n    event OwnerSet(address indexed owner);\n    event Withdrawn(address indexed to, uint256 eth);\n\n    error NoCode(address at);\n    error ZeroAddress();\n    error BadName();\n    error BadSymbol();\n    error BadFee();\n    error CutChanged(uint256 cutBps);\n    error NotOpening();\n    error NotOwner();\n    error Nothing();\n    error SendFailed();\n\n    /// @param beacon ENGRAM's Beacon, already deployed with drand quicknet's key\n    /// @param owner_ sets the cut for launches to come and the payee; the first payee too\n    constructor(address beacon, address owner_) {\n        if (beacon.code.length == 0) revert NoCode(beacon);\n        if (owner_ == address(0)) revert ZeroAddress();\n        BEACON = beacon;\n        owner = owner_;\n        payee = owner_;\n        GENOME_TOKEN = _blueprint(type(Neuroblast).creationCode);\n        GENOME_HOOK = _blueprint(type(DentateGyrus).creationCode);\n        GENOME_DREAMS = _blueprint(type(Dreams).creationCode);\n        LYSOSOME = address(new Lysosome());\n        emit OwnerSet(owner_);\n        emit PayeeSet(owner_);\n        emit CutSet(1000);\n    }\n\n    /// @notice Launches a token: its Niche, its contracts and its pool, closed, or open at once with openNow (send the\n    ///         one wei the band owes; any more comes back). name is 1 to 32 bytes, symbol 1 to 16.\n    /// @param openingTick the opening price as a tick, a multiple of 60: the top of the band holding the whole supply\n    /// @param hookBps     the creator's hook fee, 30 to 300 basis points of every trade's ether leg, fixed for ever\n    /// @param cut         the launchpad's cut as the creator saw it: it must be cutBps now, or nothing is launched\n    /// @param userSalt    any value; with the creator's address it fixes the Niche's address\n    /// @param hookSalt    lands the hook on an address carrying flags 0x20CC, mined against that Niche, fee and cut\n    function launch(\n        string calldata name,\n        string calldata symbol,\n        int24 openingTick,\n        uint256 hookBps,\n        uint256 cut,\n        bytes32 userSalt,\n        bytes32 hookSalt,\n        bool openNow\n    ) external payable returns (address niche) {\n        uint256 n = bytes(name).length;\n        if (n == 0 || n > 32) revert BadName();\n        n = bytes(symbol).length;\n        if (n == 0 || n > 16) revert BadSymbol();\n        if (hookBps < MIN_HOOK_BPS || hookBps > MAX_HOOK_BPS) revert BadFee();\n        if (cut != cutBps) revert CutChanged(cutBps);\n        if (!openNow && msg.value != 0) revert NotOpening();\n\n        Niche nc = new Niche{salt: keccak256(abi.encode(msg.sender, userSalt))}(msg.sender);\n        nc.setup(Genome(GENOME_TOKEN, GENOME_HOOK, GENOME_DREAMS, BEACON, address(this), LYSOSOME), name, symbol, openingTick, hookBps, cut, hookSalt);\n        niche = address(nc);\n        address token = nc.TOKEN();\n        entries.push(uint256(uint160(niche)) | (block.number << 160));\n        uint256 number = entries.length;\n        numberOf[token] = number;\n        emit Born(number, msg.sender, token, niche, nc.HOOK(), nc.DREAMS(), openingTick, hookBps, cut, name, symbol);\n        if (openNow) nc.open{value: msg.value}();\n    }\n\n    // ------------------------------------------------------------------ the launchpad's half, and OWNER\n\n    /// @notice Sends everything this contract holds, the launchpad's half of every launch's cut, to the payee. Anyone\n    ///         may call it.\n    function withdraw() external returns (uint256 amount) {\n        amount = address(this).balance;\n        if (amount == 0) revert Nothing();\n        address to = payee;\n        (bool ok,) = to.call{value: amount}(\"\");\n        if (!ok) revert SendFailed();\n        emit Withdrawn(to, amount);\n    }\n\n    /// @notice The cut of launches to come, in basis points of their hook fee: at most MAX_CUT_BPS. Launches already\n    ///         made keep theirs.\n    function setCut(uint256 bps) external {\n        if (msg.sender != owner) revert NotOwner();\n        if (bps > MAX_CUT_BPS) revert BadFee();\n        cutBps = bps;\n        emit CutSet(bps);\n    }\n\n    /// @notice Where withdraw sends the launchpad's half from now on.\n    function setPayee(address to) external {\n        if (msg.sender != owner) revert NotOwner();\n        if (to == address(0)) revert ZeroAddress();\n        payee = to;\n        emit PayeeSet(to);\n    }\n\n    /// @notice Names the next owner, who must accept. Naming zero withdraws an offer.\n    function offerOwner(address to) external {\n        if (msg.sender != owner) revert NotOwner();\n        pendingOwner = to;\n        emit OwnerOffered(to);\n    }\n\n    /// @notice The named owner takes the role.\n    function acceptOwner() external {\n        if (msg.sender != pendingOwner || msg.sender == address(0)) revert NotOwner();\n        owner = msg.sender;\n        pendingOwner = address(0);\n        emit OwnerSet(msg.sender);\n    }\n\n    /// @notice Gives the role up for ever: the cut and the payee are then fixed as they stand.\n    function renounceOwner() external {\n        if (msg.sender != owner) revert NotOwner();\n        owner = address(0);\n        pendingOwner = address(0);\n        emit OwnerSet(address(0));\n    }\n\n    /// @dev Each launch's hook pays the launchpad's half here, through the PoolManager; anyone else may add to it.\n    receive() external payable {}\n\n    // ------------------------------------------------------------------ views\n\n    /// @notice How many tokens have been launched here.\n    function count() external view returns (uint256) {\n        return entries.length;\n    }\n\n    /// @notice Launch number n's Niche is niches(n - 1).\n    function niches(uint256 i) external view returns (address) {\n        return address(uint160(entries[i]));\n    }\n\n    /// @notice Up to n launches, from launch number `first` on, with each pool's price now and each launch's fee and cut:\n    ///         the launch page's list in one call.\n    function launches(uint256 first, uint256 n) external view returns (Launch[] memory out) {\n        uint256 total = entries.length;\n        if (first == 0) first = 1;\n        if (n == 0 || first > total) return out;\n        uint256 last = total - first < n ? total : first + n - 1;\n        out = new Launch[](last - first + 1);\n        for (uint256 i; i < out.length; ++i) {\n            uint256 e = entries[first - 1 + i];\n            Niche nc = Niche(payable(address(uint160(e))));\n            Launch memory l = out[i];\n            l.number = first + i;\n            l.born = e >> 160;\n            l.niche = address(nc);\n            l.token = nc.TOKEN();\n            l.hook = nc.HOOK();\n            l.dreams = nc.DREAMS();\n            l.creator = nc.OWNER();\n            l.openingTick = nc.OPENING_TICK();\n            l.position = nc.POSITION();\n            PoolKey memory key = PoolKey(Currency.wrap(address(0)), Currency.wrap(l.token), LPFee.DYNAMIC, 60, IHooks(l.hook));\n            (l.sqrtPriceX96,) = PoolState.slot0(IPoolManager(nc.POOL_MANAGER()), PoolState.toId(key));\n            l.hookBps = DentateGyrus(l.hook).HOOK_BPS();\n            l.cutBps = DentateGyrus(l.hook).LAUNCHPAD_BPS();\n            l.name = Neuroblast(l.token).name();\n            l.symbol = Neuroblast(l.token).symbol();\n        }\n    }\n\n    /// @notice Where launch(..., userSalt, ...) from `creator` puts its Niche. The token is that Niche's first CREATE,\n    ///         the Dreams its third; the hook is its CREATE2 at hookSalt.\n    function nicheFor(address creator, bytes32 userSalt) external view returns (address) {\n        bytes32 code = keccak256(abi.encodePacked(type(Niche).creationCode, abi.encode(creator)));\n        bytes32 salt = keccak256(abi.encode(creator, userSalt));\n        return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, code)))));\n    }\n\n    /// @dev Writes a data contract whose code is one STOP byte and then `initCode`, and returns its address.\n    function _blueprint(bytes memory initCode) private returns (address a) {\n        uint256 n = initCode.length + 1;\n        // PUSH2 n, DUP1, PUSH1 10, RETURNDATASIZE, CODECOPY, RETURNDATASIZE, RETURN: returns the n bytes after itself\n        bytes memory c = abi.encodePacked(hex\"61\", uint16(n), hex\"80600a3d393df3\", hex\"00\", initCode);\n        assembly (\"memory-safe\") { a := create(0, add(c, 32), mload(c)) }\n        if (a == address(0)) revert NoCode(a);\n    }\n}\n"},"neuro/Niche.sol":{"content":"// NEUROGENESIS . the ENGRAM launchpad . Ethereum\n// engramv4.xyz . x.com/engram_v4\n//\n//   PROTOCOL N-3 . THE NICHE: WHERE ONE LAUNCH IS GROWN, AND KEPT\n//\n//   Purpose. One Niche per launch. It deploys the launch's token, hook and Dreams, opens the pool\n//   closed, and later, on its creator's word, puts the whole supply in. Then it holds the launch\n//   liquidity for ever, as ENGRAM's Launcher holds ENGRAM's.\n//\n//   Materials. The init code of the token (PROTOCOL N-1), the hook (PROTOCOL N-2) and ENGRAM's\n//   Dreams, read from the launchpad's three blueprints: data contracts too large to live inside\n//   the launchpad itself. The Beacon is ENGRAM's, already on chain with drand quicknet's key.\n//\n//   Procedure. The launchpad (PROTOCOL N-4) deploys this contract and calls setup once, in the\n//   same transaction. setup, in order, or not at all:\n//     1. deploys the token (this contract's CREATE nonce 1): 1,000,000,000 minted here;\n//     2. deploys the hook with CREATE2 at the creator's mined salt, on an address carrying exactly\n//        the flags 0x20CC (nonce 2), told in advance where the Dreams will be, with the creator's\n//        hook fee and the launchpad's cut, both fixed in it for ever;\n//     3. deploys the Dreams (nonce 3), which MUST land exactly there;\n//     4. initializes the ETH / token pool one unit of price inside the opening tick. The pool is\n//        closed: it holds no liquidity, so the hook refuses every swap, and nobody else can add\n//        liquidity at or below its price, which takes the token, and all of it is here.\n//   open, OWNER's one call, puts the band in: every token, from the opening price up (ticks\n//   -887220 to the opening tick), its liquidity computed here from the whole supply, so no\n//   launch can keep a share back. What rounding leaves over goes to OWNER: dust. A creator who\n//   asks for it has the launchpad call open in the launch transaction itself instead.\n//\n//   Expected result. The LP position stays here for ever; nothing here can move, withdraw or burn\n//   it. collect, which anyone may call, pays OWNER the band's LP fees at the base 0.30% rate and\n//   destroys what the mind charged above it (the token burned, the ether sent to DEAD), exactly\n//   as ENGRAM's Launcher does.\n//\n//   Hazards. If OWNER never opens, nothing ever trades. If OWNER ever refuses ether, collect\n//   reverts and the fees wait in the position. The Dreams' clock starts at setup.\n//\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\nimport \"../engram/V4Types.sol\";\n\ninterface ITokenLike {\n    function balanceOf(address) external view returns (uint256);\n    function transfer(address, uint256) external returns (bool);\n    function approve(address, uint256) external returns (bool);\n    function burn(uint256) external;\n}\n\ninterface IFeeSums {\n    function feeSums() external view returns (uint256 ethAtBase, uint256 ethCharged, uint256 tokAtBase, uint256 tokCharged);\n}\n\n/// @notice What every launch is built from, fixed by the launchpad at its deployment: the three blueprints, the Beacon,\n///         and the two that share the launchpad's cut.\nstruct Genome {\n    address token;\n    address hook;\n    address dreams;\n    address beacon;\n    address launchpad;\n    address lysosome;\n}\n\n/// @title  The Niche\n/// @notice One launch: deploys its contracts and its closed pool, opens it on the creator's word, then keeps the launch\n///         liquidity for ever and pays its LP fees to the creator at the base rate.\ncontract Niche {\n    address public constant POOL_MANAGER = 0x000000000004444c5dc75cB358380D2e3dE08A90;\n    address public constant POSITION_MANAGER = 0xbD216513d74C8cf14cf4747E6AaA6420FF64ee9e;\n    address public constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;\n    /// @notice Where the ether the mind charged above the base fee goes: no key is known for it.\n    address public constant DEAD = 0x000000000000000000000000000000000000dEaD;\n    uint256 public constant SUPPLY = 1_000_000_000 ether;\n    int24 internal constant MIN_TICK = -887220;\n\n    /// @notice The launchpad that deployed this Niche; only it may call setup.\n    address public immutable FACTORY;\n    /// @notice The creator: opens the pool, once; receives the token's hook fee share and its LP fees at the base rate.\n    address public immutable OWNER;\n\n    address public TOKEN;\n    address public HOOK;\n    address public DREAMS;\n    int24 public OPENING_TICK;\n    uint128 public LIQUIDITY;\n\n    // the hook's fee tallies at the last collect: ether side, and token side (at base, low 128 | charged, high 128)\n    uint256 internal sums;\n    uint256 internal sumsTok;\n    /// @notice The launch position's token id in the PositionManager, set once by open; zero while the pool is closed.\n    uint256 public POSITION;\n\n    event Deployed(address indexed token, address indexed hook, address indexed dreams, bytes32 poolId, int24 openingTick, address owner);\n    event Launched(address indexed token, address indexed hook, address indexed dreams, bytes32 poolId, int24 openingTick, uint128 liquidity, uint256 position, address owner);\n    event Collected(address indexed to, uint256 ethPaid, uint256 tokPaid, uint256 ethBurned, uint256 tokBurned);\n\n    error NotFactory();\n    error AlreadySet();\n    error NotSet();\n    error WrongDreams(address dreams);\n    error WrongHook(address hook);\n    error OffSpacing(int24 tick);\n    error OffRange(int24 tick);\n    error CreateFailed();\n    error ZeroAddress();\n    error NotLocked();\n    error NotPoolManager();\n    error SendFailed(address to);\n    error NotOwner();\n    error AlreadyOpen();\n    error NotOpen();\n\n    constructor(address owner) {\n        if (owner == address(0)) revert ZeroAddress();\n        FACTORY = msg.sender;\n        OWNER = owner;\n    }\n\n    /// @notice Deploys the launch: token, hook, Dreams, and the pool, closed. The launchpad calls it once, in the\n    ///         transaction that deploys this Niche.\n    function setup(\n        Genome calldata bp,\n        string calldata name,\n        string calldata symbol,\n        int24 openingTick,\n        uint256 hookBps,\n        uint256 launchpadBps,\n        bytes32 hookSalt\n    ) external {\n        if (msg.sender != FACTORY) revert NotFactory();\n        if (TOKEN != address(0)) revert AlreadySet();\n        if (openingTick % 60 != 0) revert OffSpacing(openingTick);\n        if (openingTick - 60 <= MIN_TICK || openingTick + 60 > -MIN_TICK) revert OffRange(openingTick);\n\n        address token = _create(_initCode(bp.token, abi.encode(name, symbol)), 0, false);\n        address dreamsAt = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), address(this), bytes1(0x03))))));\n        address hook = _create(\n            _initCode(bp.hook, abi.encode(POOL_MANAGER, token, dreamsAt, OWNER, openingTick, bp.launchpad, bp.lysosome, hookBps, launchpadBps)),\n            hookSalt,\n            true\n        );\n        if (uint160(hook) & 0x3FFF != 0x20CC) revert WrongHook(hook);\n        address dreams = _create(_initCode(bp.dreams, abi.encode(hook, bp.beacon)), 0, false);\n        if (dreams != dreamsAt) revert WrongDreams(dreams);\n\n        PoolKey memory key = PoolKey(Currency.wrap(address(0)), Currency.wrap(token), LPFee.DYNAMIC, 60, IHooks(hook));\n        uint160 top = TickMath.getSqrtPriceAtTick(openingTick);\n        IPoolManager(POOL_MANAGER).initialize(key, top - 1);\n\n        TOKEN = token;\n        HOOK = hook;\n        DREAMS = dreams;\n        OPENING_TICK = openingTick;\n        // the band's liquidity for the whole supply, from the opening price down to MIN_TICK\n        LIQUIDITY = uint128(FullMath.mulDiv(SUPPLY, 1 << 96, uint256(top) - TickMath.getSqrtPriceAtTick(MIN_TICK)));\n        emit Deployed(token, hook, dreams, PoolState.toId(key), openingTick, OWNER);\n    }\n\n    /// @notice The second half of the launch: puts every token in the pool, in one band, with the one wei it owes (any\n    ///         more ether sent goes back to OWNER). Only OWNER may call it, or the launchpad inside the launch\n    ///         transaction, and only once; until then nothing can trade.\n    function open() external payable {\n        if (msg.sender != OWNER && msg.sender != FACTORY) revert NotOwner();\n        if (TOKEN == address(0)) revert NotSet();\n        if (POSITION != 0) revert AlreadyOpen();\n        address token = TOKEN;\n        PoolKey memory key = PoolKey(Currency.wrap(address(0)), Currency.wrap(token), LPFee.DYNAMIC, 60, IHooks(HOOK));\n        uint256 position = IPositionManager(POSITION_MANAGER).nextTokenId();\n        POSITION = position;\n        ITokenLike(token).approve(PERMIT2, type(uint256).max);\n        IPermit2Approve(PERMIT2).approve(token, POSITION_MANAGER, type(uint160).max, type(uint48).max);\n        bytes[] memory params = new bytes[](3);\n        params[0] = abi.encode(key, MIN_TICK, OPENING_TICK, uint256(LIQUIDITY), uint128(msg.value), uint128(SUPPLY), address(this), bytes(\"\"));\n        params[1] = abi.encode(key.currency0, key.currency1);\n        params[2] = abi.encode(key.currency0, OWNER);\n        IPositionManager(POSITION_MANAGER).modifyLiquidities{value: msg.value}(abi.encode(hex\"020d14\", params), block.timestamp);\n        if (IPositionManager(POSITION_MANAGER).ownerOf(position) != address(this)) revert NotLocked();\n\n        uint256 left = ITokenLike(token).balanceOf(address(this));\n        if (left != 0) ITokenLike(token).transfer(OWNER, left);\n        emit Launched(token, HOOK, DREAMS, PoolState.toId(key), OPENING_TICK, LIQUIDITY, position, OWNER);\n    }\n\n    /// @notice Collects the locked position's accrued LP fees and pays OWNER, in each currency, what the base 0.30% fee\n    ///         would have earned on the same trades; the ether above that goes to DEAD and the tokens above it are\n    ///         burned. Anyone may call it, once the pool is open.\n    function collect() external returns (uint256 ethPaid, uint256 tokPaid, uint256 ethBurned, uint256 tokBurned) {\n        uint256 position = POSITION;\n        if (position == 0) revert NotOpen();\n        address token = TOKEN;\n        (uint256 eb, uint256 ec, uint256 tb, uint256 tc) = IFeeSums(HOOK).feeSums();\n        uint256 seen = sums;\n        uint256 seenTok = sumsTok;\n        sums = eb | (ec << 128);\n        sumsTok = tb | (tc << 128);\n\n        uint256 eth0 = address(this).balance;\n        uint256 tok0 = ITokenLike(token).balanceOf(address(this));\n        bytes[] memory params = new bytes[](2);\n        params[0] = abi.encode(position, uint256(0), uint128(0), uint128(0), bytes(\"\"));\n        params[1] = abi.encode(Currency.wrap(address(0)), Currency.wrap(token), address(this));\n        IPositionManager(POSITION_MANAGER).modifyLiquidities(abi.encode(hex\"0111\", params), block.timestamp);\n        uint256 eth = address(this).balance - eth0;\n        uint256 tok = ITokenLike(token).balanceOf(address(this)) - tok0;\n\n        ethPaid = _atBase(eth, eb - (seen & type(uint128).max), ec - (seen >> 128));\n        tokPaid = _atBase(tok, tb - (seenTok & type(uint128).max), tc - (seenTok >> 128));\n        ethBurned = eth - ethPaid;\n        tokBurned = tok - tokPaid;\n        if (tokPaid != 0) ITokenLike(token).transfer(OWNER, tokPaid);\n        if (tokBurned != 0) ITokenLike(token).burn(tokBurned);\n        if (ethPaid != 0) _send(OWNER, ethPaid);\n        if (ethBurned != 0) _send(DEAD, ethBurned);\n        emit Collected(OWNER, ethPaid, tokPaid, ethBurned, tokBurned);\n    }\n\n    /// @notice The hook's fee tallies as they stood at the last collect. The next collect pays OWNER each currency's fees\n    ///         x (atBase now - atBase here) / (charged now - charged here), at most all of them.\n    function lastSums() external view returns (uint256 ethAtBase, uint256 ethCharged, uint256 tokAtBase, uint256 tokCharged) {\n        return (sums & type(uint128).max, sums >> 128, sumsTok & type(uint128).max, sumsTok >> 128);\n    }\n\n    /// @dev A blueprint's code is one STOP and then an init code; this returns that init code with `args` appended.\n    function _initCode(address blueprint, bytes memory args) private view returns (bytes memory c) {\n        uint256 n = blueprint.code.length;\n        if (n < 2) revert CreateFailed();\n        n -= 1;\n        c = new bytes(n + args.length);\n        assembly (\"memory-safe\") {\n            extcodecopy(blueprint, add(c, 32), 1, n)\n            mcopy(add(add(c, 32), n), add(args, 32), mload(args))\n        }\n    }\n\n    function _create(bytes memory c, bytes32 salt, bool two) private returns (address a) {\n        assembly (\"memory-safe\") {\n            switch two\n            case 0 { a := create(0, add(c, 32), mload(c)) }\n            default { a := create2(0, add(c, 32), mload(c), salt) }\n        }\n        if (a == address(0)) revert CreateFailed();\n    }\n\n    /// @dev The share of `amount` the base fee would have earned: amount x atBase / charged, never more than amount.\n    function _atBase(uint256 amount, uint256 atBase, uint256 charged) private pure returns (uint256) {\n        return charged <= atBase ? amount : (amount * atBase) / charged;\n    }\n\n    function _send(address to, uint256 amount) private {\n        (bool ok,) = to.call{value: amount}(\"\");\n        if (!ok) revert SendFailed(to);\n    }\n\n    /// @dev The position's ether fees arrive from the PoolManager during collect; nothing else is accepted.\n    receive() external payable {\n        if (msg.sender != POOL_MANAGER) revert NotPoolManager();\n    }\n}\n"},"engram/V4Types.sol":{"content":"// ENGRAM . a mind in the pool . Ethereum\n// engramv4.xyz . x.com/engram_v4\n//\n//   ATLAS OF ENGRAM                                                        PLATE 0\n//   THE SKELETON: PARTS OF UNISWAP V4 IT IS BUILT ON\n//\n//   Description. The bones the other plates hang from, cut down to what they use: the swap and\n//   liquidity structs, Currency, BalanceDelta and BeforeSwapDelta, PoolKey, the PoolManager and\n//   PositionManager interfaces, the hook permission check, slot0 and liquidity read straight out\n//   of the PoolManager's storage (pools live at slot 6), Uniswap's TickMath, and a 512-bit\n//   mulDiv. Never deployed on its own; compiled into the contracts that use it.\n//\n//   Relations. No periphery import, so nothing here drifts when a package moves.\n//\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.28;\n\nstruct SwapParams { bool zeroForOne; int256 amountSpecified; uint160 sqrtPriceLimitX96; }\nstruct ModifyLiquidityParams { int24 tickLower; int24 tickUpper; int256 liquidityDelta; bytes32 salt; }\n\ntype Currency is address;\n\ntype BalanceDelta is int256;\nlibrary BalanceDeltaLibrary {\n    function amount0(BalanceDelta self) internal pure returns (int128) { return int128(BalanceDelta.unwrap(self) >> 128); }\n    function amount1(BalanceDelta self) internal pure returns (int128) { return int128(BalanceDelta.unwrap(self)); }\n}\nusing BalanceDeltaLibrary for BalanceDelta global;\n\ntype BeforeSwapDelta is int256;\nlibrary BeforeSwapDeltaLibrary { BeforeSwapDelta public constant ZERO_DELTA = BeforeSwapDelta.wrap(0); }\n\nfunction toBeforeSwapDelta(int128 deltaSpecified, int128 deltaUnspecified) pure returns (BeforeSwapDelta) {\n    return BeforeSwapDelta.wrap((int256(deltaSpecified) << 128) | int256(uint256(uint128(deltaUnspecified))));\n}\n\nlibrary SafeCast {\n    function toInt128(uint256 x) internal pure returns (int128) {\n        require(x <= uint256(uint128(type(int128).max)), \"toInt128 overflow\");\n        return int128(uint128(x));\n    }\n}\n\n/// @dev The LP-fee flags of a dynamic-fee pool: the key carries DYNAMIC, a hook's per-swap answer carries OVERRIDE.\nlibrary LPFee {\n    uint24 internal constant DYNAMIC = 0x800000;\n    uint24 internal constant OVERRIDE = 0x400000;\n}\n\ninterface IHooks {\n    function beforeInitialize(address, PoolKey calldata, uint160) external returns (bytes4);\n    function afterInitialize(address, PoolKey calldata, uint160, int24) external returns (bytes4);\n    function beforeAddLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata) external returns (bytes4);\n    function afterAddLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata) external returns (bytes4, BalanceDelta);\n    function beforeRemoveLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata) external returns (bytes4);\n    function afterRemoveLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata) external returns (bytes4, BalanceDelta);\n    function beforeSwap(address, PoolKey calldata, SwapParams calldata, bytes calldata) external returns (bytes4, BeforeSwapDelta, uint24);\n    function afterSwap(address, PoolKey calldata, SwapParams calldata, BalanceDelta, bytes calldata) external returns (bytes4, int128);\n    function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external returns (bytes4);\n    function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external returns (bytes4);\n}\n\nstruct PoolKey { Currency currency0; Currency currency1; uint24 fee; int24 tickSpacing; IHooks hooks; }\n\ninterface IPoolManager {\n    function initialize(PoolKey memory key, uint160 sqrtPriceX96) external returns (int24);\n    function swap(PoolKey memory key, SwapParams memory params, bytes calldata hookData) external returns (BalanceDelta);\n    function modifyLiquidity(PoolKey memory key, ModifyLiquidityParams memory params, bytes calldata hookData)\n        external returns (BalanceDelta callerDelta, BalanceDelta feesAccrued);\n    function take(Currency currency, address to, uint256 amount) external;\n    function settle() external payable returns (uint256);\n    function sync(Currency currency) external;\n    function unlock(bytes calldata data) external returns (bytes memory);\n    /// @notice ERC-6909 claims: fees are minted as claims inside the swap and burned for ether later.\n    function mint(address to, uint256 id, uint256 amount) external;\n    function burn(address from, uint256 id, uint256 amount) external;\n    function balanceOf(address owner, uint256 id) external view returns (uint256);\n    function extsload(bytes32 slot) external view returns (bytes32);\n}\n\ninterface IUnlockCallback {\n    function unlockCallback(bytes calldata data) external returns (bytes memory);\n}\n\ninterface IPositionManager {\n    function modifyLiquidities(bytes calldata unlockData, uint256 deadline) external payable;\n    function nextTokenId() external view returns (uint256);\n    function ownerOf(uint256 tokenId) external view returns (address);\n    function getPositionLiquidity(uint256 tokenId) external view returns (uint128);\n}\n\ninterface IPermit2Approve {\n    function approve(address token, address spender, uint160 amount, uint48 expiration) external;\n}\n\nlibrary Hooks {\n    uint160 public constant BEFORE_INITIALIZE_FLAG               = 1 << 13;\n    uint160 public constant AFTER_INITIALIZE_FLAG                = 1 << 12;\n    uint160 public constant BEFORE_ADD_LIQUIDITY_FLAG            = 1 << 11;\n    uint160 public constant AFTER_ADD_LIQUIDITY_FLAG             = 1 << 10;\n    uint160 public constant BEFORE_REMOVE_LIQUIDITY_FLAG         = 1 << 9;\n    uint160 public constant AFTER_REMOVE_LIQUIDITY_FLAG          = 1 << 8;\n    uint160 public constant BEFORE_SWAP_FLAG                     = 1 << 7;\n    uint160 public constant AFTER_SWAP_FLAG                      = 1 << 6;\n    uint160 public constant BEFORE_DONATE_FLAG                   = 1 << 5;\n    uint160 public constant AFTER_DONATE_FLAG                    = 1 << 4;\n    uint160 public constant BEFORE_SWAP_RETURNS_DELTA_FLAG       = 1 << 3;\n    uint160 public constant AFTER_SWAP_RETURNS_DELTA_FLAG        = 1 << 2;\n    uint160 public constant AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG    = 1 << 1;\n    uint160 public constant AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG = 1 << 0;\n    uint160 public constant ALL_HOOK_MASK = 0x3FFF;\n\n    struct Permissions {\n        bool beforeInitialize; bool afterInitialize;\n        bool beforeAddLiquidity; bool afterAddLiquidity;\n        bool beforeRemoveLiquidity; bool afterRemoveLiquidity;\n        bool beforeSwap; bool afterSwap;\n        bool beforeDonate; bool afterDonate;\n        bool beforeSwapReturnDelta; bool afterSwapReturnDelta;\n        bool afterAddLiquidityReturnDelta; bool afterRemoveLiquidityReturnDelta;\n    }\n\n    function permissionsToFlags(Permissions memory p) internal pure returns (uint160 f) {\n        if (p.beforeInitialize) f |= BEFORE_INITIALIZE_FLAG;\n        if (p.afterInitialize) f |= AFTER_INITIALIZE_FLAG;\n        if (p.beforeAddLiquidity) f |= BEFORE_ADD_LIQUIDITY_FLAG;\n        if (p.afterAddLiquidity) f |= AFTER_ADD_LIQUIDITY_FLAG;\n        if (p.beforeRemoveLiquidity) f |= BEFORE_REMOVE_LIQUIDITY_FLAG;\n        if (p.afterRemoveLiquidity) f |= AFTER_REMOVE_LIQUIDITY_FLAG;\n        if (p.beforeSwap) f |= BEFORE_SWAP_FLAG;\n        if (p.afterSwap) f |= AFTER_SWAP_FLAG;\n        if (p.beforeDonate) f |= BEFORE_DONATE_FLAG;\n        if (p.afterDonate) f |= AFTER_DONATE_FLAG;\n        if (p.beforeSwapReturnDelta) f |= BEFORE_SWAP_RETURNS_DELTA_FLAG;\n        if (p.afterSwapReturnDelta) f |= AFTER_SWAP_RETURNS_DELTA_FLAG;\n        if (p.afterAddLiquidityReturnDelta) f |= AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG;\n        if (p.afterRemoveLiquidityReturnDelta) f |= AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG;\n    }\n\n    function validateHookPermissions(IHooks self, Permissions memory perms) internal pure {\n        require((uint160(address(self)) & ALL_HOOK_MASK) == permissionsToFlags(perms), \"Hooks: address flags mismatch\");\n    }\n}\n\nabstract contract BaseHook is IHooks {\n    error NotPoolManager();\n    IPoolManager public immutable poolManager;\n    constructor(IPoolManager _pm) { poolManager = _pm; Hooks.validateHookPermissions(IHooks(address(this)), getHookPermissions()); }\n    modifier onlyPoolManager() { if (msg.sender != address(poolManager)) revert NotPoolManager(); _; }\n    function getHookPermissions() public pure virtual returns (Hooks.Permissions memory);\n\n    function beforeInitialize(address, PoolKey calldata, uint160) external virtual override returns (bytes4) { revert(\"ni\"); }\n    function afterInitialize(address, PoolKey calldata, uint160, int24) external virtual override returns (bytes4) { revert(\"ni\"); }\n    function beforeAddLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata) external virtual override returns (bytes4) { revert(\"ni\"); }\n    function afterAddLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata) external virtual override returns (bytes4, BalanceDelta) { revert(\"ni\"); }\n    function beforeRemoveLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata) external virtual override returns (bytes4) { revert(\"ni\"); }\n    function afterRemoveLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata) external virtual override returns (bytes4, BalanceDelta) { revert(\"ni\"); }\n    function beforeSwap(address, PoolKey calldata, SwapParams calldata, bytes calldata) external virtual override returns (bytes4, BeforeSwapDelta, uint24) { revert(\"ni\"); }\n    function afterSwap(address, PoolKey calldata, SwapParams calldata, BalanceDelta, bytes calldata) external virtual override returns (bytes4, int128) { revert(\"ni\"); }\n    function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external virtual override returns (bytes4) { revert(\"ni\"); }\n    function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external virtual override returns (bytes4) { revert(\"ni\"); }\n}\n\n/// @dev The PoolManager keeps `mapping(PoolId => Pool.State) _pools` at storage slot 6. Word 0 of a pool is slot0:\n///      sqrtPriceX96 (160) | tick (24) | protocolFee (24) | lpFee (24). Word 3 is the in-range liquidity.\nlibrary PoolState {\n    uint256 internal constant POOLS_SLOT = 6;\n\n    function slot0(IPoolManager pm, bytes32 poolId) internal view returns (uint160 sqrtPriceX96, int24 tick) {\n        bytes32 data = pm.extsload(keccak256(abi.encodePacked(poolId, POOLS_SLOT)));\n        assembly (\"memory-safe\") {\n            sqrtPriceX96 := and(data, 0xffffffffffffffffffffffffffffffffffffffff)\n            tick := signextend(2, shr(160, data))\n        }\n    }\n\n    function liquidity(IPoolManager pm, bytes32 poolId) internal view returns (uint128) {\n        bytes32 base = keccak256(abi.encodePacked(poolId, POOLS_SLOT));\n        return uint128(uint256(pm.extsload(bytes32(uint256(base) + 3))));\n    }\n\n    function toId(PoolKey memory key) internal pure returns (bytes32) {\n        return keccak256(abi.encode(key));\n    }\n}\n\n/// @dev Remco Bloemen's 512-bit mulDiv, floor(a * b / d), reverting on overflow or d == 0.\nlibrary FullMath {\n    function mulDiv(uint256 a, uint256 b, uint256 d) internal pure returns (uint256 result) {\n        unchecked {\n            uint256 prod0;\n            uint256 prod1;\n            assembly (\"memory-safe\") {\n                let mm := mulmod(a, b, not(0))\n                prod0 := mul(a, b)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n            if (prod1 == 0) { require(d > 0); assembly (\"memory-safe\") { result := div(prod0, d) } return result; }\n            require(d > prod1);\n            uint256 remainder;\n            assembly (\"memory-safe\") {\n                remainder := mulmod(a, b, d)\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n            uint256 twos = (0 - d) & d;\n            assembly (\"memory-safe\") {\n                d := div(d, twos)\n                prod0 := div(prod0, twos)\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n            prod0 |= prod1 * twos;\n            uint256 inv = (3 * d) ^ 2;\n            inv *= 2 - d * inv;\n            inv *= 2 - d * inv;\n            inv *= 2 - d * inv;\n            inv *= 2 - d * inv;\n            inv *= 2 - d * inv;\n            inv *= 2 - d * inv;\n            result = prod0 * inv;\n        }\n    }\n}\n\n/// @dev Uniswap's tick <-> sqrtPrice map. sqrtPriceX96 = sqrt(1.0001^tick) * 2^96.\nlibrary TickMath {\n    int24 internal constant MIN_TICK = -887272;\n    int24 internal constant MAX_TICK = 887272;\n\n    error InvalidTick();\n\n    function getSqrtPriceAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {\n        unchecked {\n            uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n            if (absTick > uint256(int256(MAX_TICK))) revert InvalidTick();\n\n            uint256 ratio = absTick & 0x1 != 0\n                ? 0xfffcb933bd6fad37aa2d162d1a594001\n                : 0x100000000000000000000000000000000;\n            if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;\n            if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;\n            if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;\n            if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;\n            if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;\n            if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;\n            if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;\n            if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;\n            if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;\n            if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;\n            if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;\n            if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;\n            if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;\n            if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;\n            if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;\n            if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;\n            if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;\n            if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;\n            if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;\n\n            if (tick > 0) ratio = type(uint256).max / ratio;\n            sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));\n        }\n    }\n}\n"},"engram/Cortex.sol":{"content":"// ENGRAM . a mind in the pool . Ethereum\n// engramv4.xyz . x.com/engram_v4\n//\n//   ATLAS OF ENGRAM                                                        PLATE III\n//   THE CORTEX: HOW A MIND THINKS\n//\n//   Description. The arithmetic every mind runs, shared by the pool's hook (PLATE II), which\n//   asks the living mind for each trade's fee, and by the Dreams (PLATE IV), which grade\n//   candidate minds on recorded trades. One copy of the code, so a mind is graded by exactly\n//   the function that will later price trades with it.\n//\n//   Anatomy of a mind. 109 bytes, kept as the code of a small data contract behind one STOP:\n//       [0]        version, 1\n//       [1]        s1, the right shift after each hidden unit's ReLU, 0 to 24\n//       [2]        s2, the right shift of the output, 0 to 24\n//       [3..82]    W1, eight rows of ten int8 weights; row j feeds hidden unit j\n//       [83..98]   b1, eight int16 biases, big-endian\n//       [99..106]  W2, eight int8 output weights\n//       [107..108] b2, the int16 output bias, big-endian\n//   A perceptron of ten inputs, eight hidden units and one output:\n//       h_j  = b1_j + sum_i W1_ji * f_i\n//       out  = b2 + sum_j W2_j * (max(h_j, 0) >> s1)\n//       pred = out >> s2                      (arithmetic shifts, rounding toward minus infinity)\n//   pred is the expected markout of the trade, in basis points: how far the price will keep going\n//   in the trade's own direction over the next eight trades, from the price the trade leaves. The\n//   default mind, address zero, predicts 0 for everything.\n//\n//   Anatomy of a trace. One uint256 per trade, written by the hook once the trade is eight trades\n//   old and its markout is known:\n//       bits   0..79   the ten features, int8 each, feature i at bits 8i..8i+7\n//       bits  80..95   the label: the realised markout in basis points, int16, clipped to +-200\n//       bits  96..103  the weight in sixteenths of a unit: ether / 0.003125, at most 48 (from 0.15 ETH)\n//       bits 104..135  the trade's timestamp\n//\n//   Function. loss = sum over traces of weight * (label - clip(pred))^2, with pred clipped to the\n//   same +-200 as the label, over the traces of trades made at or after a given moment (the Dreams\n//   pass the moment submissions closed). Lower is better. The default mind's loss is the sum of\n//   weight * label^2.\n//\n//   Lesions. A mind sees only what the hook can see on chain. Nothing here learns: learning\n//   happens off chain, in public, and arrives as a candidate in the Dreams.\n//\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\nlibrary Cortex {\n    uint256 internal constant MIND_BYTES = 109;\n    int256 internal constant CLIP = 200;\n    uint256 internal constant F_MASK = (1 << 80) - 1;\n    uint256 internal constant W_MAX = 48;\n\n    error BadMind();\n\n    /// @dev A candidate's bytes must be a version 1 mind with sane shifts; nothing else is checked, or needed.\n    function check(bytes calldata mind) internal pure {\n        if (mind.length != MIND_BYTES || uint8(mind[0]) != 1 || uint8(mind[1]) > 24 || uint8(mind[2]) > 24) revert BadMind();\n    }\n\n    /// @dev The init code of a data contract whose code is one STOP followed by `data`.\n    function dataContract(bytes calldata data) internal pure returns (bytes memory) {\n        uint256 n = data.length + 1;\n        return abi.encodePacked(hex\"61\", uint16(n), hex\"80600a3d393df3\", hex\"00\", data);\n    }\n\n    /// @dev A mind's 109 bytes copied into fresh memory. 0 for the default mind.\n    function load(address mind) internal view returns (uint256 m) {\n        if (mind == address(0)) return 0;\n        assembly (\"memory-safe\") {\n            m := mload(0x40)\n            extcodecopy(mind, m, 1, 109)\n            mstore(0x40, add(m, 0xa0))\n        }\n    }\n\n    /// @dev One forward pass. `f` carries the ten int8 features in its low 80 bits: feature i is byte 31 - i of the word.\n    ///      The ten products of a hidden unit are written out, not looped: grading runs this 512 times per segment\n    ///      per mind, so the loop overhead was most of its gas.\n    function forward(uint256 m, uint256 f) internal pure returns (int256 out) {\n        if (m == 0) return 0;\n        assembly (\"memory-safe\") {\n            let head := mload(m)\n            let s1 := byte(1, head)\n            let s2 := byte(2, head)\n            out := signextend(1, shr(240, mload(add(m, 107))))\n            let w2 := mload(add(m, 99))\n            for { let j := 0 } lt(j, 8) { j := add(j, 1) } {\n                let row := mload(add(m, add(3, mul(10, j))))\n                let h := signextend(1, shr(240, mload(add(m, add(83, shl(1, j))))))\n                h := add(h, mul(signextend(0, byte(0, row)), signextend(0, byte(31, f))))\n                h := add(h, mul(signextend(0, byte(1, row)), signextend(0, byte(30, f))))\n                h := add(h, mul(signextend(0, byte(2, row)), signextend(0, byte(29, f))))\n                h := add(h, mul(signextend(0, byte(3, row)), signextend(0, byte(28, f))))\n                h := add(h, mul(signextend(0, byte(4, row)), signextend(0, byte(27, f))))\n                h := add(h, mul(signextend(0, byte(5, row)), signextend(0, byte(26, f))))\n                h := add(h, mul(signextend(0, byte(6, row)), signextend(0, byte(25, f))))\n                h := add(h, mul(signextend(0, byte(7, row)), signextend(0, byte(24, f))))\n                h := add(h, mul(signextend(0, byte(8, row)), signextend(0, byte(23, f))))\n                h := add(h, mul(signextend(0, byte(9, row)), signextend(0, byte(22, f))))\n                if sgt(h, 0) { out := add(out, mul(signextend(0, byte(j, w2)), sar(s1, h))) }\n            }\n            out := sar(s2, out)\n        }\n    }\n\n    function clip(int256 x) internal pure returns (int256) {\n        return x > CLIP ? CLIP : (x < -CLIP ? -CLIP : x);\n    }\n\n    /// @dev The weighted squared error of a loaded mind (0 = the default) over a run of traces, counting only traces of\n    ///      trades made at or after `from`: a mind is graded only on trades that did not exist when it was sealed.\n    function loss(uint256 m, uint256[] calldata traces, uint256 from) internal pure returns (uint256 total) {\n        for (uint256 k; k < traces.length; ++k) {\n            uint256 t = traces[k];\n            if (((t >> 104) & 0xffffffff) < from) continue;\n            int256 y = clip(int256(int16(uint16(t >> 80))));\n            int256 p = m == 0 ? int256(0) : clip(forward(m, t & F_MASK));\n            int256 e = y - p;\n            total += ((t >> 96) & 0xff) * uint256(e * e);\n        }\n    }\n\n    /// @dev floor(log2(x)), 0 for x < 2.\n    function log2(uint256 x) internal pure returns (uint256 r) {\n        assembly (\"memory-safe\") {\n            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))\n            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))\n            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n            r := or(r, shl(4, lt(0xffff, shr(r, x))))\n            r := or(r, shl(3, lt(0xff, shr(r, x))))\n            r := or(r, shl(2, lt(0xf, shr(r, x))))\n            r := or(r, shl(1, lt(0x3, shr(r, x))))\n            r := or(r, lt(0x1, shr(r, x)))\n        }\n    }\n\n    /// @dev A trade's weight in sixteenths, linear in its ether: one sixteenth per 0.003125 ETH (16 at 0.05 ETH), at\n    ///      most 48 (from 0.15 ETH). Linear on purpose: a trade's say in a grade costs fees in proportion, so weight\n    ///      cannot be bought cheaply with many small trades. The hook remembers only trades of 0.005 ETH or more, so\n    ///      every trace weighs at least 1.\n    function weight(uint256 eth) internal pure returns (uint256 w) {\n        w = eth / 3125000000000000;\n        if (w > W_MAX) w = W_MAX;\n    }\n}\n"},"engram/Dreams.sol":{"content":"// ENGRAM . a mind in the pool . Ethereum\n// engramv4.xyz . x.com/engram_v4\n//\n//   ATLAS OF ENGRAM                                                        PLATE IV\n//   THE DREAMS: HOW THE MIND IS REPLACED WHILE IT SLEEPS\n//\n//   Description. Every day is a window of trading. Before a window opens, anyone may submit a\n//   candidate mind for it, sealed: a commitment, a bond of 0.01 ETH, and optionally the mind\n//   itself time-locked to the drand quicknet round published the moment the window closes, so that\n//   anyone can open it then. Once the window opens no candidate can be changed, and none can be\n//   read by anyone but its own trainer until the window closes. Candidates are graded only on\n//   trades made after submissions closed: trades that did not exist when they were sealed.\n//\n//   When the window closes the mind sleeps, and dreams:\n//     1. Reveal (8 hours). Each candidate is opened against its commitment and kept as a data\n//        contract of 109 bytes (PLATE III).\n//     2. Seed. The window's drand signature is checked on chain by the Beacon (PLATE V). Its hash\n//        draws up to four of the segments of 512 traces that both began and ended during the window\n//        (the first segment completed in a window began before it, and is never drawn). The range\n//        of segments is named by the caller and checked against the hook's checkpoint times at both\n//        ends, so it cannot leave out, or invent, a segment.\n//     3. Score (18 hours from the close). Anyone may hand in a drawn segment's 512 traces; they are\n//        checked against the hook's hash chain, and each named candidate, the living mind and the\n//        default mind are graded by the Cortex's loss on those of them that record trades made at or\n//        after the window opened.\n//     4. Wake. A candidate graded on every drawn segment takes over only if its loss is below both\n//        the living mind's and the default's by 3%. A living mind that grades worse than the default\n//        is retired to the default even when nobody challenges it.\n//\n//   The bounty. The winner is paid out of the trainers' pot (0.04% of every trade's ether) by the\n//   improvement it showed: 4 x what the pot took in while the drawable segments were written x\n//   (bar - loss) / bar,\n//   where the bar is the lower of the living and default minds' losses, and never more than the\n//   pot holds; plus the bonds forfeited since the last win. A bond comes back when its candidate is\n//   graded on every drawn segment; one never revealed, or never fully graded, is forfeited.\n//   Winnings and refunds are pulled, never pushed. Any number of candidates may enter a round.\n//\n//   Why these numbers. econ_mind.js, with the contract's own fees, weights and memory floor: a\n//   promotion margin of 3% against both the living mind and the default promoted 12 minds, 2 of\n//   which then graded worse than the default and were retired at the next round. Pools too quiet to\n//   complete two segments in a window cannot be graded at all. Faking improvement (a trainer with no skill planting trades only its own weights can\n//   predict, back to back so that they push each other's labels) costs a fixed amount per unit of\n//   improvement; this bounty pays at most half of it, so faking loses at every size, in every pool\n//   shape tested (the tightest, a busy pool: faking costs 2.09 times what it pays).\n//\n//   Lesions.\n//     - A sealed mind whose time-lock blob does not open can only be revealed by its submitter. It\n//       may then be withheld, at the price of its bond.\n//     - Rounds settle in order. Each needs someone to seed, score and finalize it; all are open to\n//       anyone. A round that is not seeded, or whose drawn segments are not all graded for the\n//       default mind, is void: nothing changes and revealed candidates get their bonds back.\n//     - A window needs at least two completed segments (about 1,024 remembered trades) to be graded.\n//       A quieter pool keeps its mind.\n//     - Grading is by sample: up to four segments of 512 trades, not every trade.\n//     - A living mind is retired only when someone grades it in a round. Grading is open to anyone,\n//       but if nobody grades the living mind, it stays.\n//     - The owner receives the hook's 1% and the locked band's LP fees at the base rate, so it trades\n//       in this pool at almost no cost. It could therefore fake an improvement more cheaply than\n//       anyone else; the most that wins is the pot. A mind that charges more earns it nothing: the\n//       Launcher (PLATE VI) pays it the base rate and destroys the rest.\n//     - No owner, no settings, no pause, no upgrade.\n//\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\nimport \"./Cortex.sol\";\n\ninterface IHippocampusForDreams {\n    function champion() external view returns (address);\n    function segments() external view returns (uint256 done, uint256 open);\n    function ckHead(uint256 k) external view returns (bytes32);\n    function ckTime(uint256 k) external view returns (uint256);\n    function ckPot(uint256 k) external view returns (uint256);\n    function pot() external view returns (uint256 owed, uint256 cumulative);\n    function setChampion(address mind) external;\n    function payPot(address to, uint256 amount) external;\n}\n\ninterface IBeacon {\n    function randomness(uint64 round, bytes calldata signature) external view returns (bytes32);\n}\n\n/// @title  The Dreams\n/// @notice Daily rounds in which sealed candidate minds are graded on trades that did not exist when they were\n///         submitted. The best, if it beats the living mind and the default by 3%, becomes the pool's mind.\ncontract Dreams {\n    uint256 public constant DAY = 1 days;\n    uint256 public constant REVEAL_FOR = 8 hours;\n    uint256 public constant SCORE_FOR = 18 hours;\n    uint256 public constant BOND = 0.01 ether;\n    uint256 public constant SAMPLES = 4;\n    uint256 public constant SEG = 512;\n    uint256 public constant MARGIN_BPS = 300;\n    uint256 public constant BOUNTY_PER_UNIT = 4;   // bounty = 4 x the window's pot x the winner's relative improvement\n    uint256 public constant LOCK_BYTES = 277;     // round (8) | U (96) | V (32) | W: the mind and its salt (141)\n    uint256 public constant QUICKNET_GENESIS = 1692803367;\n\n    IHippocampusForDreams public immutable HOOK;\n    IBeacon public immutable BEACON;\n    uint256 public immutable GENESIS;\n\n    struct Entry {\n        address trainer;\n        bool revealed;\n        uint8 mask;          // drawn segments graded\n        bool refunded;\n        address mind;\n        bytes32 commitment;\n        uint256 loss;\n    }\n\n    struct Round {\n        bool seeded;\n        bool finalized;\n        bool void_;\n        uint8 n;             // segments drawn\n        uint8 champMask;\n        uint8 defaultMask;\n        uint32 kFirst;\n        uint32 kLast;\n        uint32[4] picks;\n        address champion;    // the living mind when the round was seeded\n        bytes32 seed;\n        uint256 champLoss;\n        uint256 defaultLoss;\n        uint32 revealed;     // candidates opened\n        uint32 graded;       // candidates graded on every drawn segment\n        uint32 bestId;       // the best of those so far (lowest loss, then lowest id)\n        uint256 bestLoss;\n    }\n\n    mapping(uint256 => Entry[]) internal entries;\n    mapping(uint256 => Round) internal rounds;\n    /// @notice Every round up to and including this one is settled. Round 0 is the launch day.\n    uint256 public finalizedThrough;\n    /// @notice Bonds forfeited and not yet paid to a winner.\n    uint256 public carry;\n    /// @notice Ether a trainer may pull: bounties and refunded bonds.\n    mapping(address => uint256) public owed;\n\n    event Submitted(uint256 indexed round, uint256 indexed id, address indexed trainer, bytes32 commitment, bytes locked);\n    event Revealed(uint256 indexed round, uint256 indexed id, address mind);\n    event Seeded(uint256 indexed round, bytes32 seed, uint256 kFirst, uint256 kLast, uint32[4] picks, uint256 drawn);\n    event Graded(uint256 indexed round, uint256 indexed pick, uint256 segment);\n    event Woke(uint256 indexed round, address champion, address winner, uint256 bounty, bool retired, bool void_);\n    event Pulled(address indexed to, uint256 eth);\n\n    error NotOpen();\n    error WrongBond();\n    error WrongLock();\n    error AlreadyDone();\n    error WrongSecret();\n    error WrongRange();\n    error WrongChain();\n    error OutOfOrder();\n    error NotYet();\n    error Nothing();\n    error PayFailed();\n\n    constructor(IHippocampusForDreams hook, IBeacon beacon) {\n        HOOK = hook;\n        BEACON = beacon;\n        GENESIS = block.timestamp;\n    }\n\n    receive() external payable {}\n\n    // ------------------------------------------------------------------ the clock\n\n    /// @notice The current day: round r's window is [GENESIS + r days, GENESIS + (r + 1) days).\n    function clock() public view returns (uint256) {\n        return (block.timestamp - GENESIS) / DAY;\n    }\n\n    function window(uint256 r) public view returns (uint256 start, uint256 end) {\n        start = GENESIS + r * DAY;\n        end = start + DAY;\n    }\n\n    /// @notice The drand quicknet round a candidate for round r is time-locked to: the first one published at or\n    ///         after the window closes.\n    function lockRound(uint256 r) public view returns (uint64) {\n        (, uint256 end) = window(r);\n        return uint64((end - QUICKNET_GENESIS + 2) / 3 + 1);\n    }\n\n    function commitmentOf(uint256 r, address trainer, bytes calldata mind, bytes32 salt) public pure returns (bytes32) {\n        return keccak256(abi.encodePacked(r, trainer, mind, salt));\n    }\n\n    // ------------------------------------------------------------------ before the window: seal\n\n    /// @notice Seal a candidate for round r, which must be the next window. `locked` is optional: the mind and its salt\n    ///         time-locked to lockRound(r), so that anyone can reveal it for you.\n    function submit(uint256 r, bytes32 commitment, bytes calldata locked) external payable returns (uint256 id) {\n        if (r != clock() + 1) revert NotOpen();\n        if (msg.value != BOND) revert WrongBond();\n        if (locked.length != 0 && (locked.length != LOCK_BYTES || uint64(bytes8(locked[:8])) != lockRound(r))) revert WrongLock();\n        Entry[] storage es = entries[r];\n        id = es.length;\n        es.push(Entry({ trainer: msg.sender, revealed: false, mask: 0, refunded: false, mind: address(0), commitment: commitment, loss: 0 }));\n        emit Submitted(r, id, msg.sender, commitment, locked);\n    }\n\n    // ------------------------------------------------------------------ after the window: dream\n\n    /// @notice Open a candidate. Anyone may, with the mind and salt (from the submitter, or from its time-lock).\n    function reveal(uint256 r, uint256 id, bytes calldata mind, bytes32 salt) external returns (address m) {\n        (, uint256 end) = window(r);\n        if (block.timestamp < end || block.timestamp >= end + REVEAL_FOR) revert NotOpen();\n        Entry storage e = entries[r][id];\n        if (e.revealed) revert AlreadyDone();\n        if (commitmentOf(r, e.trainer, mind, salt) != e.commitment) revert WrongSecret();\n        Cortex.check(mind);\n        bytes memory code = Cortex.dataContract(mind);\n        assembly (\"memory-safe\") { m := create(0, add(code, 0x20), mload(code)) }\n        if (m == address(0)) revert WrongSecret();\n        e.mind = m;\n        e.revealed = true;\n        rounds[r].revealed += 1;\n        emit Revealed(r, id, m);\n    }\n\n    /// @notice Seed round r with its drand signature and name the window's completed segments, kFirst to kLast (an\n    ///         empty range when none completed). The range is checked against the hook's checkpoint times.\n    function seed(uint256 r, bytes calldata signature, uint32 kFirst, uint32 kLast) external {\n        (uint256 start, uint256 end) = window(r);\n        if (block.timestamp < end) revert NotYet();\n        if (finalizedThrough + 1 < r) revert OutOfOrder();\n        Round storage R = rounds[r];\n        if (R.seeded || R.finalized) revert AlreadyDone();\n        bytes32 rnd = BEACON.randomness(lockRound(r), signature);\n\n        // kFirst is the first segment completed at or after the window opened, kLast the last completed before it\n        // closed; an empty window is kLast = kFirst - 1. Both must lie within the segments that exist, the one before\n        // kFirst (if any) must have completed before the window, and the one after kLast (if any) after it.\n        (uint256 done,) = HOOK.segments();\n        if (kFirst == 0 || kFirst > done + 1 || kLast > done || uint256(kLast) + 1 < kFirst) revert WrongRange();\n        if (kFirst > 1 && HOOK.ckTime(kFirst - 1) >= start) revert WrongRange();\n        if (kFirst <= kLast && (HOOK.ckTime(kFirst) < start || HOOK.ckTime(kLast) >= end)) revert WrongRange();\n        if (kLast < done && HOOK.ckTime(uint256(kLast) + 1) < end) revert WrongRange();\n        // segment kFirst began before the window opened (its predecessor completed before it), so it is never drawn:\n        // only kFirst + 1 .. kLast, which began AND ended inside the window, are. A window needs two completed segments.\n        uint256 n = kLast > kFirst ? uint256(kLast) - kFirst : 0;\n\n        R.seeded = true;\n        R.bestLoss = type(uint256).max;\n        R.seed = rnd;\n        R.kFirst = kFirst;\n        R.kLast = kLast;\n        R.champion = HOOK.champion();\n        uint256 draw = n < SAMPLES ? n : SAMPLES;\n        for (uint256 i; i < draw; ++i) {\n            for (uint256 j;; ++j) {\n                uint32 k = uint32(kFirst + 1 + uint256(keccak256(abi.encode(rnd, i, j))) % n);\n                bool dup;\n                for (uint256 q; q < i; ++q) if (R.picks[q] == k) dup = true;\n                if (!dup) { R.picks[i] = k; break; }\n            }\n        }\n        R.n = uint8(draw);\n        if (draw == 0) R.void_ = true;\n        emit Seeded(r, rnd, kFirst, kLast, R.picks, draw);\n    }\n\n    /// @notice Grade on drawn segment `pick` of round r: its 512 traces, checked against the hook's hash chain, are\n    ///         used to grade the candidates `ids`, and the living and default minds when asked.\n    function score(uint256 r, uint256 pick, uint256[] calldata traces, uint256[] calldata ids, bool living, bool dflt) external {\n        Round storage R = rounds[r];\n        (uint256 start, uint256 end) = window(r);\n        if (!R.seeded || R.void_ || R.finalized || block.timestamp >= end + SCORE_FOR || pick >= R.n) revert NotOpen();\n        if (traces.length != SEG) revert WrongChain();\n        uint256 k = R.picks[pick];\n        bytes32 h = HOOK.ckHead(k - 1);\n        // h = keccak256(abi.encode(h, trace)) for each trace, in scratch memory\n        assembly (\"memory-safe\") {\n            let at := traces.offset\n            for { let i := 0 } lt(i, 512) { i := add(i, 1) } {\n                mstore(0x00, h)\n                mstore(0x20, calldataload(add(at, shl(5, i))))\n                h := keccak256(0x00, 0x40)\n            }\n        }\n        if (h != HOOK.ckHead(k)) revert WrongChain();\n\n        uint8 bit = uint8(1 << pick);\n        uint8 full = uint8((1 << R.n) - 1);\n        if (dflt && R.defaultMask & bit == 0) {\n            R.defaultLoss += Cortex.loss(0, traces, start);\n            R.defaultMask |= bit;\n        }\n        if (living && R.champion != address(0) && R.champMask & bit == 0) {\n            R.champLoss += Cortex.loss(Cortex.load(R.champion), traces, start);\n            R.champMask |= bit;\n        }\n        Entry[] storage es = entries[r];\n        for (uint256 i; i < ids.length; ++i) {\n            uint256 id = ids[i];\n            Entry storage e = es[id];\n            if (!e.revealed || e.mask & bit != 0) continue;\n            e.loss += Cortex.loss(Cortex.load(e.mind), traces, start);\n            e.mask |= bit;\n            if (e.mask == full) {\n                R.graded += 1;\n                if (e.loss < R.bestLoss || (e.loss == R.bestLoss && id < R.bestId)) { R.bestLoss = e.loss; R.bestId = uint32(id); }\n            }\n        }\n        emit Graded(r, pick, k);\n    }\n\n    /// @notice Settle round r: promote, retire, pay, and release bonds. Rounds settle in order, each after its scoring.\n    function finalize(uint256 r) external {\n        if (r != finalizedThrough + 1) revert OutOfOrder();\n        (, uint256 end) = window(r);\n        if (block.timestamp < end + SCORE_FOR) revert NotYet();\n        Round storage R = rounds[r];\n        uint8 full = uint8((1 << R.n) - 1);\n        bool void_ = !R.seeded || R.void_ || R.n == 0 || R.defaultMask != full;\n        address winner;\n        uint256 bounty;\n        bool retired;\n        Entry[] storage es = entries[r];\n\n        if (!void_) {\n            bool livingGraded = R.champion == address(0) || R.champMask == full;\n            uint256 living = R.champion == address(0) ? R.defaultLoss : R.champLoss;\n            if (R.champion != address(0) && livingGraded && R.champLoss > R.defaultLoss) {\n                HOOK.setChampion(address(0));\n                retired = true;\n            }\n            if (livingGraded && R.graded != 0) {\n                uint256 bar = living < R.defaultLoss ? living : R.defaultLoss;\n                if (R.bestLoss < bar * (10000 - MARGIN_BPS) / 10000) {\n                    Entry storage w = es[R.bestId];\n                    HOOK.setChampion(w.mind);\n                    retired = false;\n                    winner = w.trainer;\n                    // paid by the improvement it showed, at a rate below what improvement costs to fake (econ_mind.js)\n                    (uint256 potOwed,) = HOOK.pot();\n                    // the pot taken in while the drawable segments (kFirst + 1 .. kLast) were written\n                    uint256 windowPot = HOOK.ckPot(R.kLast) - HOOK.ckPot(R.kFirst);\n                    bounty = BOUNTY_PER_UNIT * windowPot * (bar - R.bestLoss) / bar;\n                    if (bounty > potOwed) bounty = potOwed;\n                    if (bounty != 0) HOOK.payPot(address(this), bounty);\n                    bounty += carry;\n                    carry = 0;\n                    owed[winner] += bounty;\n                }\n            }\n        }\n        // bonds: refundable when revealed and fully graded, or revealed in a void round; the rest are forfeited. Counted,\n        // not looped, so a round can take any number of candidates.\n        carry += (es.length - (void_ ? R.revealed : R.graded)) * BOND;\n        R.finalized = true;\n        if (void_) R.void_ = true;\n        finalizedThrough = r;\n        emit Woke(r, HOOK.champion(), winner, bounty, retired, void_);\n    }\n\n    /// @notice Release the bond of a candidate that was revealed and fully graded (or revealed in a void round).\n    function refund(uint256 r, uint256 id) external {\n        Round storage R = rounds[r];\n        if (!R.finalized) revert NotYet();\n        Entry storage e = entries[r][id];\n        uint8 full = uint8((1 << R.n) - 1);\n        if (e.refunded || !e.revealed || !(R.void_ || e.mask == full)) revert Nothing();\n        e.refunded = true;\n        owed[e.trainer] += BOND;\n    }\n\n    /// @notice Pull what you are owed.\n    function pull() external {\n        uint256 a = owed[msg.sender];\n        if (a == 0) revert Nothing();\n        owed[msg.sender] = 0;\n        (bool ok,) = msg.sender.call{value: a}(\"\");\n        if (!ok) revert PayFailed();\n        emit Pulled(msg.sender, a);\n    }\n\n    // ------------------------------------------------------------------ views\n\n    function entryCount(uint256 r) external view returns (uint256) {\n        return entries[r].length;\n    }\n\n    function entry(uint256 r, uint256 id) external view returns (Entry memory) {\n        return entries[r][id];\n    }\n\n    function roundOf(uint256 r) external view returns (Round memory) {\n        return rounds[r];\n    }\n}\n"}},"settings":{"viaIR":true,"optimizer":{"enabled":true,"runs":200},"evmVersion":"cancun","metadata":{"bytecodeHash":"ipfs"},"outputSelection":{"*":{"*":["abi","evm.bytecode.object","evm.deployedBytecode.object","metadata"]}}}}