Back to homepage
Developer Documentation

CompactForge Docs

Everything you need to build, compile, deploy, and interact with Compact smart contracts on the Midnight Network.

Overview

  • CI/CD Pipeline — GitHub Actions that compile Compact circuits on every push
  • Proof Benchmarking — Per-circuit proving times in Neon Postgres
  • Live Dashboard — Deployments, CI runs, charts — wired to a real database
  • Deploy & Interact — One-click deployment and circuit invocation via 1AM wallet
  • token_ledger — Reference Compact contract with 6 ZK circuits

Prerequisites

Install these before running locally:

Node.js >= 18

LTS recommended

npm >= 9

or pnpm/yarn

Git

for webhooks & CI

1AM Wallet

Chrome extension — required for Deploy/Interact

Neon Postgres

Free tier; provides DATABASE_URL

Compact CLI >= 0.20

For contract compilation in CI

⚠️
Warning The 1AM Wallet Chrome extension is mandatory for Deploy and Interact. Install from 1am.xyz

Installation

1. Clone the repository

bash
git clone https://github.com/sauravs296/CompactForge.git
cd CompactForge

2. Environment variables

Create a .env file:

.env
DATABASE_URL="postgresql://user:pass@host/db?sslmode=require"
GITHUB_WEBHOOK_SECRET="your_secret"
GITHUB_TOKEN="ghp_xxxxx"

3. Install & setup database

bash
npm install
npx prisma generate
npx prisma db push

4. Start dev server

bash
npm run dev
# → http://localhost:3000
✅
Tip Dev server uses webpack mode for Midnight SDK WASM compatibility. Already configured in package.json.

Architecture

plaintext
CompactForge/
├── src/
│   ├── app/
│   │   ├── (marketing)/page.tsx      ← Landing page
│   │   ├── docs/page.tsx             ← This docs page
│   │   ├── dashboard/
│   │   │   ├── page.tsx              ← Overview (KPIs + charts)
│   │   │   ├── contracts/page.tsx    ← Contract details
│   │   │   ├── deployments/page.tsx  ← Deployment history
│   │   │   ├── benchmarks/page.tsx   ← Proof benchmark table
│   │   │   ├── ci-runs/page.tsx      ← CI run log
│   │   │   └── interact/page.tsx     ← Deploy + Interact panel
│   │   └── api/
│   │       ├── contracts/token_ledger/
│   │       │   ├── contract/         ← Metadata endpoint
│   │       │   ├── zkir/[circuit]/   ← Serves .bzkir ZK IR
│   │       │   └── keys/[filename]/  ← Serves .prover/.verifier
│   │       ├── deployments/          ← CRUD for deployments
│   │       ├── benchmarks/           ← Benchmark data
│   │       └── ci-runs/              ← Webhook receiver
│   └── components/
│       ├── DeployButton.tsx          ← One-click deployment
│       ├── InteractPanel.tsx         ← 6-circuit interaction
│       └── WalletConnectButton.tsx   ← 1AM wallet connect
├── contracts/token_ledger/
│   ├── token_ledger.compact          ← Compact source
│   └── build/token_ledger/
│       ├── contract/index.js         ← Compiled JS module
│       ├── keys/*.prover             ← ZK prover keys (2-10 MB)
│       ├── keys/*.verifier           ← ZK verifier keys (~2 KB)
│       └── zkir/*.bzkir              ← ZK intermediate repr
├── prisma/schema.prisma              ← DB schema
└── .github/workflows/                ← 3 CI/CD workflows

The Contract

token_ledger.compact — privacy-preserving token ledger demonstrating the full Midnight ZK model:

Public Ledger State

balances, totalSupply, ownerCount, admin, paused — all on-chain

Private Witnesses

localSecretKey() — caller key, never touches the chain

disclose() calls

Every ledger write wrapped in disclose() as Compact requires

ZK-safe arithmetic

Explicit as Uint<64> casts to prevent circuit overflow

assert() guards

Access control at compile-time

Circuit breaker

pause() / unpause() — admin-only emergency freeze

Ledger declaration

compact
pragma language_version >= 0.20;
import CompactStandardLibrary;

export ledger balances: Map<Bytes<32>, Uint<64>>;
export ledger totalSupply: Uint<64>;
export ledger ownerCount: Uint<32>;
export ledger admin: Bytes<32>;
export ledger paused: Boolean;

witness localSecretKey(): Bytes<32>;

constructor(adminAddress: Bytes<32>) {
    admin       = disclose(adminAddress);
    totalSupply = disclose(0 as Uint<64>);
    ownerCount  = disclose(0 as Uint<32>);
    paused      = disclose(false);
}

Circuits Reference

6 circuits, each with its own ZK prover/verifier keypair:

deposit(amount: Uint<64>): []Public

Adds tokens to caller's own balance via localSecretKey() identity.

ArgTypeNote
amountUint<64>Must be > 0
transfer(recipient: Bytes<32>, amount: Uint<64>): []Public

ZK-private: proves sender has balance without revealing identity.

ArgTypeNote
recipientBytes<32>32-byte public address
amountUint<64><= sender balance
mint(recipient: Bytes<32>, amount: Uint<64>): []Admin only

Creates tokens assigned to recipient. Admin key checked in circuit.

ArgTypeNote
recipientBytes<32>Address receiving tokens
amountUint<64>Must be > 0
burn(amount: Uint<64>): []Public

Destroys caller's tokens. Reduces totalSupply permanently.

ArgTypeNote
amountUint<64><= caller balance
pause(): []Admin only

Sets paused=true. All other circuits assert(!paused) and revert.

No arguments required.

unpause(): []Admin only

Clears paused flag, resuming operations.

No arguments required.

CI/CD Pipeline

3 GitHub Actions workflows on every push to main:

.github/workflows/contracts.yml

Compiles all Compact circuits. POSTs benchmark data to /api/benchmarks on success.

.github/workflows/frontend.yml

Runs next build — fails fast on TypeScript errors.

.github/workflows/typecheck.yml

Runs tsc --noEmit for strict type-checking.

GitHub Webhook

bash
# GitHub repo → Settings → Webhooks
Payload URL:   https://your-domain.com/api/ci-runs
Content type:  application/json
Secret:        <GITHUB_WEBHOOK_SECRET from .env>
Events:        push

Dashboard

/dashboard

KPI overview: contracts, deployments, avg proof time, CI success rate

/dashboard/contracts

Contract details, circuits, benchmark data

/dashboard/deployments

History with contract address + 1AM Explorer links

/dashboard/benchmarks

Per-circuit proving times with commit verify links

/dashboard/ci-runs

GitHub Actions run log

/dashboard/interact

Deploy and call any of the 6 circuits

Deploy & Interact

⚠️
Warning Requires the 1AM Wallet Chrome extension and a locally running instance. See Why Local Only?

Deployment flow

  1. 11AM Wallet opens permission prompt — click Approve
  2. 2App fetches your wallet address and shielded keys in parallel
  3. 3createUnprovenDeployTx builds the unproven deployment transaction
  4. 41AM ProofStation generates the ZK proof (downloads keys from /api/contracts/token_ledger/keys/)
  5. 5balanceUnsealedTransaction adds dust fees via the 1AM server wallet (you pay 0 NIGHT)
  6. 6submitTransaction broadcasts to Midnight Preprod network
  7. 7Contract address shown in toast — copy for Interact panel
  8. 8Deployment saved to database automatically

Circuit arguments

CircuitArgs
deposit[BigInt(amount)]
burn[BigInt(amount)]
transfer[parseAddressToBytes32(recipient), BigInt(amount)]
mint[parseAddressToBytes32(recipient), BigInt(amount)]
pause[]
unpause[]

Benchmarks

bash
curl -X POST https://your-domain/api/benchmarks \
  -H "Content-Type: application/json" \
  -d '{
    "commitSha": "abc1234",
    "benchmarks": [
      { "circuit": "deposit",  "provingMs": 1340 },
      { "circuit": "transfer", "provingMs": 1810 },
      { "circuit": "mint",     "provingMs": 1890 },
      { "circuit": "burn",     "provingMs": 1120 },
      { "circuit": "pause",    "provingMs": 750  },
      { "circuit": "unpause",  "provingMs": 750  }
    ]
  }'

API Reference

GET
/api/deployments

List all deployments

POST
/api/deployments

Record a deployment { contractAddress, txId, network }

GET
/api/deployments/[id]

Get single deployment

GET
/api/benchmarks

List benchmark records

POST
/api/benchmarks

Record CI benchmark data { commitSha, benchmarks[] }

GET
/api/ci-runs

List CI run history

POST
/api/ci-runs

GitHub webhook receiver

GET
/api/contracts/token_ledger/contract

Contract metadata

GET
/api/contracts/token_ledger/zkir/[circuit]

Streams .bzkir ZK IR file

GET
/api/contracts/token_ledger/keys/[filename]

Streams .prover or .verifier key

Why Local Only?

Deploy & Interact cannot run on Vercel or any hosted deployment.

  • 1.ZK proving keys read from disk — .prover and .verifier files (2-10 MB each) are served via API routes from contracts/token_ledger/build/token_ledger/keys/. Vercel serverless functions have size limits that prevent serving large binaries this way.
  • 2.1AM ProofStation calls back to your server — the cloud ProofStation tries to download key files from the URL you configured. It cannot reach localhost:3000. The app must be publicly accessible — OR the custom inline provider we use forces key fetching in the browser extension (works only locally).

Production workaround

  1. 1Host .prover / .verifier key files on CDN (Cloudflare R2, AWS S3) with CORS: *
  2. 2Use FetchZkConfigProvider with the CDN URL as base
  3. 3The 1AM ProofStation can fetch keys from the public CDN
  4. 4The Next.js frontend can run on Vercel — only key files need a CDN
✅
Tip For the hackathon demo, running locally is perfectly fine. Clone the repo, run npm install && npm run dev, and everything works end-to-end.