SatoHook
a uniswap v4 hook that takes over every swap, replaces the amm math, and acts as the counterparty.
SatoHook is the v4 hook that does all of the work. it holds the eth reserves, runs the curve, enforces the guardrails, and is the one address allowed to mint or burn SatoToken. it is the only contract in the system with non-trivial logic.
permissions
v4 hooks declare which lifecycle callbacks they want via 14 bits encoded in the hook's address. SatoHook turns on exactly four of them:
beforeInitialize— validates the pool key.beforeAddLiquidity— reverts forever. the pool can never carry liquidity.beforeSwap— intercepts every swap and replaces amm math with the curve.beforeSwapReturnDelta— flags thatbeforeSwapreturns aBeforeSwapDeltathat cancels the amm-routed portion of the swap.
the constructor calls Hooks.validateHookPermissionsto check that the deployed contract's address has these exact bits set in its low 14 bits. a salt grinder in the deploy script searches for a CREATE2 salt that produces such an address.
state
ethCum- cumulative fair-curve eth (post-fee) ever absorbed by the curve. buys add, sells subtract. this is the single number that determines the curve's position.
totalMintedFair- fair-curve circulating supply. equals
totalMinted(ethCum)up to rounding. differs fromSATO_TOKEN.totalSupply()whenever entropy bonuses have been minted — the inverse curve always references this fair number, not the actual supply. selfDeprecatedbool. flips totruethe first timetotalMintedFair / K ≥ 99/100. once true, all future buys revert.poolInitializedbool. flips totruewhenbeforeInitializeaccepts a pool key for the first time.lastBuyBlock[a]mapping(address => uint256). the block in which addressalast bought. read by the cooldown check on sells.feesAccrued- cumulative 0.3% fee revenue, in eth. the hook does not expose a withdraw function, so this counter grows forever and the eth backing it is locked.
the buy flow
the buyer (or their router) pre-settles eth into the pool manager, then calls swap. the hook's beforeSwap intercepts before the amm sees anything, computes the curve mint amount, applies the entropy multiplier if applicable, and returns a BeforeSwapDelta that nets eth in and sato out at settlement time.
the sell flow
sells take the seller's actual sato, convert it into fair-curve units, evaluate the inverse curve at the current totalMintedFair, deduct the fee, and pay eth out from the hook's balance.
fair-curve unit conversion
because entropy adds bonus sato above the curve, the actual erc-20 supply and the curve's notion of supply diverge. when sellers burn, the contract scales their input back into fair-curve units:
satoFairIn = satoIn × totalMintedFair / totalSupply
this keeps the inverse curve referencing the canonical state (ethCum, totalMintedFair), regardless of how much bonus supply has entered circulation. entropy bonuses get socialized uniformly into every holder's per-token exit value — nobody's sell prices ahead of the curve.
error surface
NotPoolManager- any entrypoint called by an address other than the v4 pool manager.
InvalidPool- pool key mismatch: not eth/sato, wrong fee, wrong hook. raised in
beforeInitializeor inbeforeSwapif a malformed key gets through. LiquidityAdditionsForbidden- any attempt to add liquidity to the pool, ever.
BuyTooLarge- a buy with
ethIn > MAX_BUY_WEI(5 eth). split into multiple buys to get around this, but each will pay gas and be priced one step further up the curve. CooldownActive- a sell from an address whose last buy was within
COOLDOWN_BLOCKS(1 block). wait until the next block. SelfDeprecatedNoBuys- a buy after
selfDeprecatedhas flipped. only sells remain enabled from this point forward. ExactOutputUnsupported- a swap with
amountSpecified >= 0. only exact-input swaps are accepted in either direction. MissingSwapperInHookDatahookDatafailed to encode an address. the swapper identity must be threaded through so the cooldown can attribute buys to the right account.InsufficientEthReserves- the inverse curve would owe more eth than the hook currently holds. should be impossible in practice (the hook holds every eth wei ever sent into it minus what it has paid out), but catches degenerate cases like external donations being withdrawn through synthetic paths.
view helpers
curveReserveEth()- returns
address(this).balance − feesAccrued— the eth pool that backs sells. drops as sells consume it, grows as buys add to it. philosophy()- returns the contract's 256-byte manifesto string. set in the constructor from 8
bytes32words; the getter concatenates them and trims trailing nulls. immutable, on-chain statement of intent.
this page describes deployed code. nothing here is a promise — it is a description of what the contract does and what it doesn't.
