Skip to content

Hood market integration

This page describes the minimum on-chain information required to integrate Candle markets on Robinhood Chain.

PropertyValue
Chain ID4663
Native assetETH
RPChttps://rpc.mainnet.chain.robinhood.com
Explorerhttps://robinhoodchain.blockscout.com

There are two canonical contract generations, and both are live. Candle redeployed its launch stack at block 30442973 (2026-08-07) to add on-chain token metadata. Curves created before that block keep trading and migrating through the first-generation contracts indefinitely, so an integrator must index both generations; treating the first generation as deprecated will break live markets. Branch on block height, not on a date.

Each generation’s identity root is its CandleCurveRegistry, permanently bound to its CandleCurveFactory:

GenerationRegistryFactory
Current (block ≥ 30442973)0x75934c16162075583435358f9F042D8D6E42Bd5C0x63a289eB0F620fE7701D314022F52783547D8F3a
First (legacy, still live)0xFD54D094ad93349CA316bA309A3C8573E3D6C0cd0xdaCfdBbb62FFEB32917547e130464CDc9747BdC2

On the current generation, curve contracts are CREATE2-deployed by a separate CandleCurveDeployer (0x15257Bf766509388e72b523F1A0bF2C1697eE4F1), not by the factory itself. To derive a curve address ahead of time, read the deployer from factory.deployer() rather than hardcoding it; the factory no longer exposes predictCurveAddress.

Index successful CurveRegistered logs emitted by both canonical registries. Each log identifies:

  • The curve contract.
  • The launched base-token contract.
  • The quote token (address(0) means native ETH).
  • The approved migrator.
  • The per-market fee recipient.
  • The curve configuration hash and implementation version.
  • The CNDL-staker allocation.

The factory emits CurveCreated in the same transaction with the creator, DEX version, configuration hash, and launcher salt. Treat the registry log as the admission proof and the factory log as additional launch metadata.

For a candidate curve:

  1. Call isRegistered(curve) on each canonical registry and require true on one of them.
  2. Call getRecord(curve) on that registry and require active == true.
  3. Require curveForBaseToken(record.baseToken) == curve.
  4. Read execution and lifecycle state from that curve address.

For a candidate token, call curveForBaseToken(token) on each registry. A zero address on both means it is not a canonically registered Candle launch. For a nonzero result, perform the curve checks above and require getRecord(curve).baseToken == token.

Contract roleIdentification rule
CurveActive record on either canonical registry
Launched tokenReverse mapping from curveForBaseToken to an active curve record
v3 curveRecord migrator is 0x1F495F6326908f8e595b18BF6754F2a066Fd3846 (current) or 0x5A51Fc39cFA93B14bD184D76F7Dc2C00aC13620b (first generation)
v4 curveRecord migrator is 0xeB4aAF61c5BC8c5cc6136dd8D9fdd89146B8D59a (current) or 0x934349F158b9e2BCA0d8324626962C01a1De6Bd0 (first generation)
Fee recipient/splitterAddress recorded for that curve; do not assume one global recipient
Migrated poolConfirmed curve lifecycle plus the canonical migrator’s migration event

The migrator is also readable directly from any curve as migrator(); it is immutable per curve. Never assume one global migrator.

Do not identify Candle contracts by contract name, matching function selectors, bytecode similarity, token symbol, deployer history, or an API label. Those are useful discovery hints but can be copied by another deployment. The canonical registry record is the provenance check.

Native-ETH curves use:

buy(uint256 minAmountOut) payable
sell(uint256 baseIn, uint256 minAmountOut)

ERC-20-quoted curves require approval of the quote token followed by:

buyWithQuote(uint256 quoteIn, uint256 minAmountOut)

Selling requires approval of the launched base token to the curve.

Before building calldata, read the curve’s quote token and lifecycle. Calling the native buy on an ERC-20 curve, or the ERC-20 buy on a native curve, reverts with WrongQuoteMode.

A buy that would cross the threshold is capped. Use the capped quote when calculating minAmountOut; the remainder is returned as refund in the Bought event and transaction accounting.

Once lifecycle is Graduated, both buys and sells are closed. Do not route to v3 or v4 until migration is confirmed.

  • Fee: 3000
  • Tick spacing: 60
  • Factory: 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA
  • WETH: 0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73

The v3 pool is normally pre-created when the curve is launched. Pool existence is therefore not proof that migration has occurred. Route only after the curve reaches Migrated and the migrator’s activation transaction is confirmed.

  • PoolManager: 0x8366a39CC670B4001A1121B8F6A443A643e40951
  • Fee: 3000
  • Tick spacing: 60
  • Hook: 0x299FbBECBdBec82B56117d7593a89fe64F77a000 (current generation) or 0x000509f35530F91068684Fef08AF0405486eE000 (first generation)

The v4 PoolKey must include the correctly ordered currencies, fee, tick spacing, and hook. Native ETH is represented by the zero-address currency in the PoolKey, and it always sorts first. Use the hook that matches the curve’s generation (which registry it is registered on); a PoolKey built with the other generation’s hook derives a different PoolId and finds nothing. The pool does not exist before migration.

Index at minimum:

  • Factory CurveCreated
  • Registry CurveRegistered
  • Curve Bought, Sold, Graduated, LifecycleChanged, and MigratedToDex
  • Curve recovery events
  • v3/v4 migrator Migrated and fee-collection events
  • Fee splitter distribution, deferred payout, and claim events

Tokens launched by the current generation (block ≥ 30442973) carry their metadata on-chain, and the token contract is authoritative for it:

  • Getters: description(), image(), socials() (a JSON string), metadataVersion() (0 means never edited), and creator().
  • tokenURI() returns base64 JSON for ERC-721-convention tooling but omits socials; prefer the individual getters.
  • Only the creator can revise metadata, via setMetadata. Every revision emits MetadataUpdated(address indexed editor, uint64 indexed version, string description, string image, string socials), and version 0 is emitted from the constructor, so an indexer can rebuild current metadata from logs alone without any eth_call.

First-generation tokens have none of these fields; the calls revert, permanently. They cannot be retrofitted, so a reverting metadata getter on an older token is expected state, not an error.

On-chain registry and contract state are authoritative for market identity and execution. Candle and HMC provide lower-latency discovery, images, social links, candles, trades, holders, and wallet enrichment. Treat those services as indexing and metadata layers rather than replacements for canonical contract checks.

The current-generation contracts are source-verified on Blockscout, so the full ABI for any address on this page is available from the explorer (the Contract tab, or GET https://robinhoodchain.blockscout.com/api/v2/smart-contracts/{address}). Per-launch curves and tokens share one implementation each, so a verified instance covers the rest by bytecode match.

For convenience, the exact surface this page documents, as human-readable fragments accepted directly by viem’s parseAbi and ethers’ Interface:

// CandleCurveRegistry (either generation)
const registryAbi = parseAbi([
"struct CurveRecord { address baseToken; address quoteToken; address migrator; address feeRecipient; bytes32 configHash; bytes32 implementationVersion; uint16 stakerAllocationBps; bool active; }",
"function isRegistered(address curve) view returns (bool)",
"function getRecord(address curve) view returns (CurveRecord memory)",
"function curveForBaseToken(address baseToken) view returns (address)",
"event CurveRegistered(address indexed curve, address indexed baseToken, address indexed quoteToken, address migrator, address feeRecipient, bytes32 configHash, bytes32 implementationVersion, uint16 stakerAllocationBps)",
])
// CandleCurveFactory (current generation)
const factoryAbi = parseAbi([
"function deployer() view returns (address)",
"event CurveCreated(address indexed curve, address indexed baseToken, address indexed creator, uint8 dexVersion, bytes32 configHash, bytes32 launcherSalt)",
])
// CandleCurve (per launch)
const curveAbi = parseAbi([
"function baseToken() view returns (address)",
"function quoteToken() view returns (address)",
"function lifecycle() view returns (uint8)",
"function migrator() view returns (address)",
"function migrationThreshold() view returns (uint256)",
"function stakerAllocationBps() view returns (uint16)",
"function buy(uint256 minAmountOut) payable returns (uint256 baseOut)",
"function buyWithQuote(uint256 quoteIn, uint256 minAmountOut) returns (uint256 baseOut)",
"function sell(uint256 baseIn, uint256 minAmountOut) returns (uint256 quoteOut)",
"event Bought(address indexed buyer, uint256 quoteIn, uint256 baseOut, uint256 refund)",
"event Sold(address indexed seller, uint256 baseIn, uint256 quoteOut)",
"event Graduated(uint256 quoteReserve)",
"event MigratedToDex(uint256 baseToMigrator, uint256 quoteToMigrator)",
"event LifecycleChanged(uint8 indexed previous, uint8 indexed next)",
])
// CandleToken (current generation only; these revert on first-generation tokens)
const tokenAbi = parseAbi([
"function decimals() view returns (uint8)",
"function creator() view returns (address)",
"function description() view returns (string)",
"function image() view returns (string)",
"function socials() view returns (string)",
"function metadataVersion() view returns (uint64)",
"function tokenURI() view returns (string)",
"event MetadataUpdated(address indexed editor, uint64 indexed version, string description, string image, string socials)",
])