DEV Community

Tosh
Tosh

Posted on

The Claude Prompts I Use to Build Midnight dApps Faster

The Claude Prompts I Use to Build Midnight dApps Faster

Midnight's development stack is genuinely different from anything else in the blockchain space. You're writing Compact — a DSL for ZK circuits — alongside TypeScript that calls into a proof server, coordinates with a DApp connector, and manages UTXO state that doesn't behave like Ethereum accounts. The mental model takes weeks to build.

What I've found is that Claude (and other LLMs) can compress that learning curve significantly, but only if you prompt correctly. Generic prompts produce generic answers. The Midnight-specific prompts I've developed produce answers that are actually useful.

Here are the six prompts I use most in my Midnight development workflow, with real before/after examples showing what difference the specificity makes.


1. Debugging Compact Circuit Errors

Generic approach: "What does this error mean?"

What actually works:

Explain this Compact circuit error: TypeError: Cannot assign to 'ledger.balance' in a pure circuit — ledger writes require an impure context

I'm writing a Midnight smart contract in Compact. This error appears when I try to update ledger state inside a circuit I thought was a transaction entrypoint. Explain what the error means, why Compact distinguishes pure from impure circuits, and how I should restructure the code.

Before (generic result): A vague explanation that pure functions don't have side effects, with no Midnight-specific context.

After (specific result): Claude explains that Compact circuits are pure by default — they can only read ledger state and return values. Ledger mutations require a transition function (or a circuit called from one), which is the mechanism through which state changes are committed. It explains the mental model: think of pure circuits as things that run inside the proof, and transitions as the "commit" step that changes on-chain state.

The key is naming the environment (Compact), naming the error message verbatim, and asking for the why and the fix in the same prompt.


2. TypeScript SDK Version Compatibility Review

Midnight's TypeScript SDK has changed significantly between releases. Code written against @midnight-ntwrk/midnight-js-contracts 0.1.x can have subtle issues on 0.2.x+.

The prompt:

Review this Midnight TypeScript code for SDK version compatibility. I'm using @midnight-ntwrk/midnight-js-contracts version 0.2.4 and the proof server from the midnight-js-examples repo tagged at v0.2.4.

const deployedContract = await deployContract(wallet, {
  contract: counterContract,
  privateStateKey: 'counter',
  initialPrivateState: { secretValue: 0n }
});

The deployment hangs indefinitely. No error is thrown. Identify what might be wrong and what to check.

What this surfaces: Claude will flag that in newer SDK versions, deployment requires the proof server to be running and reachable, that the initialPrivateState shape must match the Compact circuit's expected witness type exactly, and that secretValue: 0n using BigInt syntax vs number is a common source of silent type mismatches. It'll also suggest checking the proof server logs rather than waiting on the deployment promise.

The before version of this prompt — "why is my contract deployment hanging" — produces suggestions to check network connectivity, gas, etc. None of which apply to Midnight.


3. Edge Case Analysis for Contract Types

When you're designing a new contract, the hardest part isn't writing the happy path — it's knowing what can go wrong. This prompt externalizes that thinking.

The prompt:

I'm building a Midnight escrow contract in Compact. The contract holds shielded NIGHT tokens until both parties agree to release, with a timeout after which the depositor can reclaim. What edge cases am I missing in this design?

Current assumptions:

  • Depositor calls deposit() with an amount
  • Both parties call approve() to release to recipient
  • After block height X, depositor can call reclaim()

Focus on: ZK proof failures, UTXO race conditions, timeout manipulation, and cases where one party acts in bad faith.

What this surfaces: Claude will typically catch: (1) the timeout based on block height is observable on-chain, leaking information about when the escrow expires; (2) if the UTXO is spent in a different transaction before reclaim() fires, the coin is lost; (3) there's no protection against the depositor calling both approve() and reclaim() in a race if timing is tight; (4) the approve() circuit needs to track which party already approved to prevent double-counting.

The specificity — naming Midnight, naming the exact mechanisms, listing what you want focused on — is what produces useful edge case lists rather than generic smart contract pitfalls.


4. Business Requirement to Ledger State Design

One of the most underused prompt patterns: translating product requirements into Compact ledger state before writing any code.

The prompt:

Translate this business requirement into a Compact ledger state design:

"Users can create private polls where they submit votes without revealing their choice. After the poll closes, anyone can verify the vote count is correct without learning individual votes."

Design the ledger section of a Compact contract that supports this. Explain each field you include, what type it is, and why it's public vs private. Don't write the circuit logic yet — just the state model.

What this produces: A structured analysis of what needs to be on-ledger (poll ID, deadline block, aggregated vote counts per option), what should be derived in circuits rather than stored (individual votes stay private as witness inputs), and the ZK property being achieved (tallying without revelation). Claude will also flag the commitment scheme you'd need if you want provably correct vote aggregation.

This front-loading saves hours. You don't want to design circuit logic around the wrong data model.


5. Witness Function Debugging

ZK proof failures are among the hardest errors to debug in Midnight because the failure usually manifests as "proof generation failed" with no further information. The problem is almost always in the witness.

The prompt:

My ZK proof is failing — debug this witness function. I'm getting ProofGenerationError: witness computation failed with no further details. Here's the witness function in my TypeScript code:

const witnesses = {
  computeSecret: async (secretInput: bigint): Promise<bigint> => {
    return secretInput * 31337n % 2n ** 64n;
  }
};

The corresponding Compact circuit expects a Uint<64>. Explain what could cause witness computation to fail, specifically for Uint<64> values in Midnight's proof system.

What this surfaces: Claude will flag that the modulo operation % 2n ** 64n may produce values that are valid BigInts in JavaScript but fail when converted to Uint<64> if the Compact runtime expects the value to fit within a specific field element representation. It'll point out that the issue might be at the boundary — values at or near 2^64 - 1 — and suggest adding runtime bounds checking in the witness function before returning.

Without the Compact/Midnight context, you'd get a generic "check your arithmetic" answer.


6. Solidity to Compact Pattern Translation

If you have Solidity background, the conceptual mappings aren't always obvious. This prompt is a shortcut.

The prompt:

What's the Compact equivalent of this Solidity pattern:

mapping(address => uint256) public balances;

function transfer(address to, uint256 amount) external {
  require(balances[msg.sender] >= amount, "insufficient");
  balances[msg.sender] -= amount;
  balances[to] += amount;
}

I'm building a token transfer mechanism in Midnight. I know Midnight doesn't have the same account model — explain what the equivalent pattern would be in Compact, including what changes in the privacy model and how UTXO state works differently from mappings.

What this produces: Claude explains that Midnight doesn't use address-to-balance mappings because that would expose transfer history. Instead, token balances are held as UTXOs (unspent transaction outputs) in shielded coins. The "transfer" pattern in Compact involves spending an existing coin UTXO and creating a new one for the recipient — the balance is never stored as a named mapping. Claude will walk through the sendShielded approach and explain why the privacy model changes the entire architecture.

This translation prompt is the fastest way to break Solidity mental models and build Midnight ones.


The Repeatable Pattern

Every prompt here follows the same structure:

  1. Name the environment explicitly (Compact, Midnight, the specific SDK version)
  2. Include the actual code or error message — no paraphrasing
  3. Ask for the why alongside the what
  4. Scope the focus area (proof failures, UTXO model, circuit vs transition, etc.)

Generic questions get generic answers. The more context you give, the more Midnight-specific the response.


These six are a small slice of what I've built up over months of Midnight development. The full library covers proof server debugging, DApp connector integration, ZK proof composition, UTXO management patterns, and more — 500+ prompts organized by use case.

$27 instant download: Claude & ChatGPT Prompts for Midnight dApp Development

If you're building on Midnight and want to stop losing hours to context switches, this is the shortcut.

Top comments (0)