Local development
Requirements
- Node 22 and pnpm 10 (the repository is a pnpm workspace:
packages/*,services/*,apps/*). - Foundry (
forge,anvil,cast). The setup script installs it from npm and pins solc 0.8.26. - Git submodules under
contracts/lib/(Infinity core and periphery, OpenZeppelin, forge-std).
Setup
git clone https://github.com/camdengrieh/zk-pad.git
cd zk-pad
pnpm run setup # = bash scripts/setup-toolchain.sh (Foundry, solc 0.8.26, submodules)
pnpm install
scripts/bootstrap-refs.sh clones the reference repositories (Clanker v4, Railgun, Privacy
Pools...) into /tmp/claude-0/ref/. You only need them to compare the port against upstream.
Build and test the contracts
pnpm build:contracts # cd contracts && forge build
pnpm test:contracts # cd contracts && forge test
When several processes build at once, give each its own output directory:
cd contracts
FOUNDRY_OUT=out-mine FOUNDRY_CACHE_PATH=cache-mine forge test -vv
Tests deploy a real PancakeSwap Infinity stack (Vault, CLPoolManager,
CLProtocolFeeController) from the submodules; see contracts/test/utils/InfinityDeployer.sol.
One-command local environment
scripts/dev.sh # anvil + deploy + env files + relayer + attestor; Ctrl-C stops all
scripts/dev.sh --demo # ... and drive one full claim through the relayer
scripts/dev.sh --ci # start, run the demo, stop, exit with the demo's status
What it does:
-
Starts
anvil(chain id 31337, portANVIL_PORT, default 8545). -
Runs
contracts/script/LocalDev.s.sol: the Infinity stack from the submodules (Vault, CLPoolManager, CLProtocolFeeController with the BSC default 0.03% dynamic-pool fee), WBNB, USDT (18 decimals) and XAUt (6 decimals) mocks, always-fresh mock Chainlink feeds, the behavioural Railgun mock, and hookless WBNB/USDT and XAUt/USDT reference pools seeded with liquidity. It then runs the same deployment and wiring as production (DeployBase) with the roles incontracts/config/31337.json, and writescontracts/deployments/31337.json. -
Builds the SDK and both services, then writes
.dev/relayer.envand.dev/attestor.env(copied toservices/*/.env.local) and, ifapps/webexists,apps/web/.env.local. -
Starts the relayer (
:8080) and two attestors (:8081and:8082, anvil accounts #3 and #4, sharing the local hint key and pepper), because31337.jsonsets an attestor threshold of 2 and a handle bind needs two signatures. All read the deployment file throughDEPLOYMENT_FILE. The web app's env getsNEXT_PUBLIC_ATTESTOR_URLSwith both URLs. The indexer and the web app are started separately (pnpm -C services/indexer dev,pnpm -C apps/web dev). -
With
--demo/--ci, it runspackages/sdk/examples/local-claim.mjs:- a fresh stealth beneficiary;
- a WBNB launch built with
buildLaunchParams(anti-snipe on), then buys and sells throughZkPadSwapRouterandcollectRewards; - a relayed
registerStealth+RegisterShieldTemplates; - the creator's
creatorConsolidateAndShield; - a relayed
multicall(consolidate, claim)to the Railgun adapter; - a relayed direct withdrawal of the leftover USDT.
It checks that the stealth key never paid gas.
The first run compiles everything with via-IR (about 12 minutes). Point FOUNDRY_OUT and
FOUNDRY_CACHE_PATH at an existing build to skip it, and set SKIP_TS_BUILD=1 to reuse the
dist/ builds. Logs are in .dev/.
Anvil accounts used: #0 deployer and owner, #1 team fee recipient, #2 guardian, #3 to #5
attestors (threshold 2; dev.sh runs #3 and #4), #6 demo creator, #7 demo trader, #9 relayer.
Ports can be changed with ANVIL_PORT, RELAYER_PORT, ATTESTOR_PORT and ATTESTOR2_PORT.
In your own scripts, register the local addresses with the SDK:
import { readFileSync } from 'node:fs';
import { registerDeployment } from '@zk-pad/sdk';
registerDeployment(JSON.parse(readFileSync('contracts/deployments/31337.json', 'utf8')));
TypeScript packages
pnpm -r typecheck
pnpm -r test
pnpm -r build
Per package, for example: pnpm -C packages/sdk test, pnpm -C services/relayer test.
This documentation site
pnpm -C apps/docs start # dev server with hot reload
pnpm -C apps/docs build # production build; fails on any broken link
pnpm -C apps/docs serve # serve the build locally