Robinhood Mainnet
Robinhood mainnet · Loading deploymentStatus

Documentation

Multiasset transfers, account recovery and mainnet integration.

v0.3.0 · Robinhood Chain mainnet · Chain ID 4663

Use registered official Stock Tokens in Owly IQ's shared multiasset pool. Generate proofs on your device and submit transactions through your EVM wallet.

Getting started

Open the app on HTTPS with browser storage enabled. Account encryption uses Web Crypto; transaction locking uses Web Locks. Keep ETH in your browser wallet for Robinhood Chain gas.

  1. Open Recovery, create a privacy account, download the encrypted backup and verify its password.
  2. Connect your EVM wallet on Robinhood Chain mainnet, chain ID 4663.
  3. Open Wallet, choose an official Stock Token and sync. The app verifies its registration, contract code and pool state.
  4. Enter a deposit amount and review its public information. Generate the local proof, approve the exact allowance if required, and confirm the pool transaction in your wallet.
  5. Open Send, select the same token and enter the recipient's complete privacy address. Review the amount, protocol fee and ETH gas before confirming.
  6. Recipients sync the selected token in Wallet to recover their notes. Use Withdraw to send funds to a public EVM address.

Check Activity for transaction hashes. The app reports an L2 receipt after confirmation; Ethereum settlement happens later. Resolve any pending transaction before sending again.

Mainnet deployment

One multiasset contract holds the registered Stock Tokens. It uses one commitment tree with separate backing, fee and deposit cap for each token. Proofs bind the chain ID, pool address and selected token address. A transfer uses one token throughout.

Each registered token has its own manifest, even when the manifests point to the same pool contract. Select a token by its official address. The Contracts page lists deployed contract addresses and explorer links.

The launch configuration sets a 0.001-token fee per private transfer and a 100-token backing cap per asset. The fee also applies to note consolidation. Deposit and withdrawal have no protocol fee. Withdrawals free backing capacity; each token's USD value differs.

The app reads the current fee from the contract before preparing a proof. Governance can update fees and register additional official tokens after a 48-hour timelock. A new catalog listing becomes usable after contract registration and an updated verified manifest.

Recovering notes from an earlier deployment

If your notes belong to a previous contract, select its recovery pool in Wallet and sync before withdrawing. Existing notes stay bound to that contract. To move them into the current multiasset pool, withdraw to your public wallet and make a new deposit. Both operations reveal their amounts and EVM addresses.

New deposits use the multiasset pool. Token selection pauses while a submitted transaction needs confirmation. Known deployments remain available for recovery when catalog lookup fails; new deposits require an active official catalog entry.

SDK integration

Import the lib/v3 source modules from a project checkout. The SDK is not published to npm. Supply a viem public client, a verified token manifest, an unlocked privacy account and the user's EVM wallet provider.

mainnetClient() from lib/v3/chain.ts connects to the same-origin read-only /api/v3/rpc endpoint. Resolve the token manifest before syncing or preparing an operation.

Multiasset token selection
import { parsePoolRegistryV3, findPool } from './lib/v3/registry';

const response = await fetch('/api/v3/deployment');
if (!response.ok) throw new Error('Deployment unavailable');
const data = await response.json();
const registry = parsePoolRegistryV3({
  schemaVersion: 1, chainId: 4663,
  defaultAsset: data.defaultAsset, pools: data.pools
});
// Resolve by the verified official token address, not the ticker alone.
const manifest = findPool(registry, officialTokenAddress);
if (!manifest || manifest.poolKind !== 'multiasset')
  throw new Error('Token not registered in the multiasset pool');
// Use this manifest for sync, proving and submission of this asset only.
v3 transfer example
import { parseUnits } from 'viem';
import { makeNote, parsePrivacyAddress, prepareTransition,
  selectNotes } from './lib/v3/crypto';
import { sync, submitTransaction } from './lib/v3/chain';
import { generateProof } from './lib/v3/prover';

const state = await sync(rpc, manifest, account);
const amount = parseUnits('1.25', 18);
const fee = state.state.fee;
const inputs = selectNotes(state.notes, amount + fee);
const change = inputs.reduce((s, n) => s + n.note.amount, 0n)
  - amount - fee;
const outputs = [makeNote(amount, parsePrivacyAddress(recipient))];
if (change) outputs.push(makeNote(change, account.publicKey));

const prepared = prepareTransition({
  context: state.context, tree: state.tree,
  operation: 1, sender: account, inputs, outputs,
  protocolFee: fee, protocolRecipient: state.state.treasury,
  deadline: (await rpc.getBlock()).timestamp + 900n
});
// Display amount, fee, self-relay gas and expiry; obtain approval.
const proof = await generateProof(prepared, manifest);
const hash = await submitTransaction(
  rpc, ethereum, manifest, evmAddress, prepared, proof
);

Use the selected token's manifest for sync, proving and submission. Serve /zk/prover-worker.js and the manifest's artifact URLs. The browser worker checks artifact hashes, creates the proof and verifies it locally.

Serialize submissions across tabs. Recheck the account's encrypted-backup binding before proving and signing, record the returned transaction hash immediately, and resolve its receipt before retrying. submitTransaction() verifies deployment and current fees, simulates the transaction and asks the wallet to sign.

createPrivacyAccount()
Creates a random BabyJub spending scalar and public key.
privacyAddress() / parsePrivacyAddress()
Encodes and validates the mainnet address and checksum.
findPool()
Resolves the current registration by official token address.
sync()
Replays shared pool events, verifies the tree and spent nullifiers, and decrypts notes for the selected token.
selectNotes()
Selects up to two unspent notes. Consolidate fragmented balances before a larger spend.
prepareTransition()
Binds the operation to the chain, pool, token, fees, deadline and encrypted outputs.
generateProof()
Runs local Groth16 proving in a worker and checks the public signals.
approveDeposit()
Sets an exact ERC-20 allowance and checks the approval receipt.
submitTransaction()
Simulates, submits through the EVM wallet and waits for an L2 receipt.

Amounts & addresses

Registered Stock Tokens use 18 decimals. Use bigint raw units and viem parseUnits(text, 18) or formatUnits(raw, 18). Notes and public amounts support up to 2^120 - 1 raw units. Keep JavaScript floating point out of token accounting.

v3 balance example
import { formatUnits } from 'viem';
import { sync } from './lib/v3/chain';

const { notes, state } = await sync(rpc, manifest, account);
const raw = notes.reduce((sum, n) => sum + n.note.amount, 0n);
const display = formatUnits(raw, 18);
// Keep raw balances and decrypted history out of logs and analytics.

The issuer's multiplier affects display, while notes retain raw balances. Apply the multiplier according to the price source; applying it twice gives an incorrect displayed value.

Private recipients use complete sp3:4663: addresses with a checksum. Deposits and withdrawals use public EVM 0x addresses. A privacy address works across registered assets; each note remains bound to its token.

Wallet-based self-relay has zero relayer fee. Your connected EVM wallet pays ETH gas for approval and pool transactions. Review the total before signing.

Backup & recovery

One encrypted account backup recovers notes across registered tokens and supported earlier deployments. Keep the file and its password separately. The EVM wallet cannot restore the privacy account.

v3 recovery example
import { createPrivacyAccount, privacyAddress } from './lib/v3/crypto';
import { exportEncryptedBackup, restorePrivacyAccount } from './lib/v3/backup';
import { sync } from './lib/v3/chain';

const account = createPrivacyAccount();
const encrypted = await exportEncryptedBackup(account, password);
// Save the encrypted file and test it before the first deposit.
const recovered = await restorePrivacyAccount(encrypted, password);
const address = privacyAddress(recovered);
const { notes } = await sync(rpc, manifest, recovered);

Backups use AES-256-GCM and PBKDF2-SHA256 with 600,000 iterations. Use a password of 12 to 1,024 characters. Browser storage holds the encrypted key; unlocked keys and decrypted notes stay in memory.

Restore the file on a clean device, select each token and sync from its deployment block. The SDK rebuilds the tree from public events and checks roots, nullifiers and the scan block hash. It requires a new scan after a reorg. The current scanner starts from the deployment block on every sync.

The backup contains the key, so sending a transaction does not require a new backup. A new privacy account needs its own file. Replacing the stored backup in another tab locks the previous account; unlock the intended account before continuing.

Browser storage is separate for localhost and each hosted origin. The encrypted file remains portable. Keep the file and its password; the operator cannot recover a lost password or recreate a missing account backup.

HTTP API

Use same-origin requests from the hosted app. The API returns public catalog, deployment and release information. Never send a password, spending key, witness or decrypted note to it.

GET/api/v3/assets

Official catalog entries joined with token-specific manifests and registration status.

GET/api/v3/deployment?asset=NVDA

Registry and selected token manifest. Use a symbol or official token address; omit asset for the default registration.

GET/api/v3/status

Configured pool, registered asset count, proving artifacts and submission availability.

GET/api/v3/asset?asset=NVDA

Checks the selected official token against mainnet code and metadata. Returns 503 if verification is unavailable.

POST/api/v3/rpc

Read-only mainnet RPC for verification, event replay and simulation. Wallets broadcast transactions.

GET/api/v3/operator

Public release artifacts, deployment bytecode and internal acceptance evidence.

GET/api/health

Application, deployment and artifact availability.

Submission uses the browser wallet. Hosted relaying and hosted proving are unavailable. The retired v1 asset, pool and readiness endpoints return HTTP 410; integrations should use the v3 endpoints above.

Privacy boundaries

Internal transfer amounts and recipient keys stay encrypted. Nullifiers prevent spending the same note twice without publishing its input commitment. All assets share a tree, but the token used in each transaction remains public.

Deposit and withdrawal addresses and amounts, per-token backing, fees, ciphertext, timing and the self-relay gas payer are public. RPC providers can observe connection metadata. Matching deposit/withdrawal patterns and small activity sets can reduce practical privacy.

Governance can change fees and register tokens after the timelock. Governance and the guardian can pause an asset or the whole pool, including withdrawals. They have no spending key, administrative mint, upgrade function or backing-withdrawal privilege.

The deployed setup and review are operator-managed, with no independent audit or ceremony contributors. Verified artifacts establish consistency, while independent security clearance remains incomplete. This release has no selective-disclosure export.

Troubleshooting

WrongChain
Switch your EVM wallet to Robinhood Chain mainnet, chain ID 4663.
InvalidPrivacyAddress
Copy the complete sp3:4663: address and checksum. Use an EVM address only for withdrawals.
ConsolidationRequired
Combine two notes in Wallet, sync and repeat if necessary. Each combination pays the current fixed transfer fee.
UnknownRoot / Expired / Fees changed
Sync and prepare again. The proof uses a retained root, current fee and a 15-minute deadline.
ProofGenerationFailed
Retry on a desktop with enough memory. Check that artifact downloads and hashes succeeded.
PoolPaused
Check Status and wait for the affected operation to resume.
Pending transaction
Check the saved transaction hash and receipt before retrying. A timeout does not establish failure.
Account locked because its backup changed
Unlock or restore the intended account, sync and review the operation again.
Deployment unavailable
Retry verification. Check catalog and RPC availability; the app cannot submit without a valid manifest.
Open wallet