How Kerf works
Short, and precise about what is live, what is a preview and what is not connected. Every number the product shows traces back to a chain read at a stated block.
01Overview
Kerf looks for price differences in the same verified stock token across Uniswap pools on Robinhood Chain. It assembles closed loops that begin and end in one settlement asset (USDG or WETH), prices them with the on-chain quoter, lists every cost on its own line, and replays the exact executor call from your address. When a verified executor is deployed you can run an eligible loop atomically from your own wallet.
There is a token, $KERF, whose address is published on this site once it exists — and the app does not use it for anything. Most of the time no loop is eligible. An efficiently arbitraged market is the normal state, and the scanner tells you so.
02Network
| Fact | Value | Where it comes from |
|---|---|---|
| Chain | Robinhood Chain · 4663 | Operator docs + live eth_chainId |
| Type | Arbitrum Orbit L2, Ethereum blobs for data | Operator docs |
| Gas token | ETH | Operator docs |
| Explorer | robinhoodchain.blockscout.com | Operator docs |
| Public RPC | https://rpc.mainnet.chain.robinhood.com | Rate-limited, not for production — put a provider URL in KERF_RPC_URL (server-side) |
| Testnet | 46630 — unused | No Uniswap deployment is listed there, so nothing can be verified |
Blocks land roughly every 0.1 s. Cross-chain loops are out of scope: a cycle spread across two chains can never be atomic.
03Assets and pools
Assets. The asset list is the issuer’s stock-token registry (https://api.robinhood.com/rhj/assets). A ticker proves nothing — look-alike tokens exist — so each address must also be an ERC-1967 beacon proxy of the issuer beacon 0xe10b6f6B275de231345c20D14Ab812db62151b00 with matching symbol() and decimals(). Settlement assets are USDG 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 (6 decimals) and WETH 0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73 (18 decimals).
Pools. For every stock token × {USDG, WETH} × fee tier (0.01, 0.05, 0.30, 1 %) we call factory.getPool, plus factory.getPair for V2 and the WETH/USDG connectors. Asking the canonical factory is the membership proof. Stock/stock pools are found with a bounded PoolCreated scan and re-checked with getPool at run time.
Depth probes. A V3 position one tick wide can report huge liquidity and fill almost nothing. Every pool above the dust floor (25 USDG of +1 % depth) is sent a real quote of up to 1,000 USDG that must fill within 3 % impact beyond its fee. Pools that fail are marked hollow and excluded from spreads and routing.
Uniswap v4. v4 pools between verified assets are listed from a PoolManager census as an unsupported venue. They are never priced or routed: hooks can change fees and pricing, and the executor cannot settle v4 swaps.
04Prices and quotes
- All pool state in one comparison is read in a single multicall pinned to one block; that block number travels with the data to the screen.
- Spot prices (V3 from
sqrtPriceX96, V2 from reserves) are indicative: before impact, not an exchange price, not an oracle. - The raw spread — dearest minus cheapest verified pool of an asset — is a signal and never a result.
- Candidate loops of 2–4 distinct pools are ranked by spot edge; the best 48 are quoted hop by hop with QuoterV2 (
0x33e885eD0Ec9bF04EcfB19341582aADCb4c8A9E7), each quote an isolated, gas-cappedeth_call, at 5, 15, 40, 100 and 250 % of the loop’s shallowest depth. V2 hops use constant-product maths on same-block reserves. - Quotes expire after 20 s. The scanner may show a scan up to 90 s old while it refreshes — every row shows its age — and the route panel always re-quotes at the newest block.
05Cost accounting
| Line | How it is obtained |
|---|---|
| Raw price spread | Product of spot rates after pool fees, minus 1. Used only for ranking. |
| Quoted route output | QuoterV2 or V2 formula, hop by hop. Pool fees and impact are already inside it. |
| Trading fees | Inside the quote; never subtracted a second time. |
| Flash-liquidity fee | None on top: the flash swap repays the first pool in the settlement asset, and that pool’s swap fee is the whole borrowing cost. |
| Protocol fee | 0 % by default, and only of a positive result. Fixed per deployment, capped at 10 %; the live value is read from the contract. |
| Gas | eth_estimateGas of the real call in the route panel (a measured model × 1.25 in the scan), times the current gas price. |
| Gas in the settlement asset | Only via a credible same-block rate: the deepest depth-verified WETH/USDG pool (≥ 10,000 USDG deep), vetoed if Chainlink ETH/USD ÷ USDG/USD disagrees by more than 2 %. Otherwise gas stays in ETH and the net is flagged incomplete. |
| Estimated result after costs | Quoted result − protocol fee − gas, when gas could be converted. |
| Required minimum result | max(floor, estimate × accepted share), enforced on-chain after the protocol fee. The floor defaults to the estimated gas. |
On the backend every amount is an integer in base units with the token’s own decimals. Amounts you receive round down; costs round up.
06Simulation
“Simulate exact call” runs execute(hops, amountIn, minProfit, deadline) in an eth_call at the latest block, from your address (a placeholder before you connect), and estimates its gas. The response echoes the exact calldata. A simulation is bound to the route, amount, minimum rule, deadline, wallet, chain and contract: change any of them, let it age past 30 s, or get within 20 s of its deadline, and it no longer counts. Deadlines (30–600 s) are measured in chain time.
While no executor is deployed, the same compiled bytecode is injected at a scratch address through an eth_call state override and run against live state — labelled preview. It shows what the contract would do; there is nothing to sign.
A passing simulation is not a guarantee. State changes about ten times a second, other traders watch the same pools, and a reverted transaction still costs gas.
07Executor
Mechanics. The executor calls swap on the first pool; Uniswap sends the output first and then calls back (uniswapV3SwapCallback / uniswapV2Call). Inside the callback the other hops run exact-input on the amounts actually received, the first pool is repaid, and profit is the rise in the contract’s settlement balance over the whole call. Your minimum is checked after the protocol fee and the rest is sent to you — in one transaction or not at all. You supply no tokens and grant no allowance.
- Every hop must be the pool the immutable V2/V3 factory returns for its tokens and fee: no arbitrary call targets, no caller-supplied calldata.
- Callbacks are accepted only during an execution, only from the single pool expected next, and only once; the flash payload is hash-pinned.
- Balances already sitting in the contract can neither subsidise a loop nor leak: payout is the measured increase, and no increase means revert.
- Received amounts must equal what the pool reported (rejecting fee-on-transfer and rebasing tokens); partial fills revert; tokens are allow-listed; 2–4 hops; deadline; immutable fee cap.
The owner can allow or block tokens, change the fee recipient, pause new executions and sweep non-fee balances. The owner cannot change the fee or the factories, redirect a caller’s profit, take user tokens, or upgrade the contract (it is not a proxy).
Covered by unit tests on real Uniswap V2/V3 bytecode and mainnet-fork tests on real stock-token pools. Not independently audited.
08Transactions and history
Before your wallet opens, the call is re-quoted and simulated again, on the server and in your browser; the wallet receives exactly those bytes. States move only on real events: re-simulated → awaiting wallet → submitted → pending → confirmed or reverted (plus rejected, replaced and cancelled). Success exists only after a receipt with status success; proceeds are decoded from its CycleExecuted event.
History rows come from CycleExecuted events and receipts. Each carries a confirmation state from the node’s block tags — on L2, batch posted to L1 (safe), or finalized on L1 — and the last 256 blocks are re-scanned on every pass so a reorganised execution disappears. Proceeds and gas are shown apart unless both are in ETH. Transactions this browser submitted are tracked by receipt. CSV export writes exact units.
09Integrations
| Integration | Status | Source / reason |
|---|---|---|
| Uniswap V2 factory | supported | 0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f · Uniswap deployments registry |
| Uniswap V3 factory · QuoterV2 | supported | 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA · 0x33e885eD0Ec9bF04EcfB19341582aADCb4c8A9E7 · factory() and WETH9() cross-checked |
| Multicall3 | used for reads | 0xcA11bde05977b3631167028862bE2a173976CA11 |
| Stock token registry + beacon | supported | https://api.robinhood.com/rhj/assets · 0xe10b6f6B275de231345c20D14Ab812db62151b00 |
| Chainlink ETH/USD, USDG/USD | cross-check only | 0x78F3556b67E17Df817D51Ef5a990cDaF09E8d3A9 · 0x61B7e5650328764B076A108EFF5fa7282a1B9aD2 |
| Chainlink sequencer-uptime feed | not available | Recommended by the chain docs; none is listed for chain 4663. |
| Uniswap v4 PoolManager | listed, not routed | 0x8366a39CC670B4001A1121B8F6A443A643e40951 · hooks can change pricing; no v4 settlement adapter |
| Morpho Blue flash loans | available, unused | Uniswap flash swaps already fund every supported loop at no extra fee; a second funding path would add attack surface and change nothing. |
| Order-book / perps venues, aggregators | not integrated | No verified on-chain quote interface for spot loops; nothing is inferred from them. |
The typed registry lives in src/config/registry.ts; the backend should re-check it against its sources and the live chain on start-up.
10Token
$KERF is the Kerf token. Its contract address is published in exactly two places: the token section of the home page and the pill in the header. Anything else claiming to be $KERF is not.
The app does not use the token. Scanning, quoting, simulation and execution never read a $KERF balance: no staking, no treasury, no yield, no fee discount, no reward, no governance, no promised value. Once live, the site shows only what the chain reports at a stated block — the contract facts and, if a canonical V2/V3 pool against USDG or WETH holds liquidity in range, its spot price and +1 % depth. If there is no such pool, it says so.
11Limitations
- A web interface is slow next to a co-located bot. Expect gaps to close before inclusion; reverts cost gas.
- The +1 % depth figure is an indicator; probes only verify up to 1,000 USDG.
- Sizes come from a five-step ladder, not a continuous optimiser.
- The public RPC throttles and is not archival; the last good read stays on screen, marked stale, next to the real error.
- Stock tokens can be paused, block-listed or upgraded by their issuer; a loop touching one then reverts.
12Developer interface
struct Hop { address pool; address tokenIn; address tokenOut; uint24 fee; uint8 kind; } // kind 0 = V2, 1 = V3
function execute(Hop[] calldata hops, uint256 amountIn, uint256 minProfit, uint256 deadline)
external returns (uint256 userProfit, uint256 protocolFee);
event CycleExecuted(address indexed caller, address indexed settlementToken, uint256 amountIn,
uint256 grossProfit, uint256 userProfit, uint256 protocolFee, address[] pools);| Endpoint | Returns |
|---|---|
GET /api/markets | Pools, prices, depth, probes and gas reference at one block. |
GET /api/pools/v4?page=&includeExtreme= | Uniswap v4 pools between verified assets (listed, unsupported), paginated. |
GET /api/routes?settlement=USDG|WETH&maxHops=2..4&token=0x… | Quoted loops with accounting, rejection reasons and excluded pools. |
POST /api/quote | Fresh quote for one route; with simulate: true, the exact-call simulation and its calldata. |
GET /api/activity?caller=0x…&page=1&limit=25 | Receipt-derived history with confirmation state. |
GET /api/status | Network, finality tags, adapters, freshness, registry provenance, executor checks. |
GET /api/token | $KERF: “soon”, or the address with name, symbol, decimals, supply and market at one block. |