Local fhEVM indexer + decrypt worker + partner read API for ERC-7984 confidential tokens.
- Node.js ≥ 24, pnpm 11
- Docker (Postgres 18) — OrbStack, Docker Desktop, or equivalent
- Foundry (
anvil,forge,git) - Network access on first
pnpm dev— auto-clones forge-fhevm tovendor/forge-fhevmand installs contract deps viascripts/install-contract-deps.sh(git + pinned zips, no soldeer)
cp .env.example .env
pnpm install
pnpm dev:db
pnpm db:migrate
pnpm dev # migrations, Anvil deploy, worker, API, indexerWithout Foundry/Docker, run DB-level tests only after starting Postgres manually:
pnpm dev:db && pnpm db:migrate && pnpm testTest addresses (Anvil default mnemonic):
| Role | Account | Address |
|---|---|---|
| Indexer / worker | #0 | 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 |
| Token deployer | #8 | 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f |
| Alice (sender) | #2 | 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC |
| Bob (recipient) | #3 | 0x90F79bf6EB2c4f870365E785982E1f101E93b906 |
CONTRACT_ADDRESS and UNDERLYING_ADDRESS in .env (and the same wrapper address in apps/indexer/config.yaml) are fixed for local dev. pnpm dev deploys to those CREATE addresses using account #8 on a fresh Anvil; it does not rewrite config files.
With pnpm dev running:
pnpm fund --address 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC --amount 5
pnpm fund --address 0x90F79bf6EB2c4f870365E785982E1f101E93b906 --amount 5
# 1 — transfer (indexer is neither party)
pnpm send --from 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC --to 0x90F79bf6EB2c4f870365E785982E1f101E93b906 --amount 1
# 2 — worker logs acl_denied until grant
curl localhost:3000/v1/indexer/status
curl "localhost:3000/v1/addresses/0x90F79bf6EB2c4f870365E785982E1f101E93b906/transfers?direction=in"
# 3 — Alice delegates decrypt to indexer
pnpm grant --from 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
# 4 — worker backfill decrypts pending row
# 5 — repeat transfer
pnpm send --from 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC --to 0x90F79bf6EB2c4f870365E785982E1f101E93b906 --amount 1 --wait| Command | Purpose |
|---|---|
pnpm fund --address <addr> --amount <decimal> |
Mint underlying ERC-20 + shield to confidential balance |
pnpm send --from <A> --to <B> --amount <decimal> |
Confidential transfer (decimal, not wei) |
pnpm grant --from <sender> |
ACL delegation from sender to indexer EOA |
| Service | Path | Port |
|---|---|---|
| Read API | apps/api |
3000 |
| Decrypt worker | apps/worker |
— |
| Envio indexer | apps/indexer |
— |
| Postgres | docker postgres:18 |
5432 (envio + zama DBs) |
GET /v1/indexer/statusGET /v1/addresses/:address/balanceGET /v1/addresses/:address/transfers
pnpm dev:db && pnpm db:migrate
pnpm test
pnpm lint && pnpm typecheck && pnpm fallow# stop services
pkill -f "anvil --chain-id" 2>/dev/null || true
pkill -f "envio dev" 2>/dev/null || true
docker compose down -v
# remove artifacts created by pnpm dev
rm -rf vendor contracts/dependencies contracts/cache contracts/broadcast
cp .env.example .env # if you need a fresh .env
pnpm install
pnpm devpnpm dev runs scripts/install-contract-deps.sh and scripts/install-forge-fhevm-deps.sh (git clones + pinned Soldeer S3 zips). No forge soldeer required.
Manual reinstall:
pnpm contracts:deps
bash scripts/install-forge-fhevm-deps.sh # after vendor/forge-fhevm exists; inits git submodules + remappingsThis project no longer uses forge soldeer for local dev. If you still run it manually, remove broken zips and use the scripts above instead:
rm -rf contracts/dependencies contracts/cache
pnpm contracts:depsContracts deploy to the addresses in .env via Anvil account #8 (TOKEN_DEPLOYER_PRIVATE_KEY) at CREATE nonces 0 and 1. If that account already sent transactions on the running Anvil, addresses will not match — restart Anvil or run the clean-slate steps below. dev.sh skips deploy when bytecode already exists at CONTRACT_ADDRESS.
On local Anvil, Envio realtime sync can stall (indexed_block behind chain tip). Restart the indexer process started by pnpm dev, or run envio dev again in apps/indexer. After wiping Anvil and redeploying, restart Envio if ingest stops.
The mint step can succeed while wrap reverts. Common causes:
ERC7984TotalSupplyOverflow— cumulative shielded supply exceeds theeuint64cap (~18.44 tokens at 18 decimals). Lower--amountvalues or run the clean-slate steps so both accounts fit under the cap. Do not fund10+10on a fresh chain.- Address already funded — re-running
pnpm fundon the same account tries to wrap again; use smaller amounts or restart Anvil. - Stale deploy — ensure
.envstill has the canonicalCONTRACT_ADDRESS/UNDERLYING_ADDRESSfrom.env.example. Restartpnpm dev(or at least worker + API + indexer) after a clean redeploy.