Developer Integration Guide
Build DApps on Midnight Network using the 1AM wallet. Zero dust needed — the proof server sponsors all fees.
Quick Start
The 1AM wallet injects itself at window.midnight['1am']
1. DETECT THE WALLET
1function detectWallet(): Promise<InitialAPI | null> {
2 return new Promise((resolve) => {
3 const wallet = window.midnight?.['1am'];
4 if (wallet) { resolve(wallet); return; }
5
6 let attempts = 0;
7 const interval = setInterval(() => {
8 const w = window.midnight?.['1am'];
9 if (w) { clearInterval(interval); resolve(w); }
10 else if (++attempts > 50) { clearInterval(interval); resolve(null); }
11 }, 100);
12 });
13}2. CONNECT
1const wallet = await detectWallet();
2if (!wallet) throw new Error('1AM wallet not installed');
3
4const connectedAPI = await wallet.connect('preview');3. GET ADDRESSES & BALANCES
1const { shieldedAddress } = await connectedAPI.getShieldedAddresses();
2const { unshieldedAddress } = await connectedAPI.getUnshieldedAddress();
3const { dustAddress } = await connectedAPI.getDustAddress();
4
5const shieldedBalances = await connectedAPI.getShieldedBalances();
6const unshieldedBalances = await connectedAPI.getUnshieldedBalances();
7const { balance, cap } = await connectedAPI.getDustBalance();4. GET NETWORK CONFIG
1const config = await connectedAPI.getConfiguration();
2// {
3// networkId: 'preview',
4// indexerUri: 'https://indexer.preview.midnight.network/api/v4/graphql',
5// indexerWsUri: 'wss://indexer.preview.midnight.network/api/v4/graphql/ws',
6// proverServerUri: 'https://api-preview.1am.xyz',
7// substrateNodeUri: 'wss://rpc.preview.midnight.network',
8// }Build Providers
The 1AM wallet handles proving and dust sponsorship. Your DApp builds providers that delegate to the wallet.
INSTALL DEPENDENCIES
1npm install @midnight-ntwrk/compact-js \
2 @midnight-ntwrk/midnight-js-contracts \
3 @midnight-ntwrk/midnight-js-types \
4 @midnight-ntwrk/midnight-js-fetch-zk-config-provider \
5 @midnight-ntwrk/midnight-js-indexer-public-data-provider \
6 @midnight-ntwrk/midnight-js-network-id \
7 @midnight-ntwrk/ledger-v8BUILD PROVIDERS
1import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id';
2import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider';
3import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';
4
5async function buildProviders(connectedAPI) {
6 const config = await connectedAPI.getConfiguration();
7 setNetworkId(config.networkId);
8
9 const zkConfigProvider = new FetchZkConfigProvider(
10 'https://your-app.com/contract/compiled/your-contract',
11 fetch.bind(window),
12 );
13
14 const publicDataProvider = indexerPublicDataProvider(
15 config.indexerUri, config.indexerWsUri,
16 );
17
18 const provingProvider = await connectedAPI.getProvingProvider(zkConfigProvider);
19 const proofProvider = {
20 async proveTx(unprovenTx) {
21 const { CostModel } = await import('@midnight-ntwrk/ledger-v8');
22 return unprovenTx.prove(provingProvider, CostModel.initialCostModel());
23 },
24 };
25
26 const shieldedAddress = await connectedAPI.getShieldedAddresses();
27 const walletProvider = {
28 getCoinPublicKey: () => shieldedAddress.shieldedCoinPublicKey,
29 getEncryptionPublicKey: () => shieldedAddress.shieldedEncryptionPublicKey,
30 async balanceTx(tx) {
31 const serialized = tx.serialize();
32 const hex = Array.from(serialized).map(b => b.toString(16).padStart(2, '0')).join('');
33 const result = await connectedAPI.balanceUnsealedTransaction(hex);
34 const { Transaction } = await import('@midnight-ntwrk/ledger-v8');
35 const bytes = new Uint8Array(result.tx.match(/.{2}/g).map(b => parseInt(b, 16)));
36 return Transaction.deserialize('signature', 'proof', 'binding', bytes);
37 },
38 };
39
40 const midnightProvider = {
41 async submitTx(tx) {
42 const serialized = tx.serialize();
43 const hex = Array.from(serialized).map(b => b.toString(16).padStart(2, '0')).join('');
44 await connectedAPI.submitTransaction(hex);
45 return tx.identifiers()[0];
46 },
47 };
48
49 return { publicDataProvider, zkConfigProvider, proofProvider, walletProvider, midnightProvider };
50}Deploy & Call Contracts
DEPLOY A CONTRACT
1import { CompiledContract } from '@midnight-ntwrk/compact-js';
2import { deployContract } from '@midnight-ntwrk/midnight-js-contracts';
3import { Contract } from './your-compiled-contract';
4
5const compiledContract = CompiledContract.make('YourContract', Contract).pipe(
6 CompiledContract.withVacantWitnesses,
7 CompiledContract.withCompiledFileAssets('./contract/compiled/your-contract'),
8);
9
10const providers = await buildProviders(connectedAPI);
11const deployed = await deployContract(providers, { compiledContract });
12console.log('Contract address:', deployed.deployTxData.public.contractAddress);CALL A CIRCUIT
1import { submitCallTx } from '@midnight-ntwrk/midnight-js-contracts';
2
3const result = await submitCallTx(providers, {
4 compiledContract,
5 contractAddress: '09dbe05f...',
6 circuitId: 'register',
7 args: [nameHash, 1n],
8});
9
10console.log('Tx hash:', result.public.txHash);Transaction Flow
Build
DApp builds unproven tx (SDK + compiled contract)
Prove
DApp calls proveTx() → wallet → proof server → ZK proof generated
Balance
DApp calls balanceTx() → wallet → balance service → server adds dust fees
Submit
DApp calls submitTx() → wallet → broadcasts to Midnight chain
The user never needs dust. The proof server sponsors all fees.
DApp Connector API Reference
window.midnight['1am'] — InitialAPI
| Method | Returns | Description |
|---|---|---|
| connect(networkId) | ConnectedAPI | Connect to wallet |
| name | string | '1AM' |
| apiVersion | string | '4.0.0' |
ConnectedAPI
| Method | Returns | Description |
|---|---|---|
| getShieldedBalances() | Record<string, bigint> | Shielded token balances |
| getUnshieldedBalances() | Record<string, bigint> | Unshielded token balances |
| getDustBalance() | { cap, balance } | Dust balance |
| getShieldedAddresses() | { shieldedAddress, ... } | Shielded keys |
| getUnshieldedAddress() | { unshieldedAddress } | Unshielded address |
| getDustAddress() | { dustAddress } | Dust address |
| getConfiguration() | Configuration | Network URLs, proof server |
| balanceUnsealedTransaction(hex) | { tx } | Balance a proved tx (server pays dust) |
| submitTransaction(hex) | void | Submit to chain |
| getProvingProvider(keyProvider) | ProvingProvider | Get ZK proving provider |
| signData(data, options) | Signature | Sign arbitrary data |
| makeTransfer(outputs) | { tx } | Create a token transfer |
Hosting ZK Keys
Your compiled contract produces prover keys, verifier keys, and ZKIR files. These must be served via HTTP with CORS enabled.
1your-cdn.com/contract/compiled/your-contract/
2 keys/
3 circuitName.prover (2-10 MB each)
4 circuitName.verifier (2 KB each)
5 zkir/
6 circuitName.bzkir (1-3 KB each)The FetchZkConfigProvider fetches these on demand. Host on any CDN with Access-Control-Allow-Origin: *.
Networks
| Network | Status | Indexer | RPC |
|---|---|---|---|
| preview | Active | indexer.preview.midnight.network | rpc.preview.midnight.network |
| preprod | Active | indexer.preprod.midnight.network | rpc.preprod.midnight.network |
| mainnet | Active | indexer.mainnet.midnight.network | rpc.mainnet.midnight.network |
Use preview for development and testing.
Dust-Free Execution
Users pay nothing. Zero dust, zero NIGHT for fees.
The 1AM Wallet + ProofStation handles all transaction fees server-side. Your users never need to generate dust or hold NIGHT tokens for gas.
1Your DApp → 1AM Wallet → ProofStation
2 ↓
3 1. Proves ZK circuits (native binary, ~2-5s)
4 2. Adds dust fees (server wallet pool)
5 3. Returns finalized transaction
6 ↓
7 1AM Wallet submits to Midnight chain
8
9Total user cost: 0 NIGHT, 0 dust.The key is walletProvider.balanceTx() — it routes the transaction to ProofStation's /balance-only endpoint where the server adds dust from its funded wallet pool.
ProofStation API
ProofStation is the proving + balancing infrastructure behind 1AM Wallet — ZK proof generation, dust sponsorship, and Cardano queries.
| Endpoint | Method | Description |
|---|---|---|
| /health | GET | Server health + upstream status |
| /prove | POST | Generate ZK proof (native binary) |
| /verify | POST | Verify a ZK proof |
| /prove-and-balance | POST | Prove + balance an unproved TX |
| /balance-only | POST | Balance a pre-proven TX |
| /wallet-status | GET | Sponsorship wallet dust balance |
Auth: X-API-Key: pk_live_xxx · 3 networks: api.1am.xyz · api-preprod.1am.xyz · api-preview.1am.xyz
List Your DApp
Submit your DApp to the 1AM app registry to appear in the wallet's Apps tab.
1{
2 "name": "Your DApp",
3 "description": "Short description",
4 "logo": "https://your-app.com/logo.svg",
5 "link": "https://your-app.com",
6 "chains": ["midnight"],
7 "categories": ["defi", "games", "collectibles", "social", "other"]
8}Submit via the 1AM App Registry on GitHub or DM @oneamxyz
AI Skill for Claude Code
Install the 1AM Midnight Skill in Claude Code to give your AI assistant full knowledge of wallet integration, dust-free execution, and contract deployment.
1npx midnight-agent-skills initWhat the skill teaches your AI:
Wallet Detection
Detect and connect the 1AM extension
Provider Setup
Build all 5 providers for contract ops
Dust-Free Tx
Zero gas — ProofStation sponsors fees
Contract Deployment
Deploy and call Compact circuits
ZK Key Hosting
Serve prover keys from CDN
App Registry
Get listed in the 1AM wallet
Demo
Working Example
See the full working example at ZKMint — a DApp that compiles a Compact contract, deploys it, and calls circuits with zero dust required.
View ZKMintSupport
Need help integrating? Reach out through any of these channels.