Gat0

0x2121ed643249e344bde4f88e33023376fe09e176

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
23,962 bytes
ABI entries
148
95 read · 17 write
License
none

Contract information

Address
0x2121ed643249e344bde4f88e33023376fe09e176
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x65648551cd…f4fe35ba04

Token

Name
gat0
Symbol
GAT0
Decimals
18
Holders
452

Read contract (95)

BOUNTY_BPS()uint16
BURN_FEE_BPS()uint16
CASHCAT()address
CLAW_COOLDOWN()uint64
CLAW_FRAC()uint16
CLAW_MULT()uint16
COOLDOWN()uint64
DAY_CAP()uint256
DEADPREY_FRAC()uint16
DEPLOY_TS()uint64
DOMAIN_SEPARATOR()bytes32
DRIP_SECS()uint64
EPOCH()uint64
GAS_STIPEND()uint64
K()uint256
LAUNCH_DECAY_ETH()uint256
LAUNCH_LEVY_START_BPS()uint16
LIFE_CLAW_FRAC()uint16
LIFE_PAYLOAD_BPS()uint16
LIFE_SPACING()uint64
MAX_SUPPLY()uint256
MINT_LEVY_BPS()uint16
OBSERVE_DEAD_MAX()uint64
P0_INIT()uint256
P0_MAX()uint256
PER_TX_CAP()uint256
PER_TX_WINDOW()uint64
POOL()address
POOL_FEE()uint24
POSTDEAD_FRAC()uint16
POUNCE_CHUNK_CAP()uint256
POUNCE_MIN()uint256
POUNCE_POOL_BPS()uint16
R(uint256)uint256
ROUTER()address
S()uint256
SCAVENGE_COOLDOWN()uint64
SCAVENGE_FRAC()uint16
STARVE_FRAC()uint16
STARVE_MIN()uint256
STARVE_POUCH_BPS()uint16
THROTTLE_FRAC()uint16
TWAP_LONG()uint32
TWAP_LONG_MULT()uint16
TWAP_SHORT()uint32
UNDERWATER_MAX()uint64
WETH()address
ZONE_LEVY_BPS()uint16
accountedBalance()uint256
allowance(address, address)uint256
balanceOf(address)uint256
catHeld()uint256
costBasis()uint256
cumInflow()uint256
cumMintNet()uint256
curveReserve()uint256
daySpent()uint256
daySpentDay()uint256
decimals()uint8
dustRemainder()uint256
epochInflow()uint256
epochStart()uint64
huntAlive()bool
inertSurplus()uint256
lastClawTs()uint64
lastEligibleAt()uint64
lastForcedClawTs()uint64
lastGoodTwap()uint256
lastGoodTwapTs()uint64
lastPounceTs()uint64
lastScavengeTs()uint64
levyBps()uint256
levyRetired()bool
lifeLatched()uint8
milestone(uint256)uint256
name()string
nonces(address)uint256
observeFailSince()uint64
p0()uint256
peakTs()uint64
peakTwap()uint256
pendingBounties(address)uint256
pendingBountyTotal()uint256
pendingBurnClaimTotal()uint256
pendingBurnClaims(address)uint256
postDeadDrainTs()uint64
pouch()uint256
probeTwaps()uint256, uint256
queued(uint256)uint128, uint128, uint64
queuedLength()uint256
retirementPending()bool
symbol()string
totalSupply()uint256
underwaterSince()uint64
zoneLatched()bool

Events (13)

ApprovalBurnClaimQueuedBurnedClawedDeadpreyLifeLatchedMintedPouncedRatchetedRetiredScavengedStarvedTransfer

ABI

[
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "maxSupply",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "k",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "p0Init",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "p0Max",
            "type": "uint256"
          },
          {
            "internalType": "uint16",
            "name": "mintLevyBps",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "burnFeeBps",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "launchLevyStartBps",
            "type": "uint16"
          },
          {
            "internalType": "uint256",
            "name": "launchDecayEth",
            "type": "uint256"
          },
          {
            "internalType": "uint16",
            "name": "zoneLevyBps",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "lifePayloadBps",
            "type": "uint16"
          },
          {
            "internalType": "uint256[9]",
            "name": "milestones",
            "type": "uint256[9]"
          },
          {
            "internalType": "uint64",
            "name": "dripSecs",
            "type": "uint64"
          },
          {
            "internalType": "uint64",
            "name": "lifeSpacing",
            "type": "uint64"
          },
          {
            "internalType": "uint64",
            "name": "perTxWindow",
            "type": "uint64"
          },
          {
            "internalType": "uint64",
            "name": "gasStipend",
            "type": "uint64"
          }
        ],
        "internalType": "struct Gat0.Config",
        "name": "c",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "starveMin",
            "type": "uint256"
          },
          {
            "internalType": "uint16",
            "name": "starvePouchBps",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "starveFrac",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "postDeadFrac",
            "type": "uint16"
          },
          {
            "internalType": "uint64",
            "name": "epoch",
            "type": "uint64"
          }
        ],
        "internalType": "struct Gat0.StarveConfig",
        "name": "s",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "address",
            "name": "cashcat",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "pool",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "router",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "weth",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "poolFee",
            "type": "uint24"
          },
          {
            "internalType": "uint256",
            "name": "pounceMin",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "pounceChunkCap",
            "type": "uint256"
          },
          {
            "internalType": "uint16",
            "name": "pouncePoolBps",
            "type": "uint16"
          },
          {
            "internalType": "uint256",
            "name": "dayCap",
            "type": "uint256"
          },
          {
            "internalType": "uint64",
            "name": "cooldown",
            "type": "uint64"
          },
          {
            "internalType": "uint16",
            "name": "clawMult",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "clawFrac",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "lifeClawFrac",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "throttleFrac",
            "type": "uint16"
          },
          {
            "internalType": "uint64",
            "name": "clawCooldown",
            "type": "uint64"
          },
          {
            "internalType": "uint16",
            "name": "deadpreyFrac",
            "type": "uint16"
          },
          {
            "internalType": "uint64",
            "name": "underwaterMax",
            "type": "uint64"
          },
          {
            "internalType": "uint64",
            "name": "observeDeadMax",
            "type": "uint64"
          },
          {
            "internalType": "uint32",
            "name": "twapShort",
            "type": "uint32"
          },
          {
            "internalType": "uint32",
            "name": "twapLong",
            "type": "uint32"
          },
          {
            "internalType": "uint16",
            "name": "twapLongMult",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "bountyBps",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "scavengeFrac",
            "type": "uint16"
          },
          {
            "internalType": "uint64",
            "name": "scavengeCooldown",
            "type": "uint64"
          }
        ],
        "internalType": "struct Gat0.HuntConfig",
        "name": "h",
        "type": "tuple"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AllowanceOverflow",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "AllowanceUnderflow",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ClaimFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ExpDomain",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientAllowance",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientBalance",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPermit",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NothingToClaim",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "SD59x18",
        "name": "x",
        "type": "int256"
      }
    ],
    "name": "PRBMath_SD59x18_Exp2_InputTooBig",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "SD59x18",
        "name": "x",
        "type": "int256"
      }
    ],
    "name": "PRBMath_SD59x18_Exp_InputTooBig",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "UD60x18",
        "name": "x",
        "type": "uint256"
      }
    ],
    "name": "PRBMath_UD60x18_Exp2_InputTooBig",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "UD60x18",
        "name": "x",
        "type": "uint256"
      }
    ],
    "name": "PRBMath_UD60x18_Exp_InputTooBig",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "UD60x18",
        "name": "x",
        "type": "uint256"
      }
    ],
    "name": "PRBMath_UD60x18_Log_InputTooSmall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PerTxCap",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Permit2AllowanceIsFixedAtInfinity",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PermitExpired",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Reentrancy",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SoldOut",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "StaleOracle",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TotalSupplyOverflow",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroValue",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Approval",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "holder",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "BurnClaimQueued",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "dS",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "payout",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "Burned",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "caller",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "catIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethOut",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "bounty",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "forced",
        "type": "bool"
      }
    ],
    "name": "Clawed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [],
    "name": "Deadprey",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint8",
        "name": "life",
        "type": "uint8"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "payload",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint64",
        "name": "eligibleAt",
        "type": "uint64"
      }
    ],
    "name": "LifeLatched",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "levy",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "dS",
        "type": "uint256"
      }
    ],
    "name": "Minted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "caller",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "catOut",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "bounty",
        "type": "uint256"
      }
    ],
    "name": "Pounced",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "donation",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "absorbed",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newP0",
        "type": "uint256"
      }
    ],
    "name": "Ratcheted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [],
    "name": "Retired",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "caller",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "catIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethOut",
        "type": "uint256"
      }
    ],
    "name": "Scavenged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "postDead",
        "type": "bool"
      }
    ],
    "name": "Starved",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Transfer",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "BOUNTY_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "BURN_FEE_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "CASHCAT",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "CLAW_COOLDOWN",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "CLAW_FRAC",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "CLAW_MULT",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "COOLDOWN",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DAY_CAP",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEADPREY_FRAC",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEPLOY_TS",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DOMAIN_SEPARATOR",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "result",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DRIP_SECS",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "EPOCH",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "GAS_STIPEND",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "K",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "LAUNCH_DECAY_ETH",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "LAUNCH_LEVY_START_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "LIFE_CLAW_FRAC",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "LIFE_PAYLOAD_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "LIFE_SPACING",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MINT_LEVY_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "OBSERVE_DEAD_MAX",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "P0_INIT",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "P0_MAX",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "PER_TX_CAP",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "PER_TX_WINDOW",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POOL",
    "outputs": [
      {
        "internalType": "contract IUniswapV3Pool",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POOL_FEE",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POSTDEAD_FRAC",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POUNCE_CHUNK_CAP",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POUNCE_MIN",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POUNCE_POOL_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "s",
        "type": "uint256"
      }
    ],
    "name": "R",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "ROUTER",
    "outputs": [
      {
        "internalType": "contract ISwapRouter02",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "S",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SCAVENGE_COOLDOWN",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SCAVENGE_FRAC",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "STARVE_FRAC",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "STARVE_MIN",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "STARVE_POUCH_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "THROTTLE_FRAC",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TWAP_LONG",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TWAP_LONG_MULT",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TWAP_SHORT",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "UNDERWATER_MAX",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "WETH",
    "outputs": [
      {
        "internalType": "contract IWETH",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "ZONE_LEVY_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "_forcedClawExternal",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "accountedBalance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "acc",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      }
    ],
    "name": "allowance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "approve",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "balanceOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "dS",
        "type": "uint256"
      }
    ],
    "name": "burn",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "catHeld",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      }
    ],
    "name": "claimBounty",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      }
    ],
    "name": "claimBurnPayout",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "claw",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "costBasis",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "cumInflow",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "cumMintNet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "curveReserve",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "daySpent",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "daySpentDay",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "deadprey",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "decimals",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "donate",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "dustRemainder",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "epochInflow",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "epochStart",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "huntAlive",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "inertSurplus",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lastClawTs",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lastEligibleAt",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lastForcedClawTs",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lastGoodTwap",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lastGoodTwapTs",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lastPounceTs",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lastScavengeTs",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "levyBps",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "levyRetired",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lifeLatched",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "i",
        "type": "uint256"
      }
    ],
    "name": "milestone",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "mint",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "nonces",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "observeFailSince",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "p0",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "peakTs",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "peakTwap",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "pendingBounties",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingBountyTotal",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingBurnClaimTotal",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "pendingBurnClaims",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      },
      {
        "internalType": "uint8",
        "name": "v",
        "type": "uint8"
      },
      {
        "internalType": "bytes32",
        "name": "r",
        "type": "bytes32"
      },
      {
        "internalType": "bytes32",
        "name": "s",
        "type": "bytes32"
      }
    ],
    "name": "permit",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pokeOracle",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "postDeadDrainTs",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pouch",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pounce",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "probeTwaps",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "shortP",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "longP",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "queued",
    "outputs": [
      {
        "internalType": "uint128",
        "name": "total",
        "type": "uint128"
      },
      {
        "internalType": "uint128",
        "name": "applied",
        "type": "uint128"
      },
      {
        "internalType": "uint64",
        "name": "eligibleAt",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "queuedLength",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "retirementPending",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "scavenge",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "settleDrip",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "starve",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "symbol",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "transfer",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "transferFrom",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "underwaterSince",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "zoneLatched",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { ERC20 } from "solady/tokens/ERC20.sol";
import { ReentrancyGuard } from "solady/utils/ReentrancyGuard.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
import { CurveLib } from "./lib/CurveLib.sol";
import { OracleLib } from "./lib/OracleLib.sol";
import { IWETH, ISwapRouter02, IUniswapV3Pool, IERC20Minimal } from "./lib/External.sol";

/// @title gat0 — immutable, ownerless bonding-curve token (core)
/// @notice Curve + lives + fees + ratchet + drip + pull ledgers. Implements
/// STATE-TRANSITION-SPEC.md v2 §1–§3 EXCEPT the oracle/swap hunt functions
/// (pounce/claw/deadprey/scavenge), which arrive with HuntLib/OracleLib in the
/// next increment. Hunt STATE is declared here; with `catHeld == 0` the core is
/// fully self-consistent (forced claw is a no-op while no position exists).
///
/// No owner, no admin, no upgrade, no pause, no selfdestruct, no delegatecall.
contract Gat0 is ERC20, ReentrancyGuard {
    using SafeTransferLib for address;

    // ─────────────────────────── errors ───────────────────────────
    error SoldOut();
    error ZeroValue();
    error PerTxCap();
    error NothingToClaim();
    error ClaimFailed();

    // ─────────────────────────── immutables (config) ───────────────────────────
    struct Config {
        uint256 maxSupply;
        uint256 k;
        uint256 p0Init;
        uint256 p0Max;
        uint16 mintLevyBps; // 225
        uint16 burnFeeBps; // 90
        uint16 launchLevyStartBps; // 900
        uint256 launchDecayEth; // 50e18
        uint16 zoneLevyBps; // 112
        uint16 lifePayloadBps; // 900
        uint256[9] milestones; // reserve ETH thresholds
        uint64 dripSecs; // 9h
        uint64 lifeSpacing; // 24h
        uint64 perTxWindow; // 24h
        uint64 gasStipend; // burn payout push gas
    }

    uint256 public immutable MAX_SUPPLY;
    uint256 public immutable K;
    uint256 public immutable P0_INIT;
    uint256 public immutable P0_MAX;
    uint16 public immutable MINT_LEVY_BPS;
    uint16 public immutable BURN_FEE_BPS;
    uint16 public immutable LAUNCH_LEVY_START_BPS;
    uint256 public immutable LAUNCH_DECAY_ETH;
    uint16 public immutable ZONE_LEVY_BPS;
    uint16 public immutable LIFE_PAYLOAD_BPS;
    uint64 public immutable DRIP_SECS;
    uint64 public immutable LIFE_SPACING;
    uint64 public immutable PER_TX_WINDOW;
    uint64 public immutable GAS_STIPEND;
    uint64 public immutable DEPLOY_TS;
    uint256 public immutable PER_TX_CAP;
    uint256[9] private _milestones; // set once in constructor; never mutated

    // ─────────────────────────── curve / accounting state ───────────────────────────
    uint256 public p0;
    uint256 public curveReserve;
    uint256 public pouch;
    uint256 public dustRemainder; // retry-eligible (F6)
    uint256 public inertSurplus; // never re-applied (F4/F6)
    uint256 public cumInflow; // gross mint ETH (drives launch-levy decay)
    uint256 public cumMintNet; // MONOTONIC cumulative net (post-levy) mint ETH — drives life
        // milestones (R6-2). Only mint grows it; donations/burns/hunt-proceeds do NOT, so lives
        // and retirement can't be forced by donating a floor then burning out.

    mapping(address => uint256) public pendingBounties;
    mapping(address => uint256) public pendingBurnClaims;
    uint256 public pendingBountyTotal; // aggregate for exact I4 (R5-9)
    uint256 public pendingBurnClaimTotal;

    // ─────────────────────────── lives / retirement state ───────────────────────────
    struct Payload {
        uint128 total;
        uint128 applied;
        uint64 eligibleAt;
    }

    uint8 public lifeLatched;
    Payload[] public queued;
    uint64 public lastEligibleAt;
    uint64 public lastForcedClawTs;
    bool public zoneLatched;
    bool public retirementPending;
    bool public levyRetired;
    bool private _inClawContext; // prevents nested forced-claw during claw/scavenge (F7)
    uint64 internal constant SCAVENGE_FLOOR_DECAY = 14 days; // dead-oracle scavenge floor → 0 by here (R8-2)

    // ─────────────────────────── hunt state ───────────────────────────
    bool public huntAlive = true;
    uint256 public catHeld;
    uint256 public costBasis;
    uint256 public peakTwap; // ETH per CASHCAT, WAD; trailing peak
    uint64 public peakTs;
    uint256 public lastGoodTwap; // last successful short-TWAP price (R6-3: scavenge ref, not spot)
    uint64 public lastGoodTwapTs; // timestamp of lastGoodTwap (staleness guard, R6-3/R7)
    uint64 public underwaterSince; // 0 = not underwater; continuous run start
    uint64 public observeFailSince; // 0 = oracle healthy; continuous outage start
    uint64 public lastPounceTs;
    uint64 public lastClawTs;
    uint64 public lastScavengeTs;
    uint256 public daySpent;
    uint256 public daySpentDay;
    uint64 public epochStart;
    uint256 public epochInflow;
    uint64 public postDeadDrainTs;

    // starve constants:
    uint256 public immutable STARVE_MIN; // 0.9e18
    uint16 public immutable STARVE_POUCH_BPS; // 50
    uint16 public immutable STARVE_FRAC; // 900
    uint16 public immutable POSTDEAD_FRAC; // 2000
    uint64 public immutable EPOCH; // 9d

    // hunt targets + constants:
    address public immutable CASHCAT;
    IUniswapV3Pool public immutable POOL;
    ISwapRouter02 public immutable ROUTER;
    IWETH public immutable WETH;
    uint24 public immutable POOL_FEE;
    uint256 public immutable POUNCE_MIN; // 0.5e18
    uint256 public immutable POUNCE_CHUNK_CAP; // 2e18
    uint16 public immutable POUNCE_POOL_BPS; // 50
    uint256 public immutable DAY_CAP; // 12e18
    uint64 public immutable COOLDOWN; // 30m
    uint16 public immutable CLAW_MULT; // 15000
    uint16 public immutable CLAW_FRAC; // 5000
    uint16 public immutable LIFE_CLAW_FRAC; // 3300
    uint16 public immutable THROTTLE_FRAC; // 6000
    uint64 public immutable CLAW_COOLDOWN; // 1h
    uint16 public immutable DEADPREY_FRAC; // 3000
    uint64 public immutable UNDERWATER_MAX; // 60d
    uint64 public immutable OBSERVE_DEAD_MAX; // 7d
    uint32 public immutable TWAP_SHORT; // 1800
    uint32 public immutable TWAP_LONG; // 14400
    uint16 public immutable TWAP_LONG_MULT; // 10500
    uint16 public immutable BOUNTY_BPS; // 30
    uint16 public immutable SCAVENGE_FRAC; // 1000
    uint64 public immutable SCAVENGE_COOLDOWN; // 24h

    struct HuntConfig {
        address cashcat;
        address pool;
        address router;
        address weth;
        uint24 poolFee;
        uint256 pounceMin;
        uint256 pounceChunkCap;
        uint16 pouncePoolBps;
        uint256 dayCap;
        uint64 cooldown;
        uint16 clawMult;
        uint16 clawFrac;
        uint16 lifeClawFrac;
        uint16 throttleFrac;
        uint64 clawCooldown;
        uint16 deadpreyFrac;
        uint64 underwaterMax;
        uint64 observeDeadMax;
        uint32 twapShort;
        uint32 twapLong;
        uint16 twapLongMult;
        uint16 bountyBps;
        uint16 scavengeFrac;
        uint64 scavengeCooldown;
    }

    // ─────────────────────────── events ───────────────────────────
    event Minted(address indexed to, uint256 ethIn, uint256 levy, uint256 dS);
    event Burned(address indexed from, uint256 dS, uint256 payout, uint256 fee);
    event LifeLatched(uint8 life, uint256 payload, uint64 eligibleAt);
    event Ratcheted(uint256 donation, uint256 absorbed, uint256 newP0);
    event Retired();
    event Starved(uint256 amount, bool postDead);
    event BurnClaimQueued(address indexed holder, uint256 amount);
    event Pounced(address indexed caller, uint256 ethIn, uint256 catOut, uint256 bounty);
    event Clawed(address indexed caller, uint256 catIn, uint256 ethOut, uint256 bounty, bool forced);
    event Deadprey();
    event Scavenged(address indexed caller, uint256 catIn, uint256 ethOut);

    struct StarveConfig {
        uint256 starveMin;
        uint16 starvePouchBps;
        uint16 starveFrac;
        uint16 postDeadFrac;
        uint64 epoch;
    }

    constructor(Config memory c, StarveConfig memory s, HuntConfig memory h) ReentrancyGuard() {
        // exp-domain guard baked in at deploy (plan §1.2): k*MAX_SUPPLY must stay < CurveLib.EXP_MAX_ARG.
        require((c.k * c.maxSupply) / 1e18 <= 40e18, "exp-domain");
        require(c.p0Max >= c.p0Init, "p0max");

        MAX_SUPPLY = c.maxSupply;
        K = c.k;
        P0_INIT = c.p0Init;
        P0_MAX = c.p0Max;
        MINT_LEVY_BPS = c.mintLevyBps;
        BURN_FEE_BPS = c.burnFeeBps;
        LAUNCH_LEVY_START_BPS = c.launchLevyStartBps;
        LAUNCH_DECAY_ETH = c.launchDecayEth;
        ZONE_LEVY_BPS = c.zoneLevyBps;
        LIFE_PAYLOAD_BPS = c.lifePayloadBps;
        DRIP_SECS = c.dripSecs;
        LIFE_SPACING = c.lifeSpacing;
        PER_TX_WINDOW = c.perTxWindow;
        GAS_STIPEND = c.gasStipend;
        PER_TX_CAP = c.maxSupply / 100;
        _milestones = c.milestones;
        DEPLOY_TS = uint64(block.timestamp);

        STARVE_MIN = s.starveMin;
        STARVE_POUCH_BPS = s.starvePouchBps;
        STARVE_FRAC = s.starveFrac;
        POSTDEAD_FRAC = s.postDeadFrac;
        EPOCH = s.epoch;
        epochStart = uint64(block.timestamp);

        CASHCAT = h.cashcat;
        POOL = IUniswapV3Pool(h.pool);
        ROUTER = ISwapRouter02(h.router);
        WETH = IWETH(h.weth);
        POOL_FEE = h.poolFee;
        POUNCE_MIN = h.pounceMin;
        POUNCE_CHUNK_CAP = h.pounceChunkCap;
        POUNCE_POOL_BPS = h.pouncePoolBps;
        DAY_CAP = h.dayCap;
        COOLDOWN = h.cooldown;
        CLAW_MULT = h.clawMult;
        CLAW_FRAC = h.clawFrac;
        LIFE_CLAW_FRAC = h.lifeClawFrac;
        THROTTLE_FRAC = h.throttleFrac;
        CLAW_COOLDOWN = h.clawCooldown;
        DEADPREY_FRAC = h.deadpreyFrac;
        UNDERWATER_MAX = h.underwaterMax;
        OBSERVE_DEAD_MAX = h.observeDeadMax;
        TWAP_SHORT = h.twapShort;
        TWAP_LONG = h.twapLong;
        TWAP_LONG_MULT = h.twapLongMult;
        BOUNTY_BPS = h.bountyBps;
        SCAVENGE_FRAC = h.scavengeFrac;
        SCAVENGE_COOLDOWN = h.scavengeCooldown;

        p0 = c.p0Init;
    }

    // ─────────────────────────── ERC20 metadata ───────────────────────────
    // `virtual` so the mainnet canary can ship a decoy identity (opsec: no
    // "gat0" strings on-chain before production). Compile-time only — Gat0's
    // own bytecode is unchanged (hash-verified when this was added).
    function name() public pure virtual override returns (string memory) { return "gat0"; }
    function symbol() public pure virtual override returns (string memory) { return "GAT0"; }

    // ─────────────────────────── views ───────────────────────────
    function milestone(uint256 i) public view returns (uint256) { return _milestones[i]; }

    function R(uint256 s) public view returns (uint256) { return CurveLib.reserveAt(p0, K, s); }

    /// @notice Sum of all on-chain-tracked ETH liabilities (I4). Slush = balance - this.
    /// Includes the pull-ledger aggregates (R5-9/MED-2) so slush is exact, not over-reported.
    function accountedBalance() public view returns (uint256 acc) {
        acc = curveReserve + pouch + dustRemainder + inertSurplus
            + pendingBountyTotal + pendingBurnClaimTotal;
        uint256 n = queued.length;
        for (uint256 i; i < n; ++i) acc += queued[i].total - queued[i].applied;
        // Every bounty/burn-claim credit first debits pouch/curveReserve, so the totals are backed.
    }

    /// @notice Current mint levy in bps, honoring launch decay, zone discount, and retirement.
    function levyBps() public view returns (uint256) {
        if (levyRetired) return 0;
        uint256 base = zoneLatched ? ZONE_LEVY_BPS : MINT_LEVY_BPS;
        if (cumInflow >= LAUNCH_DECAY_ETH) return base;
        uint256 span = LAUNCH_LEVY_START_BPS - base;
        return LAUNCH_LEVY_START_BPS - (span * cumInflow) / LAUNCH_DECAY_ETH;
    }

    function queuedLength() external view returns (uint256) { return queued.length; }

    // ─────────────────────────── drip / settlement ───────────────────────────

    function _vestedOf(Payload storage p) private view returns (uint256) {
        if (block.timestamp <= p.eligibleAt) return 0;
        uint256 elapsed = block.timestamp - p.eligibleAt;
        if (elapsed >= DRIP_SECS) return p.total;
        return (uint256(p.total) * elapsed) / DRIP_SECS;
    }

    /// @dev Vest matured payload value into the curve. Never called from burn (I11).
    function _settleDrip() internal {
        uint256 i;
        while (i < queued.length) {
            Payload storage p = queued[i];
            uint256 v = _vestedOf(p);
            uint256 delta = v - p.applied;
            if (delta > 0) {
                _ratchet(delta);
                p.applied = uint128(v);
            }
            if (p.applied == p.total) {
                uint256 last = queued.length - 1;
                if (i != last) queued[i] = queued[last];
                queued.pop();
            } else {
                unchecked { ++i; }
            }
        }
        if (retirementPending && queued.length == 0 && !levyRetired) {
            levyRetired = true;
            emit Retired();
        }
    }

    /// @notice Permissionless drip poke — retirement can always complete without a mint (F2).
    /// (Lives key off cumMintNet now, so drip/settlement can't cross a milestone; no latch needed.)
    function settleDrip() external nonReentrant {
        _settleDrip();
    }

    // ─────────────────────────── ratchet ───────────────────────────

    /// @dev Donate `d` wei into the curve, raising p0 (never lowering). Retries accumulated
    /// dust (R5-8), routes P0_MAX-clipped and cap-maxed leftovers to inertSurplus (never
    /// re-applied), and keeps genuine sub-ulp remainders in dustRemainder for a later retry.
    function _ratchet(uint256 d) internal {
        if (d == 0) return;
        if (S() == 0) {
            inertSurplus += d; // no p0 change at S=0
            return;
        }
        d += dustRemainder; // fold accumulated dust into this attempt (R5-8)
        dustRemainder = 0;
        if (p0 == P0_MAX) {
            inertSurplus += d; // floor maxed → inert, not retryable dust (R5-8)
            return;
        }
        uint256 rBefore = CurveLib.reserveAt(p0, K, S());
        uint256 rawP0 = CurveLib.ratchetP0(p0, K, S(), d);
        uint256 newP0 = rawP0 > P0_MAX ? P0_MAX : rawP0;
        if (newP0 == p0) {
            dustRemainder += d; // sub-ulp: keep for later retry
            return;
        }
        uint256 rAfter = CurveLib.reserveAt(newP0, K, S());
        uint256 needed = rAfter > rBefore ? rAfter - rBefore : 0; // reserve the new floor requires
        // I1 guard (R7): only raise p0 if the donation FULLY funds the implied reserve delta.
        // Integer rounding can make `needed` exceed `d` by a wei; raising p0 anyway would leave
        // curveReserve < R(S). In that case keep the whole donation as retryable dust instead.
        if (needed > d) {
            dustRemainder += d;
            return;
        }
        curveReserve += needed; // curveReserve_old ≥ rBefore ⇒ curveReserve_new ≥ rAfter = R(S)
        if (d > needed) dustRemainder += d - needed; // undershoot excess → retry later (real floor $)
        p0 = newP0;
        emit Ratcheted(d, needed, newP0);
    }

    function S() public view returns (uint256) { return totalSupply(); }

    // ─────────────────────────── lives ───────────────────────────

    function _latchLives() internal {
        bool crossed;
        while (lifeLatched < 9 && cumMintNet >= _milestones[lifeLatched]) {
            uint256 pay = (pouch * LIFE_PAYLOAD_BPS) / 1e4;
            pouch -= pay;
            uint64 eAt = uint64(block.timestamp);
            uint64 sched = lastEligibleAt + LIFE_SPACING;
            if (sched > eAt) eAt = sched;
            queued.push(Payload({ total: uint128(pay), applied: 0, eligibleAt: eAt }));
            lastEligibleAt = eAt;
            unchecked { ++lifeLatched; }
            crossed = true;
            emit LifeLatched(lifeLatched, pay, eAt);
            if (lifeLatched == 9) retirementPending = true;
        }
        // forced claw: best-effort, ≤1 per call, ≤1 per 24h. No-op in core (catHeld==0).
        if (crossed) _forcedClaw();
    }

    /// @dev Forced partial claw at a life event. **Never blocks the latch (R5-4 + HIGH-2):**
    /// the ENTIRE body — preflight external reads AND the WETH.withdraw/ratchet success path —
    /// runs inside an external self-call wrapped in try/catch, so no external revert (hostile
    /// CASHCAT, upgradeable-WETH failure, stale oracle) can propagate up into mint/claw/etc.
    /// `_inClawContext` prevents nesting inside claw/scavenge; the self-call is onlySelf.
    function _forcedClaw() internal {
        if (catHeld == 0 || !huntAlive || _inClawContext) return;
        if (block.timestamp - lastForcedClawTs < 24 hours) return;
        try this._forcedClawExternal() { } catch { }
    }

    function _forcedClawExternal() external {
        require(msg.sender == address(this), "self");
        _inClawContext = true;
        uint256 sell = (catHeld * LIFE_CLAW_FRAC) / 1e4;
        if (sell == 0) { _inClawContext = false; return; }
        // TWAP-anchored min-out (HIGH-1): use min(shortP,longP); a stale oracle reverts here and
        // is caught by the outer try → the forced claw simply SKIPS. Never a manipulable spot.
        uint256 shortP = OracleLib.twapPriceWad(POOL, TWAP_SHORT);
        uint256 longP = OracleLib.twapPriceWad(POOL, TWAP_LONG);
        lastGoodTwap = longP; // R9-Info: store the 4h (manipulation-resistant) scavenge reference
        lastGoodTwapTs = uint64(block.timestamp);
        uint256 floorP = shortP < longP ? shortP : longP;
        uint256 minOut = (sell * floorP * (1e6 - POOL_FEE - 10000)) / (1e18 * 1e6);
        uint256 got = _sellCat(sell, minOut); // reverts on slippage/failure → caught by outer try
        if (got > 0) _ratchet(got);
        lastForcedClawTs = uint64(block.timestamp);
        emit Clawed(address(this), sell, got, 0, true);
        _inClawContext = false;
    }

    // ─────────────────────────── hunt: oracle + swap helpers ───────────────────────────

    function _catBal() internal view returns (uint256) {
        return IERC20Minimal(CASHCAT).balanceOf(address(this));
    }

    /// @dev Proportional cost-basis reduction, then decrement the position.
    function _reducePosition(uint256 catSpent) internal {
        if (catSpent >= catHeld) { costBasis = 0; catHeld = 0; return; }
        costBasis -= (costBasis * catSpent) / catHeld;
        catHeld -= catSpent;
    }

    /// @dev Trailing peak of the **4h TWAP** (R8-1): the death check compares the 4h TWAP against
    /// this peak, so the peak MUST be measured on the same long window — else a 30-min pump could
    /// inflate a short-window peak and make a normal 4h price read as a "collapse". Rises to new
    /// highs; only decays after 30d AND not underwater (C14). `price` is the 4h TWAP.
    function _updatePeak(uint256 price) internal {
        if (price > peakTwap) { peakTwap = price; peakTs = uint64(block.timestamp); return; }
        bool underwater = catHeld > 0 && (catHeld * price) / 1e18 < costBasis;
        if (!underwater && block.timestamp - peakTs > 30 days) { peakTwap = price; peakTs = uint64(block.timestamp); }
    }

    /// @dev Underwater tracker on the **4h TWAP** (R8-1): manipulation-resistant mark for the
    /// 60-day underwater death path. `price` is the 4h TWAP.
    function _trackUnderwater(uint256 price) internal {
        bool underwater = catHeld > 0 && (catHeld * price) / 1e18 < costBasis;
        if (underwater) { if (underwaterSince == 0) underwaterSince = uint64(block.timestamp); }
        else underwaterSince = 0;
    }

    /// @dev Short & long TWAP prices (ETH per CASHCAT). Long consult reverts if <4h history
    /// (§13-R4-F3) — that IS the ≥4h floor. On success clears observeFailSince; the caller
    /// (deadprey path) treats a revert as an oracle-dead signal.
    function _twaps() internal returns (uint256 shortP, uint256 longP) {
        shortP = OracleLib.twapPriceWad(POOL, TWAP_SHORT);
        longP = OracleLib.twapPriceWad(POOL, TWAP_LONG);
        observeFailSince = 0;
        lastGoodTwap = longP; // 4h manipulation-resistant reference for the scavenge fallback (R9)
        lastGoodTwapTs = uint64(block.timestamp);
    }

    // ─────────────────────────── hunt: pounce ───────────────────────────

    function pounce() external nonReentrant {
        require(huntAlive && !retirementPending, "hunt inactive");
        require(pouch >= POUNCE_MIN, "pouch low");
        bool throttled = _isThrottled();
        uint64 wait = COOLDOWN * (throttled ? 4 : 1);
        require(block.timestamp >= lastPounceTs + wait, "cooldown");

        (uint256 shortP, uint256 longP) = _twaps(); // reverts if <4h history (F3)
        _updatePeak(longP);

        _rollDay();
        uint256 poolWeth = WETH.balanceOf(address(POOL));
        uint256 chunk = POUNCE_CHUNK_CAP;
        uint256 poolCap = (poolWeth * POUNCE_POOL_BPS) / 1e4;
        if (poolCap < chunk) chunk = poolCap;
        uint256 pouchCap = (pouch * 1e4) / (1e4 + BOUNTY_BPS); // reserve bounty (F/C9)
        if (pouchCap < chunk) chunk = pouchCap;
        uint256 dayRoom = DAY_CAP > daySpent ? DAY_CAP - daySpent : 0;
        if (dayRoom < chunk) chunk = dayRoom;
        require(chunk > 0, "no room");

        uint256 bound = shortP;
        uint256 longBound = (longP * TWAP_LONG_MULT) / 1e4;
        if (longBound < bound) bound = longBound;
        // Allow the unavoidable pool fee + ≤1% impact above the TWAP bound (plan R4: residual
        // bleed ≤ fee + impact). allowed price = bound * (1 + POOL_FEE + 1%), all in 1e6 units.
        uint256 allowed = (bound * (1e6 + POOL_FEE + 10000)) / 1e6;
        uint256 minCat = (chunk * 1e18) / allowed;

        uint256 bounty = (chunk * BOUNTY_BPS) / 1e4;
        pouch -= chunk + bounty;
        daySpent += chunk;
        lastPounceTs = uint64(block.timestamp);

        uint256 catOut = _buyCat(chunk, minCat);
        catHeld += catOut;
        costBasis += chunk;
        pendingBounties[msg.sender] += bounty;
        pendingBountyTotal += bounty;
        _trackUnderwater(longP);
        emit Pounced(msg.sender, chunk, catOut, bounty);
    }

    function _isThrottled() internal view returns (bool) {
        if (catHeld == 0) return false;
        // 4h TWAP (R9-Info): match the underwater/death window so a 30-min pump/dump can't
        // flip the throttle. A "slow the dip-buying" mechanic doesn't need short-window reaction.
        uint256 longP = OracleLib.twapPriceWad(POOL, TWAP_LONG);
        return (catHeld * longP) / 1e18 <= (costBasis * THROTTLE_FRAC) / 1e4;
    }

    function _rollDay() internal {
        uint256 today = block.timestamp / 1 days;
        if (today != daySpentDay) { daySpentDay = today; daySpent = 0; }
    }

    function _buyCat(uint256 ethAmt, uint256 minOut) internal returns (uint256 catOut) {
        WETH.deposit{ value: ethAmt }();
        address(WETH).safeApprove(address(ROUTER), ethAmt);
        uint256 before = _catBal();
        ROUTER.exactInputSingle(
            ISwapRouter02.ExactInputSingleParams({
                tokenIn: address(WETH), tokenOut: CASHCAT, fee: POOL_FEE,
                recipient: address(this), amountIn: ethAmt, amountOutMinimum: minOut, sqrtPriceLimitX96: 0
            })
        );
        address(WETH).safeApprove(address(ROUTER), 0);
        catOut = _catBal() - before; // balance-delta (untrusted token)
        // enforce the bound against what the contract ACTUALLY received, not the router's report —
        // a fee-on-transfer CASHCAT could satisfy amountOutMinimum yet deliver less here (R5-7).
        require(catOut >= minOut, "buy slippage");
    }

    /// @dev Sell CASHCAT→ETH with a min-out floor; reverts on swap failure (caller-facing).
    function _sellCat(uint256 catAmt, uint256 minEthOut) internal returns (uint256 ethOut) {
        uint256 catBefore = _catBal();
        uint256 wethBefore = WETH.balanceOf(address(this));
        CASHCAT.safeApprove(address(ROUTER), catAmt);
        ROUTER.exactInputSingle(
            ISwapRouter02.ExactInputSingleParams({
                tokenIn: CASHCAT, tokenOut: address(WETH), fee: POOL_FEE,
                recipient: address(this), amountIn: catAmt, amountOutMinimum: minEthOut, sqrtPriceLimitX96: 0
            })
        );
        CASHCAT.safeApprove(address(ROUTER), 0);
        uint256 spent = catBefore - _catBal();
        ethOut = WETH.balanceOf(address(this)) - wethBefore;
        require(ethOut >= minEthOut, "sell slippage"); // balance-delta enforcement (R5-7)
        _reducePosition(spent);
        if (ethOut > 0) WETH.withdraw(ethOut);
    }

    // ─────────────────────────── hunt: claw ───────────────────────────

    function claw() external nonReentrant {
        _settleDrip();
        require(catHeld > 0, "no position");
        require(block.timestamp >= lastClawTs + CLAW_COOLDOWN, "cooldown");
        (uint256 shortP, uint256 longP) = _twaps();
        _updatePeak(longP);
        // Eligibility uses the SAME conservative price as execution (R9-Low): a 30-min pump
        // must not make a not-really-profitable position claw-eligible only to settle at the
        // lower long-window floor.
        uint256 clawP = shortP < longP ? shortP : longP;
        uint256 posVal = (catHeld * clawP) / 1e18;
        require(posVal >= (costBasis * CLAW_MULT) / 1e4, "not in profit");

        uint256 sell = (catHeld * CLAW_FRAC) / 1e4;
        // impact-bounded: cap sell so we don't exceed the pool's absorbable size (POUNCE_POOL_BPS of pool cat)
        uint256 poolCat = _poolCatReserve();
        uint256 impactCap = (poolCat * POUNCE_POOL_BPS) / 1e4;
        if (impactCap > 0 && sell > impactCap) sell = impactCap;

        uint256 floorP = shortP < longP ? shortP : longP;
        // sell floor allows the unavoidable pool fee + ≤1% impact below the TWAP floor.
        uint256 minEth = (sell * floorP * (1e6 - POOL_FEE - 10000)) / (1e18 * 1e6);
        lastClawTs = uint64(block.timestamp);
        _inClawContext = true;
        uint256 ethOut = _sellCat(sell, minEth);
        _inClawContext = false;

        uint256 bounty = (ethOut * BOUNTY_BPS) / 1e4;
        pendingBounties[msg.sender] += bounty;
        pendingBountyTotal += bounty;
        _ratchet(ethOut - bounty);
        _trackUnderwater(longP);
        emit Clawed(msg.sender, sell, ethOut, bounty, false);
    }

    function _poolCatReserve() internal view returns (uint256) {
        return IERC20Minimal(CASHCAT).balanceOf(address(POOL));
    }

    // ─────────────────────────── hunt: deadprey ───────────────────────────

    /// @notice End the hunt permanently once CASHCAT is dead. **Non-reverting (R6-1 / R7):** it
    /// durably refreshes the oracle/underwater trackers (so a recovery persists — a reverting
    /// refresh would roll back), then kills only if dead; if not dead it returns as a no-op.
    /// Price-death uses the **4-hour TWAP** (not a short-window tracker): a one-block or brief
    /// manipulated dip cannot move a 4h average to 30% of peak — only genuine sustained collapse
    /// can. Death = 4h TWAP ≤ 30% of peak, OR underwater ≥ 60d, OR oracle dead ≥ 7d.
    function deadprey() external nonReentrant {
        if (!huntAlive) return;
        bool dead;
        try this.probeTwaps() returns (uint256 shortP, uint256 longP) {
            // durable refresh (this call path does not revert, so resets persist)
            observeFailSince = 0;
            lastGoodTwap = longP;
            lastGoodTwapTs = uint64(block.timestamp);
            _updatePeak(longP); // peak measured on the 4h window (R8-1)
            _trackUnderwater(longP);
            if (peakTwap > 0 && longP <= (peakTwap * DEADPREY_FRAC) / 1e4) dead = true; // 4h TWAP vs 4h peak
        } catch {
            if (observeFailSince == 0) observeFailSince = uint64(block.timestamp);
        }
        if (underwaterSince != 0 && block.timestamp - underwaterSince >= UNDERWATER_MAX) dead = true;
        if (observeFailSince != 0 && block.timestamp - observeFailSince >= OBSERVE_DEAD_MAX) dead = true;
        if (!dead) return; // no-op; the refresh above already persisted

        huntAlive = false;
        postDeadDrainTs = uint64(block.timestamp);
        uint256 bounty = pouch < 0.009 ether ? pouch : 0.009 ether;
        if (bounty > 0) { pouch -= bounty; pendingBounties[msg.sender] += bounty; pendingBountyTotal += bounty; }
        emit Deadprey();
    }

    /// @notice Permissionless, NON-REVERTING oracle-health poke. This is the ONLY reliable way
    /// to durably maintain the underwater + oracle-dead timers without a trade (a reverting path
    /// like the old armDeadprey would roll back any state it set — R5-3). Keepers call this each
    /// block during a stalled position or an outage; on recovery it clears the timers.
    function pokeOracle() external nonReentrant {
        try this.probeTwaps() returns (uint256 shortP, uint256 longP) {
            observeFailSince = 0;
            lastGoodTwap = longP;
            lastGoodTwapTs = uint64(block.timestamp);
            _updatePeak(longP); // peak measured on the 4h window (R8-1)
            _trackUnderwater(longP);
        } catch {
            if (observeFailSince == 0) observeFailSince = uint64(block.timestamp);
        }
    }

    /// @dev External wrapper so callers can try/catch the oracle (staleness) safely.
    function probeTwaps() external view returns (uint256 shortP, uint256 longP) {
        require(msg.sender == address(this), "self");
        shortP = OracleLib.twapPriceWad(POOL, TWAP_SHORT);
        longP = OracleLib.twapPriceWad(POOL, TWAP_LONG);
    }

    // ─────────────────────────── hunt: scavenge ───────────────────────────

    function scavenge() external nonReentrant {
        _settleDrip();
        require(!huntAlive && catHeld > 0, "not scavengeable");
        require(block.timestamp >= lastScavengeTs + SCAVENGE_COOLDOWN, "cooldown");
        uint256 sell = (catHeld * SCAVENGE_FRAC) / 1e4;
        if (sell == 0) sell = catHeld;
        uint256 poolCat = _poolCatReserve();
        uint256 impactCap = (poolCat * POUNCE_POOL_BPS) / 1e4;
        if (impactCap > 0 && sell > impactCap) sell = impactCap;
        // Fire-sale floor. Prefer a LIVE TWAP (50% haircut) and store it as the next reference.
        // When the oracle is dead (a valid death path) NEVER use same-block spot — a searcher
        // could crush it to steal the liquidation (R6-3). Instead use the last GOOD TWAP with a
        // floor that DECAYS with the reference's age (R8-2): starts at a 10%-of-ref floor and
        // linearly reaches 0 by SCAVENGE_FLOOR_DECAY. A stale-high ref therefore can never
        // permanently block the exit (I8 — catHeld is always eventually liquidatable), while a
        // fresh ref still blocks a near-zero sandwich.
        uint256 minEth;
        try this.probeTwaps() returns (uint256 shortP, uint256 longP) {
            lastGoodTwap = longP; // R9: store the 4h reference for the next fallback
            lastGoodTwapTs = uint64(block.timestamp);
            uint256 floorP = shortP < longP ? shortP : longP;
            minEth = (sell * floorP * 5000) / (1e18 * 1e4); // 50% haircut vs the conservative live TWAP
        } catch {
            require(lastGoodTwap > 0, "no oracle ref");
            uint256 age = block.timestamp - lastGoodTwapTs;
            uint256 fracBps = age >= SCAVENGE_FLOOR_DECAY ? 0 : (1000 * (SCAVENGE_FLOOR_DECAY - age)) / SCAVENGE_FLOOR_DECAY;
            minEth = (sell * lastGoodTwap * fracBps) / (1e18 * 1e4); // age-decaying floor vs last-good ref
        }
        lastScavengeTs = uint64(block.timestamp);
        _inClawContext = true;
        uint256 ethOut = _sellCat(sell, minEth);
        _inClawContext = false;
        uint256 bounty = (ethOut * BOUNTY_BPS) / 1e4;
        pendingBounties[msg.sender] += bounty;
        pendingBountyTotal += bounty;
        _ratchet(ethOut - bounty);
        emit Scavenged(msg.sender, sell, ethOut);
    }

    // ─────────────────────────── starve (oracle-free hunt fn) ───────────────────────────

    // ─────────────────────────── mint / burn ───────────────────────────

    function mint() external payable nonReentrant {
        _settleDrip();
        if (totalSupply() == MAX_SUPPLY) revert SoldOut();
        if (msg.value == 0) revert ZeroValue();

        uint256 levy = (msg.value * levyBps()) / 1e4;
        uint256 net = msg.value - levy;
        uint256 dS = CurveLib.mintDelta(p0, K, totalSupply(), net);

        // per-tx cap during launch window (F/§1.7)
        if (block.timestamp < DEPLOY_TS + PER_TX_WINDOW && dS > PER_TX_CAP) revert PerTxCap();

        // clip to MAX_SUPPLY; unmintable ETH becomes a donation (never revert away user ETH here)
        uint256 ns = totalSupply() + dS;
        uint256 donateBack;
        if (ns > MAX_SUPPLY) {
            dS = MAX_SUPPLY - totalSupply();
            uint256 used = CurveLib.reserveCeilAt(p0, K, MAX_SUPPLY) - CurveLib.reserveCeilAt(p0, K, totalSupply());
            donateBack = net > used ? net - used : 0;
            net -= donateBack;
        }

        curveReserve += net;
        pouch += levy;
        cumInflow += msg.value;
        cumMintNet += net; // monotonic milestone driver (R6-2)
        epochInflow += msg.value;
        if (dS > 0) _mint(msg.sender, dS);

        if (donateBack > 0) _ratchet(donateBack);

        // zone latch (keyed off cumulative net mint, not current reserve)
        if (lifeLatched < 9 && cumMintNet >= (_milestones[lifeLatched] * 9) / 10) zoneLatched = true;
        _latchLives();

        emit Minted(msg.sender, msg.value, levy, dS);
    }

    function burn(uint256 dS) external nonReentrant {
        // I11: NO _settleDrip, NO life, NO TWAP, NO hunt on this path.
        uint256 s = totalSupply();
        if (dS > s) dS = s;
        if (dS == 0) revert ZeroValue();
        require(balanceOf(msg.sender) >= dS, "balance");

        uint256 gross = CurveLib.burnPayout(p0, K, s, dS);
        if (gross > curveReserve) gross = curveReserve;
        uint256 fee = (gross * BURN_FEE_BPS) / 1e4;
        uint256 holder = gross - fee;

        uint256 sNew = s - dS;
        curveReserve -= gross; // remove holder payout + fee; the fee is re-donated to the floor
        _burn(msg.sender, dS);

        // Re-donate the burn fee into the floor via the shared, I1-safe `_ratchet` (no external
        // calls / life / drip / TWAP — I11 preserved). At sNew==0 it routes to inertSurplus.
        if (fee > 0) _ratchet(fee);

        // full burn-down quarantine (F4): nothing survives into future supply
        if (sNew == 0) {
            uint256 sweep = curveReserve + dustRemainder;
            uint256 n = queued.length;
            for (uint256 i; i < n; ++i) sweep += queued[i].total - queued[i].applied;
            delete queued;
            curveReserve = 0;
            dustRemainder = 0; // retry-eligible dust can't survive a full burn-down (F4)
            inertSurplus += sweep;
            zoneLatched = false;
        }

        // payout: push with pull-fallback (C1/I11) — burn never reverts on delivery
        (bool ok,) = payable(msg.sender).call{ value: holder, gas: GAS_STIPEND }("");
        if (!ok) {
            pendingBurnClaims[msg.sender] += holder;
            pendingBurnClaimTotal += holder;
            emit BurnClaimQueued(msg.sender, holder);
        }
        emit Burned(msg.sender, dS, holder, fee);
    }

    // ─────────────────────────── donate (floor pump) ───────────────────────────

    /// @notice Anyone can donate ETH straight into the floor — the reserve ratchets up and
    /// every holder's redemption price rises. Requires live supply. **Donations do NOT advance
    /// life milestones or retirement (R6-2):** those key off `cumMintNet` (organic mint demand),
    /// not the ratcheted reserve, so a whale can't donate a floor + burn out to force the schedule.
    function donate() external payable nonReentrant {
        if (msg.value == 0) revert ZeroValue();
        require(totalSupply() > 0, "no live supply");
        _settleDrip();
        _ratchet(msg.value);
    }

    // ─────────────────────────── pull ledgers ───────────────────────────

    /// @notice Claim your own burn payout to a chosen recipient. Creditor = msg.sender, so a
    /// contract holder that can't receive ETH can still redirect its funds to an EOA (R5-1).
    function claimBurnPayout(address recipient) external nonReentrant {
        uint256 amt = pendingBurnClaims[msg.sender];
        if (amt == 0) revert NothingToClaim();
        pendingBurnClaims[msg.sender] = 0; // CEI; revert below rolls this back on failure
        pendingBurnClaimTotal -= amt;
        (bool ok,) = payable(recipient).call{ value: amt }("");
        if (!ok) revert ClaimFailed();
    }

    /// @notice Claim your own keeper bounties to a chosen recipient (creditor = msg.sender, R5-1).
    function claimBounty(address recipient) external nonReentrant {
        uint256 amt = pendingBounties[msg.sender];
        if (amt == 0) revert NothingToClaim();
        pendingBounties[msg.sender] = 0; // CEI; revert below rolls this back on failure
        pendingBountyTotal -= amt;
        (bool ok,) = payable(recipient).call{ value: amt }("");
        if (!ok) revert ClaimFailed();
    }

    // ─────────────────────────── starve (oracle-free hunt fn) ───────────────────────────

    function starve() external nonReentrant {
        _settleDrip();
        if (huntAlive) {
            require(block.timestamp - epochStart >= EPOCH, "epoch");
            uint256 threshold = STARVE_MIN;
            uint256 pouchThresh = (pouch * STARVE_POUCH_BPS) / 1e4;
            if (pouchThresh > threshold) threshold = pouchThresh;
            if (epochInflow < threshold) {
                uint256 sv = (pouch * STARVE_FRAC) / 1e4;
                pouch -= sv;
                _ratchet(sv);
                emit Starved(sv, false);
            }
            epochStart = uint64(block.timestamp);
            epochInflow = 0;
        } else {
            require(block.timestamp - postDeadDrainTs >= EPOCH, "epoch");
            uint256 sv = (pouch * POSTDEAD_FRAC) / 1e4;
            pouch -= sv;
            _ratchet(sv);
            postDeadDrainTs = uint64(block.timestamp);
            emit Starved(sv, true);
        }
    }

    /// @dev WETH-only ETH ingress (unwrap path), OUTSIDE the reentrancy guard (C10): a
    /// guarded receive() would revert the legitimate WETH.withdraw callback while claw/
    /// scavenge hold the guard. Only the WETH contract may send ETH here; users enter via mint().
    receive() external payable {
        require(msg.sender == address(WETH), "only WETH");
    }
}
Chain explorer3586msChain node83ms