Integration
What a front end or an indexer needs. The protocol page is the mechanism; this is the surface.
The six core contracts are deployed on chain 4663 and frozen, twelve stocks are listed, and public launching is
open: on 2026-09-22 publicLaunch reads true and strategyCount reads 1.
Addresses below are the production ones, verified read-only on chain that day. The addresses on this page are the protocol — the sandbox hook is a different contract with a similar-looking address, so point nothing at it.
Network
| Chain | Robinhood Chain |
| Chain ID | 4663 |
| The desk | hedgefun.trade — its own domain, not a subdomain of the app |
| Explorer | robinhoodchain.blockscout.com |
| A launch's own pool | Uniswap V4, on the shared hook, quoted in the stock |
| The treasury's stock leg | Uniswap V3 only, with a 10-minute TWAP corroborating spot |
| Stock oracle | Chainlink, RHxxx / USD ÷ the USDG feed — see the three prices |
| Block time | ~0.1s, which is why every decay here is timed in seconds, not blocks |
The contracts
Production addresses
Deployed from commit 3dc07eb at block 69,454,565 on chain 4663, and verified read-only at block 69,459,976 on
2026-09-22. All six carry a Sourcify creation and runtime match (not exact_match); Blockscout source display
and Uniswap routing approval are separate checks that do not follow from it.
| contract | address |
|---|---|
HedgeFunFactory | 0x58F6Ced8d02cD2567f1458801440Bc4eb67fA961 |
HedgeFunHook | 0x2A16d0973385952cFBE750D37f803d0958472844 |
TradingCalendar | 0xFE9E85f0C258Fc2757eB6Acd1ca032Ec860487F5 |
HedgeFunLaunchRouter | 0x470F5DeB0897118F6575F72183781C6a0610E1FA |
TreasuryDeployer | 0x05BC7875a7d61C3bbA2D7737D40C81aF5fD10DBE |
TokenDeployer | 0x9C0440dCc1b651C4707aa44E0eA083Fa4E0E8A6b |
HedgeFunTradeRouter | 0xb281F7D91325A3A386A00cA0F6010BC8B1491c8C |
owner, protocol and the calendar's owner are all the same Safe:
0x2910117dd2cB431173Ae9Fb6eAF30726321d1693, 3-of-4, with pendingOwner zero.
Listed stocks
Twelve, listed by the owner, each opening around a $10k FDV. A listing fixes the oracle and the V3 pool a creator cannot choose:
listings(stock) -> (oracle, pool, openingPrice)
bandCeiling(stock) -> the most staleness band a creator may pick, in bps/hour
Verified on chain 2026-09-22. Open price is openPriceE18, stock per token; the last column is the band ceiling.
| stock | stock token | price oracle | open price · band |
|---|---|---|---|
| NVDA | 0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC | 0x03c77f527Aa1B0B304602e3fB9Ac994dd1c157f8 | 44200000000 · 0 |
| SPCX | 0x4a0E65A3EcceC6dBe60AE065F2e7bb85Fae35eEa | 0xC109426706b9D01719ED82e8cd027fA91fBcdC0b | 65600000000 · 0 |
| CRCL | 0xdF0992E440dD0be65BD8439b609d6D4366bf1CB5 | 0x3bd2c093948D38A5eC2D945C5b065e5Db89aaB36 | 109000000000 · 10 |
| GOOGL | 0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3 | 0x15400359B0D14B4151FAe80b837abF3fA1977256 | 28000000000 · 0 |
| AMZN | 0x12f190a9F9d7D37a250758b26824B97CE941bF54 | 0xb34d278D2c792B7C48252C5eB8130Db45e35294e | 38500000000 · 5 |
| GME | 0x1b0E319c6A659F002271B69dB8A7df2F911c153E | 0x7548A343D06E45507Dd8F546D9d9Fdf684575ecE | 423000000000 · 5 |
| META | 0xc0D6457C16Cc70d6790Dd43521C899C87ce02f35 | 0x699E34068b8975bE113eD5fF12e429c69BCf1011 | 13400000000 · 5 |
| USAR | 0xd917B029C761D264c6A312BBbcDA868658eF86a6 | 0x02dE419166CED89fc9FCe2687CdBcBbc9eDe2612 | 596000000000 · 5 |
| MSTR | 0xec262a75e413fAfD0dF80480274532C79D42da09 | 0x19d0328F3152d1361db7B3Dd7708Cc36A779d612 | 61100000000 · 0 |
| AMD | 0x86923f96303D656E4aa86D9d42D1e57ad2023fdC | 0xc809bDf7E31E4830de2b03e6f809869Fb99f2246 | 16300000000 · 0 |
| MU | 0xfF080c8ce2E5feadaCa0Da81314Ae59D232d4afD | 0x4D15A4A1028f001800774A3d2DC31d77945399a4 | 9620000000 · 0 |
| INTC | 0xc72b96e0E48ecd4DC75E1e45396e26300BC39681 | 0xd399113f3196044A001614Ca9dda9cD8c9424264 | 82700000000 · 0 |
The V3 pool comes back from the same listings call — NVDA's is
0xD4EB21209c4d6093F80B5b84f5C45cC093eA14A3 — and a creator may not substitute either leg.
publicLaunch reads true and strategyCount reads 1. Read both live rather than hard-coding a state.
Launches
Platform test with a failed launch setup. CRCLGRID launched without two-sided liquidity or an initial USDG treasury reserve. Its strategy may remain inactive indefinitely if no lot reaches the first take-profit level and no reserve is added. See the launch postmortem.
| CRCLGRID — Hedgefun CRCL Range Grid | |
|---|---|
| token | 0xF5501c0D85992dD59371e15e3e7b60654ebaF332 |
| treasury | 0xc21873529515990C80BACe0931141e6B9D5A4fe0 |
| stock | CRCL, 0xdF0992E440dD0be65BD8439b609d6D4366bf1CB5 |
| V4 pool id | 0xfe98797b4dc2e5008712f812f3d90acba5adb216f8aa32e412fa377fb90b0b64 |
| pool key | currency0 CRCL, currency1 the token, fee 0, tickSpacing 60, hook 0x2A16d097…2844 |
| launch tx | 0x9a0c0240e9d2172440b70968294896a2b236e42ebf717fe781262b5666a1ca82, block 69,523,800 |
| rule | tp1 2000, tp2 4000, dip 1500, stop 0, lotBps 2000, bandBpsPerHour 10 |
| rates, 2026-09-22 | buyRateBps 1000, sellRateBps 1000 — the flat rate, both windows expired |
Enumerate the rest from strategies(i) for i < strategyCount(): each entry is
(token, treasury, hook, stock, creator).
What each contract is for
| contract | what you call it for |
|---|---|
HedgeFunFactory | predict(request) for a quote and its hash, launch(request, terms) to launch, and the listings a creator may pick from |
HedgeFunHook | one instance for every launch, state per pool id. The live tax rates, the split, and the tax events |
HedgeFunToken | the token, plus its page — logo(), description(), socials(), getTokenInfo(), with pons' reader names and selectors, so a tool written for a pons token reads this one unchanged |
HedgeFunTreasury | lots, the reserve, and health() |
HedgeFunLaunchRouter | launch + the creator's first buy + the treasury's first lot, in one transaction |
HedgeFunTradeRouter | USDG ↔ a launch's token in one transaction: the V3 hop, all-or-nothing, then the V4 pool |
PriceOracle, TradingCalendar | the stock's price in USDG, and the 24/5 exchange calendar it consults |
The ABI bundle lives in abi/ in the contracts repo, next to a generated surface listing. Generate it against
whatever commit is actually deployed: the take-profit fields widened to uint32 in 09f42b4, which changed the
launch, launchWithMetadata and predict selectors.
Launching
A launch takes a request and terms — the hash predict(request) returned, which is the launcher's commitment
to everything they were quoted. If a listing, a default or a fee moved in between, launch reverts rather than
going through at the new one.
| field | what it is |
|---|---|
name, symbol | the token's |
stock | a stock the factory already lists. A creator-supplied address is refused: a creator-supplied oracle is a creator-controlled price |
creator | receives the creator's share of the stock-denominated sell tax, forever. Must be an address that can call vetoCreator — an EOA or a Safe, not a splitter |
taxBps, creatorBps | the tax and the creator's cut of it, both bounded by the factory |
tp1Bps, tp2Bps | the two take-profit levels, uint32 and uncapped — surface what a creator actually set, however large |
dipBps | how far under the last sale the USDG reserve re-buys |
stopBps | optional stop. 0 means no stop, and every lot then sells above its own cost |
lotBps | the lot size the rule works in |
bandBpsPerHour | the staleness band — see below. Legal range is 0..bandCeiling(stock), which is 0 until the owner sets it, and the contract bounds it again at 200. Frozen into the treasury at birth |
Launch fee, optional, in one of three currencies: the native gas token (straight through, no swap), USDG, or
the same stock being launched against — so a creator holding only NVDA never has to touch USDG. A stray
msg.value on a non-native fee reverts rather than being kept.
launchWithMetadata also writes the token's page in the same transaction. The page belongs to the launching
address, which may appoint one editor and may lock() it for good; the protocol has no power over it.
Quoting a trade
Read the live rate. In a pool's first seconds a buy can pay up to 99%, and a sell can pay a spiked rate after every buy-back:
buy → hook.buyRateBps(poolId) // 99% → 66% → 33% → flat over the first 3 seconds
sell → hook.sellRateBps(poolId) // spiked at launch and after each buy-back, decaying to flat
A quote that shows the flat rate during those windows is wrong by up to 99 percentage points. Show the rate the hook returns, and show that it is decaying.
Other rules that change what you can offer:
- Sells are exact-input only.
- Exact-output is accepted for a buy at the flat rate only, taxed on the stock going in and grossed up so the route costs the same. Anything else is refused.
- The launcher's own buys inside the launch transaction are exempt from the window, with no cap on size or count. Render them in a launch's early history rather than averaging them away.
- Show first-buy concentration on every token — how much of the supply the launcher took in the launch transaction. It is the single number that tells a buyer how much of the float is still out there.
The staleness band, and how to ask for it
The band widens how far spot may sit from the last Chainlink print before health() closes — maxDeviationBps + bandBpsPerHour × feedAgeHours, capped at 3000 —
and the trade still executes against the oracle-derived bound.
Read bandCeiling(stock) and branch, rather than always rendering the field:
- Ceiling 0 — seven of the twelve listings today — the only legal value is 0. Skip the field and say the treasury follows Chainlink and keeps market hours.
- Ceiling above 0 — a slider from 0 to the ceiling, defaulting to the ceiling. Tighter is stricter, and 0 is a legitimate choice that trades only in market hours.
A wider band buys tolerance rather than a better price: the trade still executes against the oracle-derived bound, and the extra hours it opens are closure hours. Either way, label it frozen at launch — set once, by the creator, for the life of the treasury.
Reading a launch
| view | what it tells you |
|---|---|
lotCount() | how many lots the rule is carrying |
reserveUsdg() | the dip-buying reserve |
unbookedStock() | stock that has arrived but is not yet costed — anyone may call book() |
health() | whether the treasury can price and trade right now, and at what price |
Read health() before showing a launch as trading. It is true when the feed is fresh within the band, the
token's oraclePaused() is clear, the calendar says the market is open, and the pool agrees with the oracle
inside the execution gate. The treasury trades on a price it can verify, by construction — while its token keeps
trading all week.
Running a treasury, and getting paid
The treasury has no keeper and no privileged caller. The five writes below are open to anyone, they check their
own triggers against the oracle, and each pays the caller bountyBps of what that call produced — 50
(0.5%) as shipped, 200 (2%) at the contract's ceiling, read it from params().
| call | due when | pays you | reverts with |
|---|---|---|---|
book() | stock has arrived and the oracle is live | nothing — it returns false instead of reverting | (never; returns a bool) |
takeProfit(id) | p reached cost + tp1, then cost + tp2 | a cut of the profit, in the stock | NotDue, Unhealthy |
stopLoss(id) | the creator set a stop and p fell to it | a cut of the proceeds, in USDG | NotDue, Unhealthy |
buyDip() | p fell dip under the last sale, and the spend clears minLotUsdg | a cut of what was spent, in USDG | NotDue, Unhealthy |
buyback() | the treasury holds profit and the cooldown has passed | a cut of the tokens bought, in the token | NotDue, Cooldown, Unhealthy |
hook.sweep(poolId) | tax has accrued on a pool | sweepTipBps of what the sweep distributes | — |
takeProfit calls book() for you first, so a separate book() is only worth sending when nothing else is due
and you want the lot costed at today's price.
Finding work
for i in 0 .. factory.strategyCount()-1:
(token, treasury, hook, stock, creator) = factory.strategies(i)
(ok, p) = treasury.health() # false → nothing but buyback can fire
params = treasury.params() # tp1Bps, tp2Bps, dipBps, stopBps, lotBps, bountyBps …
for id in 0 .. treasury.lotCount()-1:
(qty, cost, half, tp1Left) = treasury.lots(id)
# tp1 is due at cost*(1+tp1Bps/1e4) while `half` is false, tp2 at cost*(1+tp2Bps/1e4) after
# stop, if stopBps != 0, at cost*(1-stopBps/1e4)
treasury.unbookedStock() # > 0 → book() has work
treasury.reserveUsdg() # buyDip spends lotBps of this
Simulate before you send. NotDue is the normal answer, not an error: an eth_call costs nothing and tells
you whether the transaction would earn anything.
What bounds a call
- One call is one chunk.
takeProfitandstopLossoffer at mostsellChunkUsdgat the rule's price, andbuyDipspendslotBpsof the reserve. A lot that is larger than a chunk takes several calls, each paying. - A sale fills as far as the oracle price less the slippage limit allows, and every effect — the lot, the event, your bounty — is sized from what actually filled.
buyback()has a cooldown (buybackCooldown, 60s as shipped) and prices itself off the token pool's own ten-minute TWAP, so it keeps working through a weekend when the equity feed is frozen.buyDip()takes the bounty off the spend, not off the ask, so what you earn tracks what the treasury actually bought.
Pricing
A launch pool's price is the token in the stock, not in USDG. To show a USDG price or a market cap, multiply by the stock's own oracle price.
price_usdg = price_in_stock × oracle_usdg(stock)
market_cap = price_usdg × (total supply − burned supply)
Two things to read from chain rather than assume:
- Token ordering. Which side of a pool is token0 is not a constant across this chain's stock pools. Assuming one ordering produces a near-zero number with no error, which reads as "no liquidity" rather than as a bug. It has shipped twice.
- The seeded range. The launch position is one-sided, from just above the opening tick to the top of the usable range, so progress along it is computed from the pool's current tick. There is no graduation event to listen for, because nothing migrates.
Computing the three figures
A front end shows market FDV, the stock sitting in the pool, and what the treasury holds. They come from three different places, and each has a lookalike that returns a confident number instead of an error.
FDV = poolPrice(token in stock) × supply × oracleUsdg(stock)
^ marginal price, not an average, and not what a sale would realise
^ say which supply: issued, or issued less burned — and subtract the burn once
^ the opening FDV is stock-denominated: supply × listings(stock).openPriceE18
NVDA at launch = 1e9 × 4.42e-8 = 44.2 NVDA, which is a dollar figure only
after you multiply by the stock's own price
pool reserve = the stock held by THIS pool, from the pool's own state
✗ balanceOf(PoolManager) — V4 keeps every pool's balances in one contract,
so that address holds the whole chain's stock, not this pool's
the token side is reported as a count, beside it, never valued at its own
marginal price and added in
treasury = stock + USDG the treasury address holds, split into principal,
realised profit, and what is free for the next buy-back
✗ principal + realised + budget summed — they are stages of one pile
✗ treasury ÷ supply presented as a redemption value — there is no redemption
✗ pool reserve + treasury — different pots, different owners
Three display rules that follow from this:
- An unread number is "not fetched", never
0. Zero is a fact about a balance; a failed read is a fact about your connection. - Every figure is its own read — a reserve comes from the pool, never from FDV or from whatever else is in hand.
- Carry provenance: which price, read when, and whether it is stale. Feeds are 24/5, so a stale read on a weekday evening is the normal state and should be labelled.
- Show first-buy concentration wherever a token is listed.
Events
Event topics and decoding examples are published here with the addresses. The paths worth indexing are the launch
itself, the hook's tax split on each swap, and the treasury's book / takeProfit / stopLoss / buyDip /
buyback steps — together they reconstruct every lot without needing our API.
Support
[email protected], or @0xHedgehood.
This is an interface, not investment advice. See the Terms of Use.