On this page

Local Development & Testing

Start a private fork, run the API and app, and test against the contracts from your own build.

How the development runtime works#

The protocol repository includes Solidity contracts, Circom relations, the browser app, SDK and local runtime. The runtime starts a disposable fork, deploys a fresh Pool and its companion contracts, and runs a loopback API. The browser worker prepares proofs while the holder's private witnesses and note secrets stay in the browser.

Use the provisioned Linux development host for protocol builds, proof generation, Solidity suites and funded browser acceptance. Give each run a fresh output directory and keep its source and build manifests. For an integration test, start the runtime alongside your frontend preview so the browser can reach the chain and test contracts.

The app's fork chain ID is 31350. The fork reads Ethereum mainnet state at block 26038058, hash 0x67624a72ab377cbdd7a963398e20308bb56ce8cae568dde56d9ba1d99b8a2940. The bootstrap selects a free local RPC port and creates disposable accounts. Find the addresses and port in that run's generated configuration instead of assuming Anvil's defaults.

How the wallet, Pool and disclosure tools fit together
How the wallet, Pool and disclosure tools fit together

Install the source toolchain#

Install Node 24, Python 3, Git LFS and the project's Foundry/Anvil toolchain. The browser package uses ethers 5.8.0, snarkjs 0.7.6, TypeScript 5.9.3 and Vite 7.2.2. Install dependencies from the lockfiles so your build and artifact checks use those versions.

Record the source revision before starting a test run. These guides refer to 4def6e17d671d284e755a03d07d99a925b631512. If you test a newer revision, record that revision with its own results. If the test includes uncommitted changes, also save those source changes so someone else can reproduce the same build.

bash
# Run on the Linux development host in the protocol checkout.
node --version                  # Node 24
git rev-parse HEAD
git status --short
git lfs pull
npm ci --ignore-scripts
npm ci --prefix apps/privacy --ignore-scripts
# Needed when rebuilding circuits:
npm ci --prefix circuits --ignore-scripts

Start the fork and API#

Run the fork bootstrap and API in separate Linux terminals. Set FUYU_QUEUE_RUNTIME_DIR to the same fresh absolute directory in both. Set FUYU_ANVIL to the binary you want to use. FUYU_MAINNET_RPC_URL provides read-only access to the historical upstream state; keep any endpoint credentials in the environment.

The bootstrap writes config.json, deploys the test contracts and checks the fork's external state. The API listens on 127.0.0.1:8796 by default and provides local /api, /rpc and /artifacts routes. Set FUYU_QUEUE_API_PORT if you need another API port. Read the bootstrap output and config for the fork's RPC endpoint.

bash
# Terminal 1, from the repository root:
export FUYU_QUEUE_RUNTIME_DIR=/absolute/path/to/fresh-runtime
export FUYU_ANVIL=/absolute/path/to/anvil
# Set FUYU_MAINNET_RPC_URL in the environment if needed.
node apps/privacy/runtime/queue-chain-bootstrap.cjs

# Terminal 2, from the same checkout:
export FUYU_QUEUE_RUNTIME_DIR=/absolute/path/to/fresh-runtime
node --experimental-strip-types apps/privacy/runtime/queue-api.cjs

Start the browser app#

Start Vite from apps/privacy, or use the prefix command below from the repository root. index.html is the app's main entry; verify-audit-answer.html is the independent audit-answer verifier. Run this protocol app separately from the documentation site's development server.

The ordinary build is read-only with the development proof setup. For a disposable integration build, enable writes with FUYU_QUEUE_E2E_ENABLED=1. Hosted test writes use a separate public-test flag and check the Sepolia deployment. Keep an E2E fork build connected to its disposable runtime, and leave the public-test deployment checks in place.

A remote browser or phone won't reach the Linux host's loopback RPC through WalletConnect pairing. Run your acceptance browser next to the runtime. If you need remote access, configure a reachable test endpoint and check its network and origin settings before using it.

bash
npm --prefix apps/privacy run dev
# Build the normal app:
npm --prefix apps/privacy run build
# An explicitly disposable integration build:
FUYU_QUEUE_E2E_ENABLED=1 npm --prefix apps/privacy run build

Choose the checks for your change#

Run the checks for the code you changed. Artifact and layout checks compare source and build inputs. Unit tests cover module behavior, Solidity suites execute contracts, and funded browser suites exercise the user flow against a running chain. Say which checks passed when reporting a result so the reader knows what you tested.

Use the repository's Foundry configuration: Solidity 0.8.30, Cancun target, optimizer runs 1 and the standard code generator. The Anvil bootstrap sets its own hardfork and gas limit. To reproduce artifacts, compare both creation and runtime bytecode with the stored source build; compilation alone won't tell you whether the bytes match.

bash
# Linux host only for protocol suites and proof work:
npm run check
npm --prefix apps/privacy run typecheck
npm --prefix apps/privacy test
node --test apps/privacy/runtime/*.test.cjs
FUYU_REMOTE_EXPERIMENTS=1 npm run test:contracts
node contracts/script/reproduce-artifacts.cjs

Funded browser acceptance#

The acceptance wrapper starts a suite's fork, API and browser preview, writes the output to its own directory and stops the processes afterward. Configure the Linux browser module and archive RPC before launching it. Build with the flags required by your suite, including profile-deposit support when the harness needs it.

For a first integration test, deposit a real fork asset, send a private payment and recover the recipient in a fresh process. Check the contract reserves against the expected amounts. For an external action, check its queued and finalized states, recover its output note and compare the adapter's token effects. Save those results alongside any screenshots of the flow.

Use the broader acceptance sweep when your change needs it. Some suites create test-only venues for Morpho, Dolomite, asynchronous tickets, composite actions or Fuyu Earn. Read the public Sepolia route catalog separately to see which routes that deployment supports. A venue created by the fork suite belongs to that test run.

bash
# Configure a fresh Linux output and the installed browser module.
export FUYU_ACCEPTANCE_DIR=/absolute/path/to/fresh-acceptance
export FUYU_PLAYWRIGHT_MODULE=/absolute/path/to/playwright
export FUYU_QUEUE_UI_OUT_DIR=/absolute/path/to/e2e-build
# Use the harness-required E2E/profile-deposit build flags.
apps/privacy/runtime/run-browser-acceptance.sh \
  first-payment test-batch-send-browser-e2e.cjs api

# Full sweep, when appropriate for the change:
FUYU_REMOTE_EXPERIMENTS=1 \
  apps/privacy/runtime/run-acceptance-sweep.sh

Test recovery without the API#

For the independent verifier, export an operator trust manifest from the matching runtime to a fresh destination. For service-independent account recovery, export a static deployment bundle. Keep them separate: the verifier reads operator trust, while the recovery app reads the bundle. A holder's recovery file has a different purpose again.

To test an outage, stop the API and recover with the exported bundle and a suitable RPC. Check that the browser makes no requests to service API paths and that each downloaded proving file matches its hash. Submit a wallet-paid exit and compare the received amount with the expected result. Record that direct submission publicly links the transaction to the wallet.

Keep demo-wallet secrets, passkey material, paper words and private note openings out of public deployment artifacts. Save the public proofs, manifests and transaction receipts with the acceptance results needed to reproduce the test. Check the output before sharing or serving it.

bash
FUYU_REMOTE_EXPERIMENTS=1 \
FUYU_AUDIT_DEPLOYMENT_OUT=/absolute/path/to/fresh-trust.json \
  node --experimental-strip-types apps/privacy/runtime/audit-deployment.cjs

node apps/privacy/runtime/queue-static-deployment.cjs \
  --config /absolute/path/to/fresh-runtime/config.json \
  --out /absolute/path/to/fresh-recovery-bundle

Common development failures#

If a proving file is missing, check whether Git LFS has downloaded it. If an artifact hash changed, compare the source and build before changing any expected digest. If the fork cannot start, check whether the upstream serves the chosen historical block. If writes are disabled, check whether you are serving the ordinary build or the disposable E2E build.

Check ports and runtime directories when a new app appears to show the wrong balance. Compare the instance ID, chain ID, Pool and build manifest so you know which run you're connected to. Keep partial output from an interrupted test and rerun it into a fresh directory. Report the completed run's result separately.