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
- Creator
- 0x7025557752…d7133AC21c
- Creation tx
- 0x65648551cd…f4fe35ba04
Token
- Name
- gat0
- Symbol
- GAT0
- Decimals
- 18
- Holders
- 452
- Market
- View token page →
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