midnight-js

The Midnight.js TypeScript SDK for building DApps on Midnight Network. Covers all provider setup, wallet SDK integration (HDWallet, WalletFacade, ShieldedWallet, UnshieldedWallet, DustWallet), contract deployment, circuit calls, private state management, DUST generation, and testkit usage. Use this skill whenever a user is wiring up providers, managing wallets in a Node.js or browser context, deploying or calling Compact contracts from TypeScript, handling DUST/fee flow, reading ledger state, or writing tests for Midnight DApps.

kali-decoder/midnight-skills91 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: midnight-js
description: The Midnight.js TypeScript SDK for building DApps on Midnight Network. Covers all provider setup, wallet SDK integration (HDWallet, WalletFacade, ShieldedWallet, UnshieldedWallet, DustWallet), contract deployment, circuit calls, private state management, DUST generation, and testkit usage. Use this skill whenever a user is wiring up providers, managing wallets in a Node.js or browser context, deploying or calling Compact contracts from TypeScript, handling DUST/fee flow, reading ledger state, or writing tests for Midnight DApps.
license: MIT
---

# Midnight.js Skill

Midnight.js is the TypeScript SDK for building DApps on Midnight Network — analogous to Web3.js for Ethereum. The official SDK page is "coming soon"; this skill is grounded in the official Counter CLI tutorial and the 1AM starter template.

**Primary references:**
- `docs.midnight.network/tutorials/counter/counter-cli` — official full implementation
- `github.com/midnightntwrk/example-counter` — reference repo (Node.js / headless wallet)
- `github.com/webisoftSoftware/1AM-starter-template` — browser wallet variant

---

## 1) Package Map

| Package | Purpose |
|---|---|
| `@midnight-ntwrk/midnight-js-contracts` | `deployContract`, `findDeployedContract`, `submitCallTx` |
| `@midnight-ntwrk/midnight-js-types` | `MidnightProviders`, `WalletProvider`, `MidnightProvider`, `createProofProvider` |
| `@midnight-ntwrk/midnight-js-indexer-public-data-provider` | `indexerPublicDataProvider` (GraphQL indexer client) |
| `@midnight-ntwrk/midnight-js-fetch-zk-config-provider` | `FetchZkConfigProvider` (browser / CDN) |
| `@midnight-ntwrk/midnight-js-node-zk-config-provider` | `NodeZkConfigProvider` (Node.js / filesystem) |
| `@midnight-ntwrk/midnight-js-http-client-proof-provider` | `httpClientProofProvider` (self-hosted proof server) |
| `@midnight-ntwrk/midnight-js-level-private-state-provider` | `levelPrivateStateProvider` (persistent LevelDB private state) |
| `@midnight-ntwrk/midnight-js-network-id` | `setNetworkId`, `getNetworkId` |
| `@midnight-ntwrk/compact-runtime` | `ContractState` (indexer deserialization) |
| `@midnight-ntwrk/ledger-v8` | `Transaction`, `LedgerParameters`, `ZswapSecretKeys`, `DustSecretKey` |
| `@midnight-ntwrk/compact-js` | `CompiledContract`, `ImpureCircuitId` |
| `@midnight-ntwrk/wallet-sdk-facade` | `WalletFacade` (unified wallet) |
| `@midnight-ntwrk/wallet-sdk-hd` | `HDWallet`, `generateRandomSeed`, `Roles` |
| `@midnight-ntwrk/wallet-sdk-shielded` | `ShieldedWallet` |
| `@midnight-ntwrk/wallet-sdk-unshielded-wallet` | `UnshieldedWallet`, `createKeystore`, `PublicKey` |
| `@midnight-ntwrk/wallet-sdk-dust-wallet` | `DustWallet` |
| `@midnight-ntwrk/wallet-sdk-address-format` | `MidnightBech32m`, `ShieldedAddress`, etc. |

---

## 2) Network Configuration

**Always call `setNetworkId` before any SDK operation.** It sets a global that all libraries use automatically.

```typescript
import { setNetworkId, getNetworkId } from '@midnight-ntwrk/midnight-js-network-id';

// Call once at startup, in the config constructor or before building providers
setNetworkId('preprod'); // 'preprod' | 'preview' | 'mainnet' | 'undeployed'
```

**Network endpoints:**

| Network | Indexer HTTP | Indexer WS | RPC |
|---|---|---|---|
| `preprod` | `https://indexer.preprod.midnight.network/api/v4/graphql` | `wss://indexer.preprod.midnight.network/api/v4/graphql/ws` | `https://rpc.preprod.midnight.network` |
| `preview` | `https://indexer.preview.midnight.network/api/v4/graphql` | `wss://indexer.preview.midnight.network/api/v4/graphql/ws` | `wss://rpc.preview.midnight.network` |
| `mainnet` | `https://indexer.mainnet.midnight.network/api/v4/graphql` | `wss://indexer.mainnet.midnight.network/api/v4/graphql/ws` | `https://rpc.mainnet.midnight.network` |
| `undeployed` (local) | `http://localhost:8088/api/v4/graphql` | `ws://localhost:8088/api/v4/graphql/ws` | `ws://localhost:9944` |

---

## 3) Compiled Contract Setup

Pre-compile the contract once at module load — not on every deploy/call.

```typescript
import { CompiledContract } from '@midnight-ntwrk/compact-js';
import { Contract, witnesses } from './managed/counter'; // generated by compact compiler

// Node.js: load ZK assets from filesystem
import { NodeZkConfigProvider } from '@midnight-ntwrk/midnight-js-node-zk-config-provider';
const zkConfigPath = path.resolve('contract/src/managed/counter');

const compiledContract = CompiledContract.make('counter', Contract).pipe(
  CompiledContract.withVacantWitnesses,
  CompiledContract.withCompiledFileAssets(zkConfigPath),
);

// Browser / CDN: load ZK assets via fetch
import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider';
const compiledContract = CompiledContract.make('YourContract', Contract).pipe(
  CompiledContract.withVacantWitnesses,
  CompiledContract.withCompiledFileAssets('/zk/your-contract'),
);
```

---

## 4) Type Definitions (Important Pattern)

Define typed aliases for your contract's providers — this is the Midnight.js pattern:

```typescript
import type { MidnightProviders } from '@midnight-ntwrk/midnight-js-types';
import type { DeployedContract, FoundContract } from '@midnight-ntwrk/midnight-js-contracts';
import type { ImpureCircuitId } from '@midnight-ntwrk/compact-js';
import { Counter, type CounterPrivateState } from './managed/counter';

// Extract circuit IDs from the contract type
export type CounterCircuits = ImpureCircuitId<Counter.Contract<CounterPrivateState>>;

// Private state key (string literal type)
export const CounterPrivateStateId = 'counterPrivateState' as const;
export type CounterPrivateStateId = typeof CounterPrivateStateId;

// Full providers type for this contract
export type CounterProviders = MidnightProviders<
  CounterCircuits,
  CounterPrivateStateId,
  CounterPrivateState
>;

// Contract instance types
export type CounterContract = Counter.Contract<CounterPrivateState>;
export type DeployedCounterContract = DeployedContract<CounterContract> | FoundContract<CounterContract>;
```

---

## 5) Wallet Setup (Node.js / Headless)

### HD Key Derivation

```typescript
import { HDWallet, generateRandomSeed, Roles } from '@midnight-ntwrk/wallet-sdk-hd';
import * as ledger from '@midnight-ntwrk/ledger-v8';
import { Buffer } from 'buffer';

// Generate a fresh random seed (returns Uint8Array)
const seed = generateRandomSeed();
const seedHex = Buffer.from(seed).toString('hex'); // save this

// Or restore from existing hex seed
const seedHex = '...'; // from user input

const hdWallet = HDWallet.fromSeed(Buffer.from(seedHex, 'hex'));
if (hdWallet.type !== 'seedOk') throw new Error('Invalid seed');

const derivationResult = hdWallet.hdWallet
  .selectAccount(0)
  .selectRoles([Roles.Zswap, Roles.NightExternal, Roles.Dust])
  .deriveKeysAt(0);

if (derivationResult.type !== 'keysDerived') throw new Error('Key derivation failed');

hdWallet.hdWallet.clear(); // wipe secret material from memory

const keys = derivationResult.keys;
const shieldedSecretKeys = ledger.ZswapSecretKeys.fromSeed(keys[Roles.Zswap]);
const dustSecretKey = ledger.DustSecretKey.fromSeed(keys[Roles.Dust]);
```

### Wallet Construction

```typescript
import { ShieldedWallet } from '@midnight-ntwrk/wallet-sdk-shielded';
import { UnshieldedWallet, createKeystore, PublicKey, InMemoryTransactionHistoryStorage } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
import { DustWallet } from '@midnight-ntwrk/wallet-sdk-dust-wallet';
import { WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade';

const unshieldedKeystore = createKeystore(keys[Roles.NightExternal], getNetworkId());

const shieldedWallet = ShieldedWallet({
  networkId: getNetworkId(),
  indexerClientConnection: { indexerHttpUrl: indexer, indexerWsUrl: indexerWS },
  provingServerUrl: new URL(proofServer),
  relayURL: new URL(node.replace(/^http/, 'ws')), // convert http → ws
}).startWithSecretKeys(shieldedSecretKeys);

const unshieldedWallet = UnshieldedWallet({
  networkId: getNetworkId(),
  indexerClientConnection: { indexerHttpUrl: indexer, indexerWsUrl: indexerWS },
  txHistoryStorage: new InMemoryTransactionHistoryStorage(),
}).startWithPublicKey(PublicKey.fromKeyStore(unshieldedKeystore));

const dustWallet = DustWallet({
  networkId: getNetworkId(),
  costParameters: {
    additionalFeeOverhead: 300_000_000_000_000n,
    feeBlocksMargin: 5,
  },
  indexerClientConnection: { indexerHttpUrl: indexer, indexerWsUrl: indexerWS },
  provingServerUrl: new URL(proofServer),
  relayURL: new URL(node.replace(/^http/, 'ws')),
}).startWithSecretKey(dustSecretKey, ledger.LedgerParameters.initialParameters().dust);

const wallet = new WalletFacade(shieldedWallet, unshieldedWallet, dustWallet);
await wallet.start(shieldedSecretKeys, dustSecretKey);
```

### Wallet Sync (RxJS pattern — always used with WalletFacade)

```typescript
import * as Rx from 'rxjs';

// Wait until wallet is fully synced
const syncedState = await Rx.firstValueFrom(
  wallet.state().pipe(
    Rx.throttleTime(5_000),
    Rx.filter((state) => state.isSynced),
  ),
);

// Wait until wallet has non-zero unshielded balance
import { unshieldedToken } from '@midnight-ntwrk/ledger-v8';

const balance = await Rx.firstValueFrom(
  wallet.state().pipe(
    Rx.throttleTime(10_000),
    Rx.filter((s) => s.isSynced),
    Rx.map((s) => s.unshielded.balances[unshieldedToken().raw] ?? 0n),
    Rx.filter((balance) => balance > 0n),
  ),
);
```

**Node.js WebSocket requirement:**
```typescript
import { WebSocket } from 'ws';
// Required for GraphQL subscriptions (wallet sync) to work in Node.js
globalThis.WebSocket = WebSocket as unknown as typeof globalThis.WebSocket;
// Put this at the very top of your entry file, before any wallet imports
```

---

## 6) DUST Generation (Preprod / Undeployed Only)

NIGHT tokens generate DUST over time, but only after UTXOs are explicitly registered on-chain. DUST is the non-transferable fee resource for all transactions.

```typescript
// 1. Wait for sync
const state = await Rx.firstValueFrom(wallet.state().pipe(Rx.filter((s) => s.isSynced)));

// 2. Check if DUST already available
if (state.dust.availableCoins.length > 0) {
  console.log('DUST already available:', state.dust.walletBalance(new Date()));
  return;
}

// 3. Register unregistered NIGHT UTXOs
const nightUtxos = state.unshielded.availableCoins.filter(
  (coin: any) => coin.meta?.registeredForDustGeneration !== true,
);

if (nightUtxos.length > 0) {
  const recipe = await wallet.registerNightUtxosForDustGeneration(
    nightUtxos,
    unshieldedKeystore.getPublicKey(),
    (payload) => unshieldedKeystore.signData(payload),
  );
  const finalized = await wallet.finalizeRecipe(recipe);
  await wallet.submitTransaction(finalized);
}

// 4. Wait for DUST balance > 0 (may take a few minutes)
await Rx.firstValueFrom(
  wallet.state().pipe(
    Rx.throttleTime(5_000),
    Rx.filter((s) => s.isSynced),
    Rx.filter((s) => s.dust.walletBalance(new Date()) > 0n),
  ),
);
```

**DUST troubleshooting:**
- DUST balance drops to 0 after failed deploy → restart DApp to release locked DUST coins
- `pendingCoins > 0 && availableCoins === 0` → DUST locked by a pending/failed transaction
- NIGHT registered but DUST = 0 → still accruing, wait a few minutes

---

## 7) Provider Configuration

### Node.js (self-hosted proof server)

```typescript
import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';
import { httpClientProofProvider } from '@midnight-ntwrk/midnight-js-http-client-proof-provider';
import { NodeZkConfigProvider } from '@midnight-ntwrk/midnight-js-node-zk-config-provider';
import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider';

const zkConfigProvider = new NodeZkConfigProvider<CounterCircuits>(zkConfigPath);

const providers: CounterProviders = {
  privateStateProvider: levelPrivateStateProvider<CounterPrivateStateId>({
    privateStateStoreName: 'counter-private-state',  // LevelDB store name
    walletProvider: walletAndMidnightProvider,
  }),
  publicDataProvider: indexerPublicDataProvider(indexerHttp, indexerWs),
  zkConfigProvider,
  proofProvider: httpClientProofProvider(proofServerUrl, zkConfigProvider),
  walletProvider: walletAndMidnightProvider,
  midnightProvider: walletAndMidnightProvider,
};
```

### Browser (1AM wallet / CDN ZK assets)

```typescript
import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider';
import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';
import { createProofProvider } from '@midnight-ntwrk/midnight-js-types';

const config = await connectedAPI.getConfiguration();
setNetworkId(config.networkId);

const zkConfigProvider = new FetchZkConfigProvider(
  new URL(zkAssetBasePath, window.location.origin).toString(),
  window.fetch.bind(window),
);

const provingProvider = await connectedAPI.getProvingProvider(zkConfigProvider);

const providers = {
  publicDataProvider: indexerPublicDataProvider(config.indexerUri, config.indexerWsUri),
  zkConfigProvider,
  proofProvider: createProofProvider(provingProvider),  // wraps 1AM proving provider
  walletProvider: { /* see 1AM skill */ },
  midnightProvider: { /* see 1AM skill */ },
  privateStateProvider: createPrivateStateProvider(), // in-memory for browser
};
```

---

## 8) WalletProvider & MidnightProvider (Node.js headless)

This is the bridge between WalletFacade and the midnight-js contracts API:

```typescript
import type { WalletProvider, MidnightProvider } from '@midnight-ntwrk/midnight-js-types';
import * as ledger from '@midnight-ntwrk/ledger-v8';

const state = await Rx.firstValueFrom(wallet.state().pipe(Rx.filter((s) => s.isSynced)));

const walletAndMidnightProvider: WalletProvider & MidnightProvider = {
  getCoinPublicKey() {
    return state.shielded.coinPublicKey.toHexString();
  },
  getEncryptionPublicKey() {
    return state.shielded.encryptionPublicKey.toHexString();
  },
  async balanceTx(tx, ttl?) {
    const recipe = await wallet.balanceUnboundTransaction(
      tx,
      { shieldedSecretKeys, dustSecretKey },
      { ttl: ttl ?? new Date(Date.now() + 30 * 60 * 1000) },
    );

    // ⚠️ KNOWN BUG WORKAROUND: wallet SDK signRecipe hardcodes 'pre-proof' but
    // proven (UnboundTransaction) intents contain 'proof' data → "Failed to clone intent"
    // Sign intents manually with the correct proof marker (see signTransactionIntents below)
    signTransactionIntents(recipe.baseTransaction, signFn, 'proof');
    if (recipe.balancingTransaction) {
      signTransactionIntents(recipe.balancingTransaction, signFn, 'pre-proof');
    }

    return wallet.finalizeRecipe(recipe);
  },
  submitTx(tx) {
    return wallet.submitTransaction(tx) as any;
  },
};
```

### The "Failed to clone intent" Bug Fix

```typescript
/**
 * Workaround for wallet SDK bug where signRecipe hardcodes 'pre-proof',
 * causing failures when signing proven (UnboundTransaction) intents.
 * Call with proofMarker='proof' for baseTransaction, 'pre-proof' for balancingTransaction.
 */
const signTransactionIntents = (
  tx: { intents?: Map<number, any> },
  signFn: (payload: Uint8Array) => ledger.Signature,
  proofMarker: 'proof' | 'pre-proof',
): void => {
  if (!tx.intents || tx.intents.size === 0) return;

  for (const segment of tx.intents.keys()) {
    const intent = tx.intents.get(segment);
    if (!intent) continue;

    const cloned = ledger.Intent.deserialize<
      ledger.SignatureEnabled, ledger.Proofish, ledger.PreBinding
    >('signature', proofMarker, 'pre-binding', intent.serialize());

    const signature = signFn(cloned.signatureData(segment));

    if (cloned.fallibleUnshieldedOffer) {
      const sigs = cloned.fallibleUnshieldedOffer.inputs.map(
        (_: ledger.UtxoSpend, i: number) =>
          cloned.fallibleUnshieldedOffer!.signatures.at(i) ?? signature,
      );
      cloned.fallibleUnshieldedOffer = cloned.fallibleUnshieldedOffer.addSignatures(sigs);
    }

    if (cloned.guaranteedUnshieldedOffer) {
      const sigs = cloned.guaranteedUnshieldedOffer.inputs.map(
        (_: ledger.UtxoSpend, i: number) =>
          cloned.guaranteedUnshieldedOffer!.signatures.at(i) ?? signature,
      );
      cloned.guaranteedUnshieldedOffer = cloned.guaranteedUnshieldedOffer.addSignatures(sigs);
    }

    tx.intents.set(segment, cloned);
  }
};
```

---

## 9) Deploy & Call Contracts

### Deploy

```typescript
import { deployContract } from '@midnight-ntwrk/midnight-js-contracts';

const deployed = await deployContract(providers, {
  compiledContract,
  privateStateId: CounterPrivateStateId,
  initialPrivateState: { privateCounter: 0 },
});

console.log('Contract address:', deployed.deployTxData.public.contractAddress);
```

### Call a Circuit

```typescript
// Using callTx (recommended — automatically builds, proves, balances, submits)
const result = await deployed.callTx.increment();
console.log('txId:', result.public.txId);
console.log('blockHeight:', result.public.blockHeight);
```

### Join an Existing Contract

```typescript
import { findDeployedContract } from '@midnight-ntwrk/midnight-js-contracts';

const contract = await findDeployedContract(providers, {
  contractAddress: '09dbe05f...', // hex string
  compiledContract,
  privateStateId: CounterPrivateStateId,
  initialPrivateState: { privateCounter: 0 },
});

// Now call circuits on it
await contract.callTx.increment();
```

### Read Ledger State

```typescript
import { ContractState } from '@midnight-ntwrk/compact-runtime';
import { Counter } from './managed/counter';

const contractState = await providers.publicDataProvider.queryContractState(contractAddress);

if (contractState === null) {
  console.log('Contract not found');
} else {
  // Apply generated ledger() function to deserialize typed state
  const ledgerState = Counter.ledger(contractState.data);
  console.log('Counter value:', ledgerState.round);
}
```

---

## 10) Private State Provider (In-Memory — Browser / Minimal)

```typescript
import type {
  PrivateStateProvider, PrivateStateId, PrivateStateExport,
  SigningKeyExport,
} from '@midnight-ntwrk/midnight-js-types';

function createPrivateStateProvider() {
  let scope = '';
  const stateStore = new Map<string, unknown>();
  const signingKeyStore = new Map<string, unknown>();
  const key = (id: string) => `${scope}:${id}`;

  return {
    setContractAddress(address: string) { scope = address; },
    async set(id: string, state: unknown) { stateStore.set(key(id), state); },
    async get(id: string) { return stateStore.get(key(id)) ?? null; },
    async remove(id: string) { stateStore.delete(key(id)); },
    async clear() { stateStore.clear(); },
    async setSigningKey(addr: string, k: unknown) { signingKeyStore.set(addr, k); },
    async getSigningKey(addr: string) { return signingKeyStore.get(addr) ?? null; },
    async removeSigningKey(addr: string) { signingKeyStore.delete(addr); },
    async clearSigningKeys() { signingKeyStore.clear(); },
    async exportPrivateStates(): Promise<PrivateStateExport> { throw new Error('Not implemented'); },
    async importPrivateStates() { throw new Error('Not implemented'); },
    async exportSigningKeys(): Promise<SigningKeyExport> { throw new Error('Not implemented'); },
    async importSigningKeys() { throw new Error('Not implemented'); },
  };
}
```

For persistent Node.js private state, use `levelPrivateStateProvider` instead.

---

## 11) Testkit (Integration Testing)

```typescript
import { getTestEnvironment } from '@midnight-ntwrk/testkit-js';
import pino from 'pino';

const logger = pino({ level: 'info' });
let testEnvironment: Awaited<ReturnType<typeof getTestEnvironment>>;

beforeAll(async () => {
  testEnvironment = getTestEnvironment(logger);
  const environmentConfig = await testEnvironment.start();
  // environmentConfig contains indexer, node, proofServer URLs
});

afterAll(async () => {
  await testEnvironment.shutdown();
});

// Get wallet providers for testing
const walletProvider = await testEnvironment.getMidnightWalletProvider();
// or multiple wallets (max 4 on local):
const [wallet1, wallet2] = await testEnvironment.startMidnightWalletProviders(2);
```

**Environment selection via env var:**
```bash
# Local Docker (default)
MN_TEST_ENVIRONMENT=undeployed yarn test

# Against preprod
MN_TEST_ENVIRONMENT=devnet yarn test

# Custom endpoints
MN_TEST_ENVIRONMENT=env-var-remote \
MN_TEST_NETWORK_ID=undeployed \
MN_TEST_INDEXER=http://localhost:8088/api/ \
MN_TEST_INDEXER_WS=ws://localhost:8088/ws/ \
MN_TEST_NODE=http://localhost:9944 \
yarn test
```

---

## 12) Proof Server

For Node.js / headless use, run the proof server locally via Docker:

```bash
# Auto-started proof server (preprod, pulls image automatically)
cd counter-cli && npm run preprod-ps

# Or manually
docker compose -f proof-server.yml up
# Wait for: "starting service... listening on: 0.0.0.0:6300"

# Direct Docker run
docker run -p 6300:6300 midnightntwrk/proof-server:latest midnight-proof-server -v
```

Proof server URL: `http://127.0.0.1:6300`

**Mac ARM (Apple Silicon) fix:** If the proof server hangs, go to Docker Desktop → Settings → General → Virtual Machine Options → select **Docker VMM** → restart Docker.

---

## 13) Common Pitfalls

**`setNetworkId` not called** → cryptic type mismatch errors deep in SDK. Call it immediately at startup, before any SDK operations.

**WebSocket not set globally in Node.js** → wallet sync GraphQL subscriptions silently fail. Put `globalThis.WebSocket = WebSocket` at the top of your entry file.

**ZK assets not found** → double-check `zkConfigPath` is absolute, points to the compiled managed folder, and contains `keys/` and `zkir/` subdirectories. Run the `sync:assets` npm script before dev.

**"Failed to clone intent"** → wallet SDK bug, use the `signTransactionIntents` workaround. Occurs when a proven `UnboundTransaction` is balanced — the SDK hardcodes `'pre-proof'` but the intents contain `'proof'` data.

**DUST = 0 after failed deploy** → known wallet SDK issue. Restart the DApp to release the locked DUST coins. Use the DUST monitor to verify status before deploying.

**Wallet 0 balance after faucet** → wait for sync to complete first. If still 0, verify you used the unshielded address (`mn_addr_preprod1...`), not the shielded address.

**`relayURL` must be WebSocket** → convert `https://` → `wss://`, `http://` → `ws://`. The wallet SDK expects a WebSocket URL for the relay.

**`CompiledContract.make` called on every deploy** → call it once at module load, not inside the deploy function. Loading ZK keys on every call is slow.

**`levelPrivateStateProvider` in browser** → LevelDB is Node.js only. Use the in-memory `createPrivateStateProvider` for browser contexts.

**Old contract address after recompile** → verifier keys change when the Compact contract changes. Old addresses will fail proof verification. Always redeploy after any contract change.

---

## 14) Dependency Versions (Pinned — Official Counter Example)

```json
{
  "@midnight-ntwrk/compact-runtime": "0.15.0",
  "@midnight-ntwrk/ledger-v8": "8.0.3",
  "@midnight-ntwrk/midnight-js-contracts": "4.0.2",
  "@midnight-ntwrk/midnight-js-http-client-proof-provider": "4.0.2",
  "@midnight-ntwrk/midnight-js-indexer-public-data-provider": "4.0.2",
  "@midnight-ntwrk/midnight-js-level-private-state-provider": "4.0.2",
  "@midnight-ntwrk/midnight-js-network-id": "4.0.2",
  "@midnight-ntwrk/midnight-js-node-zk-config-provider": "4.0.2",
  "@midnight-ntwrk/midnight-js-types": "4.0.2",
  "@midnight-ntwrk/midnight-js-utils": "4.0.2",
  "@midnight-ntwrk/wallet-sdk-address-format": "3.1.0",
  "@midnight-ntwrk/wallet-sdk-dust-wallet": "3.0.0",
  "@midnight-ntwrk/wallet-sdk-facade": "3.0.0",
  "@midnight-ntwrk/wallet-sdk-hd": "3.0.1",
  "@midnight-ntwrk/wallet-sdk-shielded": "2.1.0",
  "@midnight-ntwrk/wallet-sdk-unshielded-wallet": "2.1.0",
  "@midnight-ntwrk/compact-js": "2.5.0"
}
```

Node.js version: **22+** (required)

More Deployment & CI/CD skills

azure-enterprise-infra-planner

microsoft/azure-skills

Architect and provision enterprise Azure infrastructure from workload descriptions. For cloud architects and platform engineers planning networking, identity, security, compliance, and multi-resource topologies with WAF alignment. Generates Bicep or Terraform directly (no azd). WHEN: 'plan Azure infrastructure', 'architect Azure landing zone', 'design hub-spoke network', 'plan multi-region DR topology', 'set up VNets firewalls and private endpoints', 'subscription-scope Bicep deployment', 'Azure Backup for VM workloads'. PREFER azure-prepare FOR app-centric workflows.

387.5k

azure-kubernetes-app-deploy

microsoft/azure-skills

Use when deploying an existing web application or API to an already-running Azure Kubernetes Service cluster. Detects the framework, generates a Dockerfile and Kubernetes manifests, validates against AKS Deployment Safeguards, and deploys with verification. WHEN: deploy app to AKS, deploy to existing AKS cluster, containerize app for Kubernetes, generate K8s manifests for Azure, set up CI/CD for AKS, my AKS deployment is failing safeguard checks, I have a Django/Express/Spring Boot app to run on AKS. DO NOT USE FOR: creating or provisioning an AKS cluster (use azure-kubernetes), assessing migration to AKS Automatic (use azure-kubernetes-automatic-readiness), or deploying to non-AKS targets like Web Apps, Container Apps, or Functions.

380.4k

finetuning

microsoft/azure-skills

Fine-tune models on Microsoft Foundry using SFT (supervised), DPO (preference), or RFT (reinforcement with graders). Covers dataset preparation, training job submission, deployment, and evaluation. USE FOR: fine-tune, SFT, DPO, RFT, training data, grader, distillation, fine-tuned model, training job, large file upload, calibrate grader, deploy fine-tuned model, evaluate fine-tuned model. DO NOT USE FOR: general model deployment without fine-tuning (use deploy-model), agent creation (use agents), prompt optimization without training (use prompt-optimizer).

323.2k

← All Deployment & CI/CD skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY