How QonStocks works
Concise, and specific about what is live, what is a preview, and what is not connected. Every number the product shows can be traced to a chain read at a stated block.
Overview
QonStocks looks for price differences of the same verified Stock Token across Uniswap pools on Robinhood Chain. It builds closed cycles that start and end in one settlement asset (USDG or WETH), quotes them with the on-chain quoter, itemises every cost, and simulates the exact executor call from your address. Where a verified executor is deployed, you can execute an eligible cycle atomically from your own wallet.
It has a token, $QONS, whose contract address is published on this site once it is live (CA: Soon until then) — and the app does not use it for anything: no staking, no treasury, no yield product, no fee discount or reward, and no promise of profit. Most of the time no route is eligible — an efficiently arbitraged market is the normal state, and the scanner says so.
Network
| Fact | Value | Source |
|---|---|---|
| Chain | Robinhood Chain · 4663 | docs.robinhood.com/chain/connecting + live eth_chainId |
| Type | Arbitrum Orbit L2, Ethereum blobs for data availability | same page |
| Gas token | ETH | same page |
| Explorer | robinhoodchain.blockscout.com | same page |
| Public RPC | https://rpc.mainnet.chain.robinhood.com | listed as rate-limited and not for production; a provider endpoint goes in QONSTOCKS_RPC_URL (server-side) |
| Testnet | 46630 — not used | Uniswap lists no deployment there, so nothing can be verified |
Blocks arrive about every 0.1 s. Cross-chain routes are out of scope: a cycle across two chains cannot be atomic.
Assets and pools
Assets. The asset list is the issuer’s Stock Token registry (https://api.robinhood.com/rhj/assets) — 195 tokens at block 71,231,041. A ticker proves nothing (the chain carries look-alikes), so each address must also be an ERC-1967 beacon proxy of the issuer’s beacon 0xe10b6f6B275de231345c20D14Ab812db62151b00 with matching symbol()/decimals(). Settlement assets: USDG 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 (6 dp) and WETH 0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73 (18 dp).
Pools. factory.getPool for every Stock Token × {USDG, WETH} × fee tier (0.01%, 0.05%, 0.3%, 1%), factory.getPair for V2, plus WETH/USDG connectors — asking the canonical factory is the membership proof. Stock/stock pools come from a bounded PoolCreated log scan; every entry is re-validated with getPool at run time.
Depth probes. A V3 position one tick wide reports huge liquidity and fills almost nothing. Every pool above the dust floor (25 USDG of +1 % depth) is probed with a real quote of up to 1000 USDG that must fill within 3% impact beyond its fee. Pools that fail are shown as 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: Uniswap v4 adapter not implemented: hooks can change fees and pricing, and the executor cannot settle v4 swaps.
Prices and quotes
- All pool state in a comparison is read in one multicall pinned to one block; the block travels with the data to the screen.
- Spot prices (V3 from
sqrtPriceX96, V2 from reserves) are indicative: before price impact, not an exchange price, not an oracle. - The raw spread — dearest vs cheapest verified pool of an asset — is a signal, never a result.
- Candidate cycles (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-capped
eth_call— at sizes of 5%, 15%, 40%, 100%, 250% of the route’s shallowest depth. V2 hops use the constant-product formula 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.
Cost accounting
| Line | How it is obtained |
|---|---|
| Raw price spread | Product of spot rates after pool fees, minus 1. Ranking only. |
| Quoted route output | QuoterV2 / V2 formula, hop by hop. Pool fees and price impact are already inside it. |
| Trading fees | Inside the quote — never subtracted a second time. |
| Flash-liquidity fee | None separate: a Uniswap flash swap repays the first pool in the settlement asset; that pool’s swap fee (inside the quote) is the borrowing cost. |
| Protocol fee | 0% by default, of a positive result only. Immutable per deployment, capped at 10%; the live value is read from the deployed contract. |
| Gas | eth_estimateGas of the actual call in the route panel (a forge-measured model × 1.25 in the scan), times the current gas price. |
| Gas in the settlement asset | Only from a credible, same-block rate: the deepest depth-verified WETH/USDG pool (≥ 10,000 USDG of depth), vetoed if fresh Chainlink ETH/USD ÷ USDG/USD disagrees by more than 2%. Otherwise gas stays in ETH and the net result is marked incomplete. |
| Estimated result after costs | Quoted result − protocol fee − gas, when gas could be converted. |
| Required minimum result | max(floor, estimate × (1 − tolerance)), enforced on-chain after the protocol fee. The floor defaults to the estimated gas cost. |
All token amounts are integers (bigint) in base units with the token’s own decimals; rounding is down for amounts you receive and up for costs.
Simulation
“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-profit rule, deadline window, wallet, chain and contract: changing any of them, or letting it age past 30 s, or getting within 20 s of its deadline, invalidates it. Deadlines (30–600 s) are measured in chain time.
While no executor is deployed, the same compiled bytecode is injected at a scratch address with 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 successful simulation is not a guarantee: state changes about ten times a second, other traders see the same pools, and a reverted transaction still costs gas.
Executor
Reading the chain…
Mechanics. The executor calls swap on the first pool; Uniswap sends the output first and calls back (uniswapV3SwapCallback / uniswapV2Call). Inside the callback the remaining hops run exact-input with the amounts actually received, the first pool is repaid, and profit is the increase of the contract’s settlement balance across the whole call. Your minimum is checked after the protocol fee and the rest is paid 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 calldata.
- Callbacks are accepted only during an execution, only from the single pool expected next, once; the flash payload is hash-pinned.
- Balances already in the contract cannot subsidise a cycle or leak: payout is the measured increase and a non-increase reverts.
- Received amounts must equal what the pool reported (rejects fee-on-transfer/rebasing); partial fills revert; tokens are allow-listed; 2–4 hops; deadline; immutable fee cap.
- Owner can: allow/disallow tokens, change the fee recipient, pause new executions, sweep non-fee balances. Owner cannot: change the fee or factories, redirect a caller’s profit, take user tokens, or upgrade (not a proxy).
Tested with unit tests on real Uniswap V2/V3 bytecode and mainnet-fork tests on real Stock Token pools (counts in the repository README). Not independently audited.
Transactions and history
Before your wallet opens, the call is re-quoted and simulated again on the server and from your browser; the wallet receives exactly those bytes. States are driven only by 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 — included 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 separately unless both are in ETH (WETH settlement). Reverted calls are read from the explorer; transactions this browser submitted are tracked by receipt. CSV export writes exact base units.
Integrations
| Integration | Status | Source / reason |
|---|---|---|
| Uniswap V2 factory | Supported | 0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f · developers.uniswap.org/deployments.json |
| Uniswap V3 factory · QuoterV2 | Supported | 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA · 0x33e885eD0Ec9bF04EcfB19341582aADCb4c8A9E7 · same registry; factory() and WETH9() cross-checked |
| Multicall3 | Used for reads | 0xcA11bde05977b3631167028862bE2a173976CA11 |
| Stock Token registry + beacon | Supported | https://api.robinhood.com/rhj/assets · beacon 0xe10b6f6B275de231345c20D14Ab812db62151b00 |
| Chainlink ETH/USD, USDG/USD | Cross-check only | 0x78F3556b67E17Df817D51Ef5a990cDaF09E8d3A9 · 0x61B7e5650328764B076A108EFF5fa7282a1B9aD2 · reference-data-directory (robinhood-mainnet) |
| Chainlink sequencer-uptime feed | Not available | Recommended by Robinhood’s 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, not used | Deployed on 4663 per Morpho’s address list. Uniswap flash swaps already fund every supported cycle at no extra fee; a second funding model would add attack surface without changing results. |
| Arcus, Lighter, aggregators | Not integrated | Order-book / perps venues without a verified on-chain quote interface for spot cycles; nothing is inferred from them. |
The full record — sources, dates, method and open dependencies — is in docs/INTEGRATIONS.md in the repository; pnpm verify:registry re-checks the typed registry (src/config/registry.ts) against its sources and the live chain.
Token
$QONS is the QonStocks token. Its contract address is published in exactly two places and nowhere else: the Token section of the landing page and GET /api/token. Both read the server-side setting QONS_TOKEN_ADDRESS, which pnpm token:ca 0x… sets only after the contract has been checked on Robinhood Chain (bytecode, name, symbol, decimals, total supply). Until then every surface shows $QONSCA: …; an address quoted anywhere else is not ours.
The app does not use the token for anything. Scanning, quoting, simulation and execution never read a $QONS balance; there is no staking, no treasury, no yield, no fee discount, no reward, no governance and no promised value. Once live, the site shows only what the chain reports at a stated block: the contract facts and, if a canonical Uniswap V2 / V3 pool against USDG or WETH holds liquidity in range, that pool’s spot price and +1 % depth with its venue and fee tier — indicative, before the pool fee and price impact. When there is no such pool, it says so.
Limitations
- A web interface is slow next to a co-located bot. Expect opportunities to vanish before inclusion; reverts cost gas.
- The +1 % depth figure is an indicator; probes verify only up to 1000 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, with the real error.
- Stock Tokens can be paused, block-listed or upgraded by their issuer; a cycle touching one then reverts.
Developer 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, reference rate and v4 summary at one block. |
GET /api/pools/v4 | Uniswap v4 pools between verified assets (listed, unsupported), paginated. |
GET /api/routes?settlement=USDG|WETH&maxHops=2..4&token=0x… | Quoted cycles 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 | $QONS: "soon", or the contract address with name, symbol, decimals, total supply, canonical pools and market, read at one block. |