Our developer feedback for ETHGlobal Tokyo 2026 is in FEEDBACK.md.
Phinary brings ETH and SOL binary options into Uniswap v4. Traders buy and sell UP or DOWN tokens through ordinary Uniswap swaps. A custom hook calculates the option price, and a shared USDC vault backs the payouts. At expiry, the winning side redeems for 1 USDC per token.
Start with the hook's swap callback, then follow the quote and execution path through the SDK. The code links below are pinned to commit 422fb4f so the highlighted lines stay stable.
| What to inspect | Implementation | Explanation |
|---|---|---|
| Hook permissions and custom swap accounting | permissions · beforeSwap | Hook reference |
| Underlying pool price at the start of a block | price capture and read | Oracle reference |
| Quotes through V4Quoter | quote calls | Quote and swap |
| UniversalRouter swap encoding and execution calldata | V4_SWAP, settlement and execute | Quote and swap |
| Permit2 permissions and token approvals | permit, allowances and approval | SDK usage |
| Settlement from pool observations and redemption | settle and redeem | Settlement rules |
| Vault deposits, withdrawals and PoolManager unlock callback | vault and unlockCallback | Vault accounting |
For the wallet walkthrough, see Trade in the Uniswap app. That guide uses a local interface fork; it does not imply that the public Uniswap app routes these pools.
Markets run on four tracks, all on one hook and one LP vault. On each track a market expires in the second the next one opens.
| Track | Tokens | A new market | Trading stops | Settlement window |
|---|---|---|---|---|
| ETH, 1 minute | ETHUP / ETHDOWN |
every minute | 12 s before expiry | 10 s |
| ETH, 15 minutes | ETH15MUP / ETH15MDOWN |
every quarter hour | 32 s before expiry | 30 s |
| SOL, 1 minute | SOLUP / SOLDOWN |
every minute | 12 s before expiry | 10 s |
| SOL, 15 minutes | SOL15MUP / SOL15MDOWN |
every quarter hour | 32 s before expiry | 30 s |
- PredictionHook is a Uniswap v4 hook that prices every YES/NO swap itself. It uses a Black-Scholes binary price in its settlement-matched average-price form, and Uniswap's liquidity curve is never used.
- UnderlyingOracleHook records the start-of-block price and volatility of one Uniswap v4 pool; there is one for ETH/USDC and one for SOL/USDC. Swaps earlier in the same block cannot move the price it reports. Both have renounced their ownership, so no key can change them.
- MarketGatekeeper is an ownerless contract that is the hook's
owner. Its constructor deployed one MarketScheduler per track, and it forwards market creation from those four only, so the track set is fixed for good. Anyone can call a scheduler'sopen()once per time slot to open that track's next market, until the slot's deadline, when it revertsTooLate. There is no admin, no setter and no keeper role on the gatekeeper, the schedulers or the hook. See gatekeeper implementation and the scheduler guide. - Settlement uses the pool's average price over the final window of each market. Anyone can call
settle(), and ties resolve DOWN. - The LP vault underwrites every market. Complete-set accounting means every winning token is always backed by 1 USDC.
Phinary is not oracle-free. Settling "ETH > $X at expiry" needs a price, and whatever supplies it is an oracle. Phinary's oracle is a Uniswap pool: no third party observes prices off-chain and posts them, so there is no operator, feed admin or pause switch to trust. Pool liquidity and the cost of manipulating its price remain security assumptions; see the security notes.
- Testnet (live): our own Uniswap v4 ETH/USDC and SOL/USDC pools, each read by its own
UnderlyingOracleHook. Unichain Sepolia has no real ETH or SOL market, so one mirror bot trades both pools to the Coinbase prices, standing in for the arbitrage that keeps real pools in line. The contracts never see Coinbase; they only read the pools. - Mainnet design:
SealedPoolOraclereads Unichain's deep ETH/USDC pool, which has no hook, through fee-growth seals and Merkle-Patricia proofs against Unichain's own block hashes. It is tested against the real pool on a mainnet fork and not deployed yet. - Bots (keeper, and on mainnet the poke-and-prove bot) only send permissionless transactions. They cannot choose a price; if they stop, markets pause, and anyone can run them.
Say "no external oracle", not "oracle-free" or "no oracles".
| Contract | Address |
|---|---|
| PredictionHook | 0xb4544Af6c126773c2f8f4f02f7a1Bde7b975aaa8 |
| MarketGatekeeper (the hook's only owner) | 0x755dBc10AB4b9BFDB8c46939702dC08B8F3e607A |
| MarketScheduler, ETH 1 minute | 0x8f1b371e41FeCBb825d0baAB19f906E645C760e0 |
| MarketScheduler, ETH 15 minutes | 0x02f0B250120c817A45C6ba580FE68eB461E0A10e |
| MarketScheduler, SOL 1 minute | 0x067180DE54F4a800C88C3dC9EDa8b106f7126b83 |
| MarketScheduler, SOL 15 minutes | 0x950eEDA8303253b76f0d47f4a3eD33aA7fF54E74 |
| UnderlyingOracleHook, ETH/USDC (ownership renounced) | 0x1F356D9E7d6dBE6d807aCBc5D163a7af265cd080 |
| UnderlyingOracleHook, SOL/USDC (ownership renounced) | 0xc7dDbB6648BE0DFCF3eF2a68D40d66374184D080 |
| dSOL (demo SOL for the SOL/USDC pool) | 0x49f3768635A2Db5e4bdcB76A4F191EC48B770417 |
| PriceSteerer (demo price mirror) | 0x21C55279188072E6BfeF14c4920eA1cEFEd358a3 |
| USDC (Circle, testnet) | 0x31d0220469e10c4E71834a79b1f276d740d3768F |
| Legacy PredictionHooks (old markets, redeem only) | 0x62bBCbA51cbFC8D0C932e482bD8F62590fEeeAa8, 0xE6780bBeAee4183Ffd8EBe0d2862dEd221B96aA8 |
| Legacy MarketScheduler | 0x511fFFb9fE5d393B10bF185A9c580A732Eff44Dd |
| PoolManager | 0x00B036B58a818B1BC34d502D3fE730Db729e62AC |
| V4Quoter | 0x56DCD40A3F2d466F48e7F48bDBE5Cc9B92Ae4472 |
| UniversalRouter | 0xf70536B3bcC1bD1a972dc186A2cf84cC6da6Be5D |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
Every address is in deployment manifest. Tell the tracks apart with schedulerOf(marketId) on the gatekeeper, or by which scheduler emitted a market's MarketOpened event.
| Path | What it holds |
|---|---|
src/ |
Solidity: the hooks, the pricing math (src/math), outcome tokens, demo contracts |
test/ |
Foundry tests: math, oracle, hook, integration, security and attack cases |
script/ |
Deploy, track, price-source, fund and withdraw scripts, the local fork environment and the rehearsal |
bot/ |
The price-mirror bot (a testnet stand-in for the arbitrage that would otherwise keep the demo pool near the real price), the keeper that opens every track's markets by calling its scheduler, and the sealed-oracle poke-and-prove bot |
packages/swap-sdk/ |
@phinary/swap-sdk: V4Quoter quotes, UniversalRouter encoding, revert decoding |
web/ |
The Phinary dashboard (Next.js) |
app/ |
The backup swap page |
interface-patches/ |
The Uniswap web app fork, as a patch |
sim/, formal/ |
Reference math, test vectors, z3 lemmas and monotonicity certificates |
docs/ |
The Phinary docs site (Nextra) |
docs/md/ |
Plan, spec, runbook, dashboard spec and research |
make build && make test # contracts and tests
make local-env # local fork of Unichain Sepolia with everything deployed and the bots running
make rehearse # scripted buy, sell, settle and redeem on that fork
cd web && npm install && npm run dev # dashboard at http://localhost:3100
cd docs && npm install && npm run dev # docs site at http://localhost:3200The live demo and deployment are covered in docs/md/RUNBOOK.md, the design in docs/md/PLAN.md and docs/md/SPEC.md, and the dashboard in docs/md/DASHBOARD.md.