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-v8

BUILD 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

1

Build

DApp builds unproven tx (SDK + compiled contract)

2

Prove

DApp calls proveTx() → wallet → proof server → ZK proof generated

3

Balance

DApp calls balanceTx() → wallet → balance service → server adds dust fees

4

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

MethodReturnsDescription
connect(networkId)ConnectedAPIConnect to wallet
namestring'1AM'
apiVersionstring'4.0.0'

ConnectedAPI

MethodReturnsDescription
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()ConfigurationNetwork URLs, proof server
balanceUnsealedTransaction(hex){ tx }Balance a proved tx (server pays dust)
submitTransaction(hex)voidSubmit to chain
getProvingProvider(keyProvider)ProvingProviderGet ZK proving provider
signData(data, options)SignatureSign 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

NetworkStatusIndexerRPC
previewActiveindexer.preview.midnight.networkrpc.preview.midnight.network
preprodActiveindexer.preprod.midnight.networkrpc.preprod.midnight.network
mainnetActiveindexer.mainnet.midnight.networkrpc.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
23                  1. Proves ZK circuits (native binary, ~2-5s)
4                  2. Adds dust fees (server wallet pool)
5                  3. Returns finalized transaction
67          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.

EndpointMethodDescription
/healthGETServer health + upstream status
/provePOSTGenerate ZK proof (native binary)
/verifyPOSTVerify a ZK proof
/prove-and-balancePOSTProve + balance an unproved TX
/balance-onlyPOSTBalance a pre-proven TX
/wallet-statusGETSponsorship 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 init

What 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 ZKMint