Real ZK privacy, five secrets, one build.
Every mega-prompt in this repo uses the same pattern, because it's the only pattern that lets a Lovable account ship a verifiable Midnight demo in one shot.
Why Midnight and not Ethereum?
Midnight is a privacy-first Layer 1 where every smart contract is a triple: a public ledger, a ZK circuit, and a local off-chain component. Circuit parameters are private by default — moving anything to public state requires an explicitdisclose() call, so leaks become compile errors instead of runtime bugs. The preview testnet is funded by a free tDUST faucet; the block explorer, indexer, and Lace wallet are the same tooling you'd use on mainnet. Move to mainnet after the hackathon by flipping VITE_NETWORK_ID.
The recipe
# 1. In your Lovable project, add five secrets (Settings -> Secrets): VITE_NETWORK_ID=preview VITE_INDEXER_URL=https://indexer.preview.midnight.network/api/v4/graphql VITE_INDEXER_WS_URL=wss://indexer.preview.midnight.network/api/v4/graphql/ws VITE_PROOF_SERVER_URL=http://localhost:6300 VITE_DEFAULT_CONTRACT=<paste the address printed by your first deploy> # Optional (only for ideas that pin artefacts to IPFS): VITE_PINATA_JWT=eyJhbGciOi... # 2. Install the Lace wallet extension: open https://www.lace.io/ # 3. Get tNIGHT from the Midnight faucet (pick the network you're building on): open https://midnight-tmnight-preview.nethermind.dev/ # Preview open https://midnight-tmnight-preprod.nethermind.dev/ # Preprod # Then in Lace click "Generate tDUST" to delegate tNIGHT → tDUST. # 4. Start the local proof server (one terminal tab, leave running): docker run -p 6300:6300 midnightntwrk/proof-server:latest midnight-proof-server -v # 5. Copy a mega-prompt from this repo into Lovable. One paste: # - scaffolds the React + Vite app with WASM + top-level-await plugins # - writes the Compact contract (with hackathon credit in the header) # - wires Lace detection, provider bootstrap, and the witness callback # - shows Proving -> Balancing -> Submitting -> Confirmed transaction states # - reads public ledger state from the Midnight Indexer # - exposes the contract address + explorer link in the UI # 6. Open the Midnight explorer link. Your demo is provably private, provably on chain.
1. The Compact contract — credit baked in
Every .compact file deployed from a Creative Midnight prompt MUST carry the hackathon credit as a header comment, so provenance lives alongside the ZK verifying key.
// contracts/TimestampLog.compact — every contract carries the hackathon credit as a header comment
// Built during the Creative AI & Quantum Hackathon
// organised by StreetKode Fam during Indian Krump Festival 14
pragma language_version 0.23;
import CompactStandardLibrary;
export ledger entry_count: Counter;
export ledger last_message: Opaque<"string">;
export ledger last_author_commitment: Bytes<32>;
witness localSecretKey(): Bytes<32>;
constructor() {
entry_count.increment(1);
last_message = disclose("(empty)");
}
export circuit appendEntry(newMessage: Opaque<"string">): [] {
const sk = localSecretKey();
const seq = entry_count as Field as Bytes<32>;
last_author_commitment = disclose(
persistentHash<Vector<3, Bytes<32>>>([pad(32, "log:author:"), seq, sk])
);
last_message = disclose(newMessage);
entry_count.increment(1);
}
2. Compile & run the proof server
# One-time toolchain install curl --proto '=https' --tlsv1.2 -LsSf \ https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh source ~/.bashrc compact update # Compile — produces ZK proving keys + TypeScript bindings compact compile contracts/TimestampLog.compact contracts/managed/timestamp-log # Serve the ZK keys to the browser (FetchZkConfigProvider reads them from /keys and /zkir) cp -r contracts/managed/timestamp-log/keys ./public/keys cp -r contracts/managed/timestamp-log/zkir ./public/zkir # Start the local proof server (all tx submits go through this) docker run -p 6300:6300 midnightntwrk/proof-server:latest midnight-proof-server -v
3. Lace wallet + provider bootstrap
// src/lib/lace.ts — Lace wallet detection + provider bootstrap (client-only)
import { Buffer } from 'buffer'; (globalThis as any).Buffer = Buffer;
import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id';
import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider';
import { httpClientProofProvider } from '@midnight-ntwrk/midnight-js-http-client-proof-provider';
import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';
import semver from 'semver';
export async function initProviders() {
const net = import.meta.env.VITE_NETWORK_ID ?? 'preview';
setNetworkId(net);
const lace = await new Promise<any>((res, rej) => {
const t0 = Date.now();
const iv = setInterval(() => {
const w = Object.values((window as any).midnight ?? {}).find((x: any) =>
x && 'apiVersion' in x && semver.satisfies(x.apiVersion, '4.x'));
if (w) { clearInterval(iv); res(w); }
else if (Date.now() - t0 > 5000) { clearInterval(iv); rej(new Error('Install Lace: https://www.lace.io/')); }
}, 100);
});
const api = await lace.connect(net);
const cfg = await api.getConfiguration();
const zk = new FetchZkConfigProvider(window.location.origin, fetch.bind(window));
return {
connectedAPI: api,
proofProvider: httpClientProofProvider(cfg.proverServerUri ?? import.meta.env.VITE_PROOF_SERVER_URL, zk),
publicDataProvider: indexerPublicDataProvider(cfg.indexerUri, cfg.indexerWsUri),
zkConfigProvider: zk,
};
}
Lace ships as a mobile wallet today (iOS / Android, the MetaMask / Phantom equivalent for the Cardano ecosystem) — but only for Cardano. Midnight support on mobile is not shipped yet, so for these demos install the Lace desktop browser extension and switch it to Midnight preview / preprod.
4. Pin artefacts to IPFS (optional)
// src/lib/pinata.ts — pin a Blob to IPFS, then commit the CID via Compact
export async function pinToIPFS(file: Blob, name = "artifact") {
const fd = new FormData(); fd.append("file", file, name);
const r = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", {
method: "POST",
headers: { Authorization: `Bearer ${import.meta.env.VITE_PINATA_JWT}` },
body: fd,
});
return (await r.json()).IpfsHash as string; // the CID — commit this to the ledger
}
Hackathon rules of thumb
- · One mega-prompt = one build message. Don't iterate the architecture, iterate the UI.
- · Always show the live Midnight explorer link in the UI — that's your proof.
- · Design for 30–120s proof latency. Show a Proving state; keep the rest of the UI usable.
- · Private-witness ideas: never send the 32-byte secret over the network. localStorage only.
- · Add a "Built during the Creative AI & Quantum Hackathon — StreetKode Fam · Indian Krump Festival 14" line to your footer.