Direct Mode Quick Start
这个教程对应的是仓库里的 direct 提交流程。
如果你想边看文档边对照代码,直接打开 JetHalo/zk-Escrow 的 direct 分支 会更顺手一些。
如果你想先看实际效果,可以直接打开 zkEscrow direct 模式演示地址。这个地址对应的就是这篇教程讲的 direct 路线。
浏览器本地生成 Groth16 proof,服务端把 proof、publicSignals 和 vk 直接提交给 Kurier。Kurier 状态进入 finalized 之后,前端再请求授权签名,最后调用链上 finalize。
这是一套 Base Sepolia 测试链 方案,不是 Base 主网方案。文档里的钱包、RPC、合约地址、测试 ETH 和链上交互,都默认发生在 Base Sepolia。
仓库里已经带上了浏览器 proving 需要的
wasm、.zkey和vkey.json。如果你的目标只是先把 direct 模式跑通,可以直接使用现成 artifacts。如果你想从电路源码重新生成它们,下面也有完整步骤。
Prerequisite
开始之前,先准备好下面这些内容:
- Node.js 22.x 或更新版本
- npm
- Foundry
- 一个 Base Sepolia 钱包
- 钱包里的测试 ETH
- 一个可用的 Kurier API Key
- 已部署好的
ZKEscrowRelease合约地址 - 这份合约对应的部署区块
- 和合约
finalizeAuthority一致的私钥
What you'll learn
完成这篇教程之后,你会得到:
- 一个可以本地启动的 direct 模式环境
- 一份最小可用的
apps/web/.env.local - 一条从
deposit到prove再到finalize的完整调试路径 - 一套可选的电路重编译和 The Graph 索引流程
1. Understand The Two Modes
先把 direct 和 aggregation 的差别说清楚。
Direct mode
direct 的重点是“证明直接提交,结果直接消费”。
- 提交 proof 时就把完整 proof、公开输入和 verification key 一起交给 Kurier。
- 前端只轮询 proof 状态。
- 状态到
finalized之后,直接进入授权和链上finalize。 - 当前仓库的主流程就是这条路径,
/api/submit-proof固定写的是submissionMode: direct。
Aggregation mode
aggregation 会比 direct 多一层聚合批次。
- proof 提交之后,不是立刻按单条结果消费。
- 还要等 proof 被收进 aggregation batch。
- 这时候除了 proof 状态,还要拿 aggregation tuple,例如
domainId、aggregationId、leafCount、index、merklePath。 - 仓库里保留
/api/proof-aggregation,就是为了处理这条路径。
如果你的目标是先把这套流程跑通,先用 direct。这个仓库当前默认也是 direct。
2. Install The Project
Install the web app dependencies
先安装前端依赖:
cd apps/webnpm install
Optional: install the subgraph dependencies
如果你准备接 The Graph,再安装 subgraph 依赖:
cd indexer/subgraphnpm install
Optional: run the contract tests
如果你想先确认合约逻辑是通的,可以执行:
cd contractsforge test
3. Generate The Proving Artifacts
如果你不想直接使用仓库里已经带好的 proving artifacts,而是想从电路源码重新生成 wasm、.zkey 和 vkey.json,走这一步。
这一节是手工流程。当前仓库没有把 circuit build 和 trusted setup 封装成脚本。
Install the circuit dependencies
circuits/escrow/circom/escrowRelease.circom 依赖 circomlib,所以先在 circuits/escrow 目录补上本地依赖:
cd circuits/escrownpm install circomlib snarkjs
另外还需要你本机已经装好 circom 二进制。
Compile escrowRelease.circom
把电路编译成 r1cs、wasm 和 sym:
cd circuits/escrowmkdir -p buildcircom circom/escrowRelease.circom --r1cs --wasm --sym -o build
执行完之后,关键输出会在这些位置:
build/escrowRelease.r1csbuild/escrowRelease.symbuild/escrowRelease_js/escrowRelease.wasm
Run the local trusted setup
这个电路走的是 groth16。本地开发可以直接做一套本地 ceremony。
下面这套流程只适合本地开发和教程复现,不适合拿来当生产 setup。
cd circuits/escrowsnarkjs powersoftau new bn128 16 build/pot16_0000.ptausnarkjs powersoftau contribute build/pot16_0000.ptau build/pot16_0001.ptau --name="zkescrow dev ptau" -e="replace-with-random-text"snarkjs powersoftau prepare phase2 build/pot16_0001.ptau build/pot16_final.ptausnarkjs groth16 setup build/escrowRelease.r1cs build/pot16_final.ptau build/escrowRelease_0000.zkeysnarkjs zkey contribute build/escrowRelease_0000.zkey build/escrowRelease_final.zkey --name="zkescrow dev zkey" -e="replace-with-random-text"snarkjs zkey export verificationkey build/escrowRelease_final.zkey build/vkey.json
到这里你会得到:
build/escrowRelease_final.zkeybuild/vkey.json
Copy the artifacts into the web app
前端 proving 读取的是 apps/web/public/zk/escrow 目录,所以最后还要把新生成的 artifacts 复制过去:
cp circuits/escrow/build/escrowRelease_js/escrowRelease.wasm apps/web/public/zk/escrow/escrowRelease.wasmcp circuits/escrow/build/escrowRelease_final.zkey apps/web/public/zk/escrow/escrowRelease_final.zkeycp circuits/escrow/build/vkey.json apps/web/public/zk/escrow/vkey.json
如果你重新生成了 artifacts,建议再重启一次 apps/web 的开发服务器,避免继续读到旧缓存。
4. Configure The Direct Mode Environment
Create apps/web/.env.local
在 apps/web 目录下新建 .env.local,填入下面这些值:
NEXT_PUBLIC_ESCROW_ADDRESS=0xYourEscrowAddressNEXT_PUBLIC_DEPLOY_BLOCK=12345678NEXT_PUBLIC_DOMAIN=111NEXT_PUBLIC_APP_ID=222NEXT_PUBLIC_BASE_SEPOLIA_RPC_URL=https://your-base-sepolia-rpcINDEXER_RPC_URL=https://your-base-sepolia-rpcINDEXER_STRATEGY=sqliteKURIER_API_KEY=your-kurier-api-keyKURIER_API_URL=https://api-testnet.kurier.xyz/api/v1FINALIZE_AUTH_PRIVATE_KEY=0xyour_finalize_authority_private_keyNEXT_PUBLIC_KURIER_POLL_ATTEMPTS=150NEXT_PUBLIC_KURIER_POLL_INTERVAL_MS=4000NEXT_PUBLIC_INDEXER_POLL_INTERVAL_MS=10000
Understand the required values
NEXT_PUBLIC_ESCROW_ADDRESS是已经部署好的ZKEscrowRelease合约地址。NEXT_PUBLIC_DEPLOY_BLOCK是合约部署区块。索引器会从这个区块开始扫描。NEXT_PUBLIC_DOMAIN和NEXT_PUBLIC_APP_ID必须和合约部署时的配置一致。FINALIZE_AUTH_PRIVATE_KEY必须和合约里的finalizeAuthority对上,否则/api/authorize-finalize产出的签名不能用。- 第一次跑教程时,先保留
INDEXER_STRATEGY=sqlite。这样前端会直接通过 RPC 扫链,把结果缓存到本地 SQLite,不需要先部署 subgraph。
5. Start The Web App
Run the development server
在 apps/web 目录启动开发服务器:
cd apps/webnpm run dev
Open the escrow page
然后打开:
http://localhost:3000
应用会自动跳到 /escrow。
如果页面正常加载,你应该能看到:
Deposit/Withdraw两个标签页- Base Sepolia 网络切换器
- Proof 状态面板
6. Run The Direct Flow
Step 1: create a deposit
- 连接钱包。
- 切到 Base Sepolia。
- 输入存款金额和收款地址。
- 点击
Deposit & Lock。
交易成功后,页面会生成一条 credential。这条值后面会直接用来生成 withdrawal proof,先保存好。
Step 2: generate and submit the proof
- 把刚才保存的
credential填到Withdraw页签。 - 点击
Unlock。 - 浏览器会本地生成 proof。
- 服务端会把 proof 按
direct模式提交给 Kurier。 - 前端开始轮询
/api/proof-status。 - 状态进入
finalized之后,服务端通过/api/authorize-finalize生成授权签名。 - 钱包发起链上
finalize交易。 - 收款地址收到资金。
这条路径里不会调用 /api/proof-aggregation。如果你看到 aggregation tuple 相关报错,说明当前环境和这篇教程假设的 direct 流程不一致。
7. Build And Deploy The Graph Subgraph
如果你不想依赖本地 SQLite 扫链,可以切到 The Graph。
Install the subgraph dependencies
先进入 indexer/subgraph:
cd indexer/subgraphnpm install
Provide the subgraph inputs
这个 subgraph 会读取 Deposited 和 Finalized 事件,所以至少要准备好下面这些值:
NEXT_PUBLIC_ESCROW_ADDRESS,或者SUBGRAPH_ESCROW_ADDRESSNEXT_PUBLIC_DEPLOY_BLOCK,或者SUBGRAPH_START_BLOCK
可选值:
SUBGRAPH_NETWORK=base-sepolia
如果你已经完成了第 4 步,通常不需要重复填写这两个核心值,npm run render 会直接读取现有环境变量。
Generate subgraph.yaml
indexer/subgraph/subgraph.template.yaml 不是直接部署文件,先渲染成实际的 subgraph.yaml:
cd indexer/subgraphnpm run render
Generate types and build the subgraph
cd indexer/subgraphnpm run codegennpm run build
跑完之后,就完成了本地 subgraph 生成和构建。
Deploy to The Graph Studio
先准备 Graph Studio 的部署密钥和 slug:
export GRAPH_DEPLOY_KEY=<your_studio_deploy_key>export SUBGRAPH_SLUG=<your_studio_subgraph_slug>
然后部署:
cd indexer/subgraphnpm run authnpm run deploy
如果你不想走 npm script,也可以直接执行:
graph auth "$GRAPH_DEPLOY_KEY"graph deploy "$SUBGRAPH_SLUG" subgraph.yaml \--node https://api.studio.thegraph.com/deploy/ \--deploy-key "$GRAPH_DEPLOY_KEY"
Connect the query URL to the web app
部署成功后,在 Graph Studio 里拿到 query URL,再把它写回 apps/web/.env.local:
INDEXER_STRATEGY=thegraphTHEGRAPH_SUBGRAPH_URL=https://your-subgraph-query-url
如果你想优先走 The Graph,失败时再回退到本地扫描,也可以这样配:
INDEXER_STRATEGY=hybrid
Troubleshooting
Missing NEXT_PUBLIC_ESCROW_ADDRESS
说明 apps/web/.env.local 里还没配好合约地址,或者改完环境变量后没有重启开发服务器。
Local scan missing deposits
通常是 NEXT_PUBLIC_DEPLOY_BLOCK 配得太晚,导致本地索引从错误区块开始扫描。把它改成真实部署区块,然后点击页面上的 Rescan。
Proof not finalized yet
说明 Kurier 还没有把这条 direct 提交处理到 finalized。先检查 KURIER_API_KEY、RPC 和 proof 参数,再看 Kurier 返回的原始状态。
bad authorization
先确认 FINALIZE_AUTH_PRIVATE_KEY 对应的地址,和合约里配置的 finalizeAuthority 是同一个地址。