Skip to main content

Midnight Network: 6 Bugs, 8 Workarounds, and 4 Working Compact Contracts

· 5 min read
Frederico Santana
Founder & Technical Writer, DPO2U

We built 4 Compact contracts with 18 passing tests and a working local deploy. But getting here was full of obstacles. This article documents every bug, workaround, and friction point encountered during a day of development on Midnight Network.

Context

The DPO2U Self-Funding Protocol implements an autonomous fee distribution pipeline for compliance agents on Midnight Network:

  • PaymentGateway — Treasury deposits and $NIGHT staking
  • FeeDistributor — 40/60 split between expert and auditor
  • ComplianceRegistry — ZK privacy-preserving attestations
  • AgentRegistry — Agent lifecycle (registration, deactivation, tasks)

Stack: Compact 0.29.0, Runtime 0.14.0, Node.js 20.18.1, TypeScript 5.9.3

Repo: github.com/fredericosanntana/dpo2u-midnight


Bug #1: Compact Compiler (compactc) Not Available

Severity: Blocking

The compactc compiler is not available as an installable npm package or standalone binary. The documentation mentions the compiler but doesn't provide clear installation instructions outside the Midnight IDE (VS Code extension).

Impact: Impossible to compile new .compact contracts to generate build artifacts (contract/index.js, zkir/, keys/).

Workaround: Reverse-engineered the already-compiled contracts (PaymentGateway as reference) and hand-wrote the contract/index.js. The AgentRegistry (400+ lines of JS) was entirely hand-written following the runtime patterns. Works for local tests but doesn't generate the ZK artifacts needed for real deployment.

Hand-written:
compact/build/agent-registry/contract/index.js (hand-written)
compact/build/agent-registry/contract/index.d.ts
compact/build/agent-registry/compiler/contract-info.json

NOT generated (require compactc):
compact/build/agent-registry/zkir/*.zkir
compact/build/agent-registry/keys/*.prover / *.verifier

Recommendation: Publish compactc as an npm package (@midnight-ntwrk/compactc) or downloadable binary.


Bug #2: signRecipe with Hardcoded Proof Marker

Severity: Critical (blocks deploy)

The wallet-sdk signRecipe method uses a hardcoded 'pre-proof' marker when cloning intents, but proved transactions (UnboundTransaction) have data with 'proof' marker. The signing fails silently or produces an invalid TX.

Implemented workaround (in scripts/lib/wallet-setup.ts):

function signTransactionIntents(
tx: { intents?: Map<number, any> },
signFn: (payload: Uint8Array) => any,
proofMarker: 'proof' | 'pre-proof',
): void {
for (const segment of tx.intents.keys()) {
const intent = tx.intents.get(segment);
const cloned = ledger.Intent.deserialize(
'signature', proofMarker, 'pre-binding', intent.serialize()
);
const sigData = cloned.signatureData(segment);
const signature = signFn(sigData);
// ... apply signatures to offers
}
}

// Correct usage with distinct markers:
signTransactionIntents(recipe.baseTransaction, signFn, 'proof');
signTransactionIntents(recipe.balancingTransaction, signFn, 'pre-proof');

Bug #3: wallet.getBalance() Doesn't Exist

Severity: Medium

The WalletFacade API doesn't expose a getBalance() method despite it being the most basic wallet operation expected:

Failed: ctx.wallet.getBalance is not a function

There's no way to programmatically check whether the wallet has enough tDUST before attempting a deploy. The developer needs to open Lace Wallet manually.


Bug #4: PreProd Deploy Hangs Indefinitely

Severity: Critical

deployContract() hangs indefinitely during the ZK proof generation phase. The wallet syncs successfully, the proof server responds health OK, but the call never returns:

=== DPO2U Self-Funding Protocol — Deploy All Contracts ===
Network: preprod

[1/3] Setting up wallet...
Wallet synced.
[2/3] Deploying contracts sequentially...
Deploying ComplianceRegistry...
<--- HANGS HERE FOR 15+ MINUTES, NO OUTPUT --->

Diagnostics:

  • Proof server (localhost:6300) returns {"status":"ok"}
  • Wallet syncs normally with the PreProd indexer
  • No errors, no timeouts, no debug messages
  • The process stays alive consuming CPU

Possible causes: Slow proof generation without feedback, silent tDUST insufficiency, no configurable timeout in deployContract().


Bug #5: Kittie DApp Doesn't Work

Severity: High

The Midnight example app "Kittie" (Midnight Kitties) doesn't work. As documented in our previous post, the tutorial asks for a 32-byte hex seed, but Lace wallet uses a 24-word mnemonic with completely different HD derivation.

Two incompatible SDKs:

  • Lace Wallet (new): HDWallet + mnemonicToSeedSync + Roles
  • Kitties (old): direct hex seed, no HD derivation

The official example referenced in the Academy is broken for any developer using Lace Wallet.


Bug #6: Compact Types That Don't Work at Runtime

Severity: Medium

The Compact language documentation and examples mention types that don't work in the actual runtime:

Documented TypeStatusWorkaround
sealed ledgerDoesn't workUse regular export ledger
Boolean in ledgerDoesn't work as slot typeUse Uint<64> with 0/1
CounterDoesn't workUse Uint<64> with manual read + increment

This cost hours of trial-and-error until discovering that the runtime only supports Uint<64> and Bytes<32> as ledger slot types.


Consolidated Workaround Table

ProblemWorkaround
No compactc compilerHand-write contract/index.js following pre-compiled contract patterns
Buggy signRecipeManual signing with correct proof markers
Missing getBalance()Check balance via Lace Wallet manually
Deploy hangs on PreProdLocal deploy works; PreProd awaiting resolution
Boolean doesn't work in ledgerUse Uint<64> with 0/1
Counter doesn't workUse Uint<64> with manual read + increment
sealed ledger doesn't workUse regular export ledger
Kittie incompatible with LaceBuild from scratch based on counter example

What Works Well

  1. Compact language — Elegant syntax. disclose(), assert(), and the privacy model are well-designed
  2. compact-runtime for testing — Testing contracts locally without a network is excellent. 18 tests in under 500ms
  3. Privacy model — Opaque Bytes<32> for sensitive data, superior to any other chain
  4. Local devnet deploy — Docker compose works. Deploy of 3 compiled contracts completed successfully
  5. ZK circuits — Separation between pure and impure, with automatic proofs

Results

Compact contracts:     4
ZK circuits: 12 total
Passing tests: 18/18
Local devnet deploy: 3/4 contracts (AgentRegistry without ZK keys)
PreProd deploy: Blocked (proof generation timeout)
Frontend: React 19 + Vite 6, 3 pages
Time on workarounds: ~60% of total time

Recommendations for the Midnight Team

  1. Publish compactc as an npm package — Blocking for any developer
  2. Fix signRecipe — Critical bug that prevents deploys
  3. Add getBalance() to WalletFacade — Basic API missing
  4. Timeout + logs in deployContract() — Can't hang forever without feedback
  5. Keep examples working — Kittie referenced in Academy but broken
  6. Create create-midnight-app — Template with pinned compatible versions
  7. Document hardware requirements — How much RAM/CPU does the proof server need?

Links:

  • Repo
  • Wallet: mn_dust_preprod1wd4mw37k4x26sfmtm5mmwhcgck5w43kqkp29f4qcjwtlaw9upwfywv50p24
  • Stack: Compact 0.29.0 | Runtime 0.14.0 | Node 20.18.1