How CycleLabs works

Create token markets with supported stock-token pairs and transparent, on-chain fee distribution. This page is generated from the same configuration the app runs on; addresses and dates below are the ones in config/networks/robinhood.json.

Overview

Three modules, one honest boundary:

  • Cycle Launch creates a fixed-supply ERC-20, pairs it with a verified quote asset (a Robinhood Stock Token or USDG) in a Uniswap v3 pool, and fixes the fee recipients — all in one transaction.
  • Cycle Explorer lists real pools from indexed events with liquidity, volume, price, collected fees and distribution status.
  • Cycle Fees follows fees from the pool through collection, allocation and claims, per asset and per recipient.

A pool paired with a Stock Token is not a token backed by shares. Launching a token does not give its holders ownership of the paired stock, a claim on its issuer, or any guarantee of value. A pool paired with a Stock Token holds Stock Tokens as one side of its liquidity; the launched token confers no rights over them.

Fee mechanism

The one mechanism CycleLabs uses is the fee accounting of Uniswap v3 itself:

  1. Each launch seeds a full-range Uniswap v3 position. The position NFT is minted to CyclePositionVault, which can never transfer it.
  2. Swaps pay the pool's fee tier. Uniswap credits a position's share as tokensOwed, separately from its liquidity (principal).
  3. collectFees(launchId) — callable by anyone — calls the position manager's collect with the launch's CycleFeeDistributor as recipient, then syncs the distributor. Only fees can take this path.
  4. The distributor recognises what arrived (Received) and lets each immutable recipient pull floor(accounted × share ÷ 10,000) − claimed. Rounding dust stays inside and is bounded by recipients − 1 wei per asset for the contract's lifetime.
  5. Principal leaves only through withdrawLiquidity (creator, after the lock): it collects pending fees to the distributor first, then decreases liquidity and collects principal to the creator's address in the same transaction, asserting that the two amounts match. Nothing can interleave.

What is not counted: fees earned by other liquidity providers in the same pool, and fees the position has earned but nobody has collected (shown as an estimate from a static call, never as revenue). There is no protocol fee in this version.

Contracts & permissions

ContractRolePrivileged functions
CycleTokenFixed-supply ERC-20 (+ permit)none — no mint, burn hook, tax, blocklist, pause or owner
CycleLaunchFactoryCreates launches, emits LaunchCreated / LaunchTermsowner (2-step): setQuoteAsset (future launches only), setPaused (new launches only). No power over existing launches or funds.
CyclePositionVaultOwns every position NFT; collects fees; withdraws principalwithdrawLiquidity: launch creator, after unlockAt, never with a permanent lock. Creator role hand-over is two-step. No admin.
CycleFeeDistributorPer-launch minimal proxy; pull claimsnone — recipients and shares are immutable; anyone may sync or claimFor (funds always go to the recipient).

Build: solc 0.8.26+commit.8a97fa7a, EVM paris, optimizer runs 800, via-IR false. Libraries: OpenZeppelin Contracts 5.4 (ERC20, ERC20Permit, Ownable2Step, Clones, SafeERC20, ReentrancyGuard, Math).

Known limitations: the token address is predictable, so a pool could in theory be pre-initialised at another price before a launch; the vault then reverts with PoolPriceMismatch and no funds move. Quote assets that rebase or take transfer fees are rejected or unsupported by design. Contracts are unaudited.

Not deployed on Robinhood Chain. Addresses appear here only from a recorded broadcast (see docs/DEPLOYMENT.md).

Integration registry

Verified 2026-09-27 at block 74296929. Re-run pnpm verify:config to re-check every fact live.

  • https://docs.robinhood.com/chain/connecting — chain id 4663 (testnet 46630), ETH gas, explorer robinhoodchain.blockscout.com, public RPC rpc.mainnet.chain.robinhood.com (rate-limited; providers recommended for production)
  • https://docs.robinhood.com/chain/protocol-contracts — L2 WETH 0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73
  • https://developers.uniswap.org/deployments.json — records with chainId 4663, tier labs-supported, status active (sourceRef 56928a9)
  • https://sourcify.dev/server/v2/contract/4663/<address> — UniswapV3Factory, NonfungiblePositionManager and QuoterV2 are Sourcify matches of solc 0.7.6 builds; SwapRouter02 has no Sourcify record but is in Uniswap's registry and its factory() returns the v3 factory
  • eth_call on 4663: factory.feeAmountTickSpacing = {100:1, 500:10, 3000:60, 10000:200}; positionManager.factory() and quoter.factory() return the factory; positionManager.WETH9() returns the documented L2 WETH
Uniswap v3Address
Factory0x1f7d7550B1b028f7571E69A784071F0205FD2EfA
NonfungiblePositionManager0x73991a25C818Bf1f1128dEAaB1492D45638DE0D3
QuoterV20x33e885eD0Ec9bF04EcfB19341582aADCb4c8A9E7
SwapRouter020xCaf681a66D020601342297493863E78C959E5cb2
WETH90x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73
Fee tiers100 (spacing 1), 500 (spacing 10), 3000 (spacing 60), 10000 (spacing 200)
Quote assetKindAddressDecimalsPrice feed
NVDAISIN US67066G1040stock-token0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC180x379EC4f7C378F34a1B47E4F3cbeBCbAC3E8E9F15
AAPLISIN US0378331005stock-token0xaF3D76f1834A1d425780943C99Ea8A608f8a93f9180x6B22A786bAa607d76728168703a39Ea9C99f2cD0
TSLAISIN US88160R1014stock-token0x322F0929c4625eD5bAd873c95208D54E1c003b2d180x4A1166a659A55625345e9515b32adECea5547C38
SPYISIN US78462F1030stock-token0x117cc2133c37B721F49dE2A7a74833232B3B4C0C180x319724394D3A0e3669269846abE664Cd621f9f6A
MSFTISIN US5949181045stock-token0xe93237C50D904957Cf27E7B1133b510C669c2e74180x45C3C877C15E6BA2EBB19eA114Ea508d14C1Af2E
AMZNISIN US0231351067stock-token0x12f190a9F9d7D37a250758b26824B97CE941bF54180xD5a1508ceD74c084eBf3cBe853e2C968fB2a651C
GOOGLISIN US02079K3059stock-token0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3180xF6f373a037c30F0e5010d854385cA89185AE638b
METAISIN US30303M1027stock-token0xc0D6457C16Cc70d6790Dd43521C899C87ce02f35180x7C38C00C30BEe9378381E7B6135d7283356D71b1
QQQISIN US46090E1038stock-token0xD5f3879160bc7c32ebb4dC785F8a4F505888de68180x80901d846d5D7B030F26B480776EE3b29374C2ae
AMDISIN US0079031078stock-token0x86923f96303D656E4aa86D9d42D1e57ad2023fdC180x943A29E7ae51A4798823ca9eEd2ed533B2A22C72
USDGstablecoin0x5fc5360D0400a0Fd4f2af552ADD042D716F1d16860x61B7e5650328764B076A108EFF5fa7282a1B9aD2

Every official Stock Token is a BeaconProxy whose beacon slot holds the issuer's AccessControlsRegistry (also the beacon). The implementation `Stock` (solc 0.8.33, Sourcify exact match) is a scaled-UI ERC-20: balanceOf/transfer are raw units and never rebase (uiMultiplier is display-only), there is no transfer fee, transfers revert with Blocked() for blocklisted from/to/spender and with IsPaused() while the token or the registry is paused, and the issuer holds adminBurn, pause and upgrade roles. Holding a Stock Token or an LP position over it is not ownership of the underlying share: see the issuer's terms.

Chainlink Data Feeds, directory https://reference-data-directory.vercel.app/feeds-robinhood-mainnet.json (58 feeds, fetched 2026-09-27); official page https://docs.robinhood.com/chain/oracles-and-price-feeds. AggregatorV3Interface proxies, 8 decimals, 24 h heartbeat. Stock feeds price ONE token (the display multiplier is already applied). Equity feeds follow market hours, so on weekends the answer can be many hours old: the UI shows the feed's updatedAt next to every USD estimate.

Data & indexing

  • Source of truth: events of the factory, the vault, each launch's distributor and each launch's pool (Swap, Mint, Burn, Collect). Token transfers are never counted as swaps.
  • Backfill from the deployment block; adaptive batch size; retries with backoff on rate limits and truncated batches.
  • Idempotent rows keyed by (tx hash, log index); checkpoint advanced only after a range commits.
  • Reorg recovery from a stored block-hash trail; finality depth 64 blocks separates "indexed" from "final".
  • Pool and position state (price, liquidity, balances, uncollected estimate) is read from the chain after each sync, never derived from events.
  • Indexing progress (indexed block, last sync, worker heartbeat) is reported separately from live chain status on /api/health.
  • Creator metadata is sanitised, content-addressed and served from this origin; the server never fetches creator-supplied URLs.
  • Chain-direct fallback: when the index database is unavailable, the API serves the current on-chain state instead (launches from the factory, pool and position state, distributor ledgers, claimable amounts) and every page says so. Volume, charts and history are then shown as unavailable, never as zero.

Environments

  • local — Anvil + a throwaway Uniswap v3 + dev quote fixtures (clearly labelled) + PGlite. The whole flow runs here: pnpm chain:local, pnpm dev:local, pnpm worker:local, pnpm e2e:local.
  • robinhood-testnet (46630) — no Uniswap v3, no Stock Tokens, no feeds: on-chain actions are disabled and the UI says why.
  • robinhood (4663) — official Uniswap v3 deployments and Stock Tokens; CycleLabs contracts only after a real broadcast is recorded.

Local fixtures live in their own database schema and can never populate production analytics.

Token

$CYCLELABS is the project token on Robinhood Chain. Its contract address is published only on the home page token card and the footer, from a single configuration value that is verified on chain before it goes live (pnpm token:ca 0x…). Current status: CA soon — no address has been published. A plain token. It gives no fee share, discount, allow-list access or governance in CycleLabs. Only the address published here is official.

Operations

  • Deploying the contracts: /deploy lets the operator sign the deployment from their own wallet (factory creation + one allow-list transaction per quote asset, optional ownership hand-over). The page produces a manifest; pnpm contracts:ca <manifest> --deploy re-verifies every address on chain, records contracts/deployments/4663.json and redeploys the site. No server key is involved.
  • Publishing the token address: pnpm token:ca 0x… checks the address is a live ERC-20 with symbol CYCLELABS on Robinhood Chain and not a quote asset or protocol contract, then sets it for production and redeploys; pnpm token:ca SOON reverts to "CA soon".
  • Registry re-verification: pnpm verify:config robinhood --write re-checks chain id, Uniswap addresses, fee tiers, Stock Token beacons, issuer registry entries and price feeds, and stamps the date shown above.

Readiness report

Working end to end on the local chain: launch creation, pool initialisation at the contribution ratio, swaps through SwapRouter02, permissionless fee collection, exact pro-rata claims, principal withdrawal with fee separation, indexing with idempotency and reorg recovery, explore / pool / fee read models, metadata and logo handling, CSV export. Verified by 47 Foundry tests (unit, fuzz, invariant), unit tests, and the e2e:local script.

Not yet true for Robinhood Chain: no CycleLabs contracts are deployed, so launching, collecting and claiming are unavailable until an authorised deployment is recorded. Uniswap v3, Stock Tokens and Chainlink feeds on this network are verified and would be used as-is. The contracts are unaudited. Passing local tests is not production readiness.