| name | midnight-wallet |
| description | Complete reference for Midnight Network wallet operations covering the Wallet SDK with shielded wallet, unshielded wallet, DUST wallet, HD wallet, and WalletFacade, wallet types including ShieldedWallet for private transactions and UnshieldedWallet for public transactions, creating wallets from seed phrases, random generation, and existing keys with FluentWalletBuilder and WalletFactory, wallet state management with WalletSaveStateProvider and state serialization, unshielded operations for sending tokens, checking balances, and UTXO management, shielded operations for creating and spending shielded coins with ZswapLocalState, DUST operations including registration, generation, and spend with DustActions and DustLocalState, generating DUST programmatically on preprod with faucet client, key management covering signing keys, coin secret keys, BIP340 derivation, and encryption public keys, transaction signing with signData and verifySignature, wallet provider interface types, wallet CLI and MCP integration for AI agent wallet operations, DApp Connector integration with walletConnectedAPI, connect, disconnect, and request authorization, test wallet patterns using FluentWalletBuilder with deterministic wallets, wallet export and import for private states and signing keys, wallet encryption with StorageEncryption and password rotation, wallet compatibility with CAIP-372 and community wallets like Lace, 1AM, Kuira, and Fireblocks, and best practices for key security, state backup, password management, network switching, and error recovery. |
Midnight Wallet Operations
Wallet SDK Overview
The Midnight Wallet SDK provides a comprehensive set of tools for managing wallets on the Midnight Network. It includes support for shielded private transactions, unshielded public transactions, DUST token management, and hierarchical deterministic key derivation. The SDK is designed to work both in browser environments through wallet extensions and in Node.js environments through programmatic wallet creation.
The core entry point for wallet operations is the WalletFacade which unifies the various wallet types into a single interface. Underneath the facade the SDK implements several specialized wallet types each handling a different aspect of token management and transaction flow.
Wallets on Midnight manage three types of tokens and coins. Unshielded NIGHT tokens exist in public UTXOs visible on the ledger. Shielded coins exist in encrypted form known only to the wallet owner. DUST tokens are a special resource used to pay for transaction fees and must be generated before they can be spent.
The Wallet SDK integrates with the DApp Connector API for browser based DApps and provides standalone programmatic interfaces for server side and CLI applications.
Wallet Types
ShieldedWallet
The ShieldedWallet manages private transactions on the Midnight Network. It handles the creation and spending of shielded coins using zero knowledge proofs through the Zswap protocol. Shielded coins obscure the transaction amount and participants from public view.
Shielded operations include creating shielded coins from unshielded tokens through the shield operation. Spend operations consume existing shielded coins and produce new shielded coins or unshielded tokens as output. The wallet maintains a local state containing the user's shielded coin set that is synchronized with the Zswap protocol state.
class ShieldedWallet<S extends ShieldedCoinInfo> {
async createShieldedCoin(amount: bigint): Promise<ShieldedCoinInfo>
async spendShieldedCoin(coin: ShieldedCoinInfo): Promise<Transaction>
async getShieldedBalances(): Promise<ShieldedBalance[]>
async getShieldedCoins(): Promise<ShieldedCoinInfo[]>
}
UnshieldedWallet
The UnshieldedWallet manages public token operations on the Midnight Network. It handles sending unshielded NIGHT tokens between addresses and managing the UTXO set. Unshielded transactions are visible on the public ledger and can be queried through the indexer.
class UnshieldedWallet {
async sendUnshieldedTokens(to: string, amount: bigint): Promise<Transaction>
async getUnshieldedBalances(): Promise<UnshieldedBalance[]>
async getUnshieldedUtxos(): Promise<Utxo[]>
}
DustWallet
The DustWallet manages DUST tokens which are a separate resource used to pay transaction fees on the Midnight Network. Unlike unshielded and shielded tokens DUST cannot be transferred between users. It is generated by the network and consumed during transaction submission.
class DustWallet {
async registerDust(): Promise<DustRegistration>
async generateDust(amount: bigint): Promise<DustGeneration>
async spendDust(amount: bigint, target: Transaction): Promise<Transaction>
async getDustBalance(): Promise<DustBalance>
async getDustState(): Promise<DustState>
}
HDWallet
The HDWallet implements BIP32 hierarchical deterministic key derivation for generating sequences of wallet keys from a single seed. It supports BIP340 derivation for signing keys and provides child key derivation for organizing keys by purpose.
class HDWallet {
static fromSeed(seed: Uint8Array): HDWallet
static fromMnemonic(mnemonic: string): HDWallet
deriveChild(path: DerivationPath): HDWallet
getPrivateKey(): Uint8Array
getPublicKey(): Uint8Array
}
WalletFacade
The WalletFacade composes the ShieldedWallet, UnshieldedWallet, DustWallet, and HDWallet into a single unified interface. It provides convenience methods that coordinate operations across multiple wallet types.
class WalletFacade {
readonly shieldedWallet: ShieldedWallet<S>;
readonly unshieldedWallet: UnshieldedWallet;
readonly dustWallet: DustWallet;
readonly hdWallet: HDWallet;
}
Creating Wallets
From Seed Phrases
Wallets can be created from BIP39 mnemonic seed phrases. The seed phrase is used to derive the master key which in turn derives the keys for shielded, unshielded, and DUST operations.
const wallet = await WalletFacade.fromMnemonic(
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
);
From Random Generation
For new wallets the SDK can generate a random mnemonic and create the corresponding wallet. This is the standard approach for creating new user wallets.
const randomWallet = await WalletFacade.generateRandom();
const mnemonic = randomWallet.getMnemonic();
From Existing Keys
Wallets can also be created from existing key material such as private keys or extended keys. This is useful for importing wallets from other systems or restoring from a raw key backup.
const wallet = await WalletFacade.fromPrivateKey(privateKeyBytes);
FluentWalletBuilder
The FluentWalletBuilder provides a builder pattern for constructing wallets with specific configurations. It is especially useful in test environments where deterministic wallets are needed.
const wallet = await new FluentWalletBuilder()
.withNetworkId(NetworkId.Preprod)
.withMnemonic(testMnemonic)
.build();
WalletFactory
The WalletFactory provides factory methods for creating specific wallet types without going through the full facade. This is useful when only a subset of wallet functionality is needed.
const shieldedWallet = await WalletFactory.createShieldedWallet({
networkId: NetworkId.Preprod,
privateKey: signingKey
});
const unshieldedWallet = await WalletFactory.createUnshieldedWallet({
networkId: NetworkId.Preprod,
privateKey: signingKey
});
Wallet State Management
WalletSaveStateProvider
The WalletSaveStateProvider handles persistence of wallet state including shielded coin information, DUST state, and key material. It serializes the wallet state to a format suitable for storage and handles deserialization for state restoration.
class WalletSaveStateProvider {
async saveState(wallet: WalletFacade): Promise<SerializedWalletState>
async restoreState(
serializedState: SerializedWalletState,
providers: MidnightProviders
): Promise<WalletFacade>
}
State Serialization
Wallet state is serialized to a structured format that includes all shielded coins, their associated metadata, DUST registration state, and wallet keys. The serialized format is encrypted before storage.
interface SerializedWalletState {
readonly shieldedCoins: SerializedShieldedCoin[];
readonly dustState: SerializedDustState;
readonly keys: SerializedKeys;
readonly version: number;
}
State Restoration
State restoration reconstructs a wallet from serialized state. This involves decrypting the state, deserializing the wallet structure, and reconnecting to the network to verify on chain state.
const serializedState = await fs.readFile('wallet-state.json', 'utf-8');
const wallet = await walletSaveStateProvider.restoreState(
JSON.parse(serializedState),
providers
);
Wallet State Files
Wallet state files contain the serialized wallet state and should be stored securely. They are typically encrypted with a user provided password to protect against unauthorized access.
State files should be backed up regularly since they contain the user's shielded coins. Losing the state file means losing access to shielded coins since their information is not stored on chain.
Unshielded Operations
Sending Unshielded Tokens
Sending unshielded tokens transfers publicly visible NIGHT tokens from one address to another. The transaction creates output UTXOs for the recipient and returns any change to the sender.
const tx = await wallet.unshieldedWallet.sendUnshieldedTokens(
recipientAddress,
amount
);
await wallet.providers.waitForTxFinalization(tx.hash);
Checking Balances
Unshielded balances are queried from the indexer which tracks all unspent transaction outputs. The balance includes all UTXOs belonging to the wallet address.
const balances = await wallet.unshieldedWallet.getUnshieldedBalances();
for (const balance of balances) {
console.log(`Balance: ${balance.amount} NIGHT`);
}
UTXO Management
UTXOs are the building blocks of unshielded transactions. The wallet manages its UTXO set by tracking which outputs have been spent and which are still available. UTXO selection algorithms choose which outputs to spend for a given transaction.
const utxos = await wallet.unshieldedWallet.getUnshieldedUtxos();
const spendableUtxos = utxos.filter(utxo => !utxo.isSpent);
getUnshieldedBalances and getUnshieldedUtxos
These two methods provide fine grained access to the wallet's unshielded state. getUnshieldedBalances returns aggregated balance information while getUnshieldedUtxos returns the individual UTXOs.
const balances = await wallet.unshieldedWallet.getUnshieldedBalances();
const utxos = await wallet.unshieldedWallet.getUnshieldedUtxos();
Shielded Operations
Creating Shielded Coins
Creating shielded coins converts public unshielded tokens into private shielded coins. The shield operation consumes UTXOs as inputs and produces shielded coins as outputs in the Zswap protocol.
const shieldedCoin = await wallet.shieldedWallet.createShieldedCoin(amount);
Spending Shielded Coins
Spending shielded coins converts them back to unshielded tokens or creates new shielded coins with different owners. The spend operation uses zero knowledge proofs to prove ownership without revealing the coin details.
const outputCoins = await wallet.shieldedWallet.spendShieldedCoin(
shieldedCoin,
recipientAddress
);
ZswapLocalState
The ZswapLocalState maintains the wallet's local view of the shielded coin pool. It tracks which coins have been created and spent and synchronizes with the Zswap protocol state on chain.
class ZswapLocalState<S extends ShieldedCoinInfo> {
async addCoin(coin: ShieldedCoinInfo): Promise<void>
async removeCoin(coinId: string): Promise<void>
async getCoins(): Promise<ShieldedCoinInfo[]>
async sync(): Promise<void>
}
Shielded Coin Info
ShieldedCoinInfo contains the metadata for a shielded coin including its value commitment, nullifier, and encryption details. This information is stored locally and never revealed on chain.
Qualified Shielded Coin Info
QualifiedShieldedCoinInfo extends ShieldedCoinInfo with additional verification data that proves the coin is valid and properly formed. This is used when spending coins to demonstrate that they were correctly created.
DUST Operations
DUST Registration
Before generating DUST a wallet must register for DUST generation. Registration associates the wallet with a DUST generation state and proves that the wallet is eligible to receive DUST.
const registration = await wallet.dustWallet.registerDust();
DUST Generation
DUST generation creates new DUST tokens for the wallet. This is an on chain operation that involves proving eligibility through the DUST protocol and receiving newly generated DUST tokens.
const generation = await wallet.dustWallet.generateDust(requestedAmount);
DUST Spend
DUST is spent automatically during transaction submission as fee payment. However the wallet provides manual DUST spend operations for advanced use cases where DUST must be applied to specific transactions.
const tx = await wallet.dustWallet.spendDust(dustAmount, targetTransaction);
DustActions
DustActions is an enum defining the possible operations on DUST state. It includes registration, generation, and spend actions each with their own state transition requirements.
enum DustActions {
Register = 'register',
Generate = 'generate',
Spend = 'spend'
}
DustLocalState and DustState
DustLocalState maintains the wallet's local view of its DUST balance and generation history. DustState represents the on chain DUST state for a wallet address.
interface DustLocalState {
readonly balance: bigint;
readonly generationHistory: DustGeneration[];
readonly registrationStatus: 'unregistered' | 'registered' | 'pending';
}
interface DustState {
readonly totalGenerated: bigint;
readonly totalSpent: bigint;
readonly lastGeneration: number;
}
DustParameters
DustParameters configure the DUST generation behavior including the maximum generation amount per epoch and the generation rate. These parameters are determined by the network and may change through governance.
DustGenerationState
DustGenerationState tracks the progress of an ongoing DUST generation operation. It includes the amount requested, the amount generated so far, and the remaining amount to generate.
Generating DUST Programmatically
Preprod DUST Generation Guide
On the preprod testnet DUST can be generated programmatically through the faucet. The preprod faucet provides small amounts of DUST for testing purposes.
const faucetResponse = await fetch('https://faucet.preprod.midnight.network/dust', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ address: walletAddress })
});
Faucet Client
The faucet client is a convenience wrapper around the faucet HTTP API. It handles request formatting and response parsing for DUST requests.
class FaucetClient {
async requestDust(address: string, amount: bigint): Promise<FaucetResponse>
async checkStatus(requestId: string): Promise<FaucetStatus>
}
Dust Generator
For programmatic DUST generation in test environments the dust generator provides a scriptable interface that can be integrated into test suites and CI pipelines.
async function generateTestDust(wallet: WalletFacade, amount: bigint): Promise<void> {
const registration = await wallet.dustWallet.registerDust();
const generation = await wallet.dustWallet.generateDust(amount);
}
Key Management
Signing Keys
Signing keys are used to authorize transactions and prove ownership of wallet addresses. They are derived from the wallet's HD tree using BIP340 derivation paths.
const signingKey = wallet.hdWallet.deriveChild(DerivationPath.signing()).getPrivateKey();
Coin Secret Keys
Coin secret keys are used for shielded coin operations. They enable the wallet to create and spend shielded coins without revealing the coin details.
const coinSecretKey = wallet.hdWallet.deriveChild(DerivationPath.coinSecret()).getPrivateKey();
Encryption Secret Keys
Encryption secret keys protect the privacy of shielded coin metadata stored locally. They encrypt the local state so that even if the state file is compromised the coin details remain hidden.
const encryptionKey = wallet.hdWallet.deriveChild(DerivationPath.encryption()).getPrivateKey();
BIP340 Derivation
BIP340 defines the derivation scheme for Schnorr signature keys used in Midnight. The derivation path follows the BIP44 structure with Midnight specific coin types.
const bip340Key = wallet.hdWallet.deriveBip340('m/44'/0'/0'/0/0');
Encryption Public Keys
Encryption public keys are used by other parties to encrypt data that only the wallet owner can decrypt. These keys are safe to share publicly.
const encryptionPublicKey = await wallet.keyMaterialProvider.getEncryptionPublicKey();
Transaction Signing
signData
The signData method creates a cryptographic signature over arbitrary data using the wallet's signing key. The signature can be verified by any party with the corresponding public key.
const signature = await wallet.keyMaterialProvider.signData(dataToSign);
verifySignature
The verifySignature method checks whether a signature is valid for the given data and public key. It returns true if the signature is authentic.
const isValid = await wallet.keyMaterialProvider.verifySignature(
data,
signature,
publicKey
);
signatureVerifyingKey
The signature verifying key is the public key counterpart to the signing key. It is used by verifiers to check signatures produced by the wallet.
const verifyingKey = wallet.keyMaterialProvider.signatureVerifyingKey();
signingKeyFromBip340
This utility function extracts the Schnorr signing key from a BIP340 derivation path. It converts the HD derivation output into a signing key suitable for signature generation.
const signingKey = signingKeyFromBip340(wallet.hdWallet, DerivationPath.signing());
Wallet Provider Interface
WalletProvider Type
The WalletProvider type defines the interface that all wallet implementations must satisfy. It includes methods for balance queries, token transfers, and key material access.
type WalletProvider = {
readonly getBalances: () => Promise<Balance[]>;
readonly getAddress: () => string;
readonly getNetworkId: () => NetworkId;
readonly keyMaterialProvider: KeyMaterialProvider;
};
MidnightWalletProvider
The MidnightWalletProvider is the concrete implementation of the WalletProvider interface that wraps the WalletFacade and exposes wallet operations in a provider compatible format.
class MidnightWalletProvider implements WalletProvider {
constructor(private readonly wallet: WalletFacade) {}
async getBalances(): Promise<Balance[]> {
return this.wallet.getBalances();
}
}
Wallet CLI and MCP Integration
Community Wallet CLI
The community wallet CLI provides command line access to wallet operations. It supports creating wallets, checking balances, sending tokens, and managing DUST.
npx @midnight-ntwrk/wallet-cli create --network preprod
npx @midnight-ntwrk/wallet-cli balance --address <address>
npx @midnight-ntwrk/wallet-cli send --to <address> --amount 100
npx @midnight-ntwrk/wallet-cli dust-generate --amount 50
MCP Server for AI Agent Wallet Operations
The wallet MCP server exposes wallet operations as MCP tools that AI agents can call. This enables AI agents to interact with the Midnight Network programmatically.
The MCP server provides tools for creating wallets, querying balances, sending transactions, and checking transaction status. Each tool maps to a corresponding WalletFacade method.
const walletMcpServer = new WalletMcpServer({
wallet,
allowedOperations: ['balance', 'send', 'dust']
});
DApp Connector Integration
walletConnectedAPI
The walletConnectedAPI is obtained through the DApp Connector after successful authorization. It provides wallet operations through the browser wallet extension.
const connectedApi = await initialApi.requestAuthorization();
const walletApi = connectedApi.walletConnectedAPI;
Connect Wallet
Connecting through the DApp Connector establishes a channel to the browser wallet. The connect method returns an initial API that requires authorization before it can be used for operations.
const midnight = window.midnight;
const initialApi = await midnight.connect();
Disconnect Wallet
Disconnecting closes the channel to the browser wallet and releases any held resources. The wallet remains available for future connections.
await connectedApi.disconnect();
Request Authorization
Authorization prompts the user to approve the DApp's access to the wallet. The user can review the requested permissions and network before approving.
const connectedApi = await initialApi.requestAuthorization({
networkId: NetworkId.Preprod
});
Test Wallet Patterns
FluentWalletBuilder for Tests
Test environments use FluentWalletBuilder to create deterministic wallets with known keys and balances. This ensures reproducible test outcomes.
const testWallet = await new FluentWalletBuilder()
.withNetworkId(NetworkId.Undeployed)
.withMnemonic(testMnemonic)
.build();
Test Mnemonics
The Midnight test kit provides standard test mnemonics that can be used across test suites. These mnemonics correspond to well known wallet addresses with predictable behavior.
const TEST_MNEMONIC = 'test test test test test test test test test test test junk';
Wallet Seeds
Wallet seeds provide an alternative to mnemonics for deterministic wallet creation. Seeds are raw byte arrays from which the HD tree is derived.
const seed = new Uint8Array(64).fill(0x01);
const wallet = await HDWallet.fromSeed(seed);
Deterministic Wallets
Deterministic wallets produce the same keys and addresses given the same seed or mnemonic. This is essential for testing where predictable wallet behavior is needed.
Wallet Export and Import
Export Private States
Private states can be exported from a wallet for backup or transfer to another device. The export includes shielded coin information and DUST state.
const exportedState = await wallet.exportPrivateStates();
await fs.writeFile('wallet-backup.json', JSON.stringify(exportedState));
Import Private States
Imported states restore the wallet's private state from a previously exported backup. The import must use the same encryption password used during export.
const exportedState = JSON.parse(await fs.readFile('wallet-backup.json', 'utf-8'));
await wallet.importPrivateStates(exportedState);
Export Signing Keys
Signing keys can be exported individually for backup or transfer. The exported keys are in a standard format that can be imported by other wallet implementations.
const exportedSigningKey = await wallet.exportSigningKey();
Import Signing Keys
Imported signing keys restore the wallet's signing capability from a previously exported key. The import verifies that the key is valid before accepting it.
await wallet.importSigningKey(exportedSigningKey);
Export and Import Formats
Wallet export formats use structured JSON with version markers and checksums. Import validation verifies the format version and checksum integrity before accepting the data.
Wallet Encryption
StorageEncryption
StorageEncryption provides the encryption layer for wallet state persistence. It supports multiple encryption algorithms and key derivation functions.
class StorageEncryption {
static async encrypt(data: Uint8Array, password: string): Promise<EncryptedData>
static async decrypt(encryptedData: EncryptedData, password: string): Promise<Uint8Array>
}
Password Rotation
Password rotation changes the encryption password without requiring a full state re-export. The wallet re-encrypts all stored data with the new password.
await wallet.rotateEncryptionPassword('old-password', 'new-password');
Crypto Backends
Multiple crypto backends are supported for wallet encryption including the Midnight default backend and platform specific implementations. Backends can be selected based on performance and security requirements.
decryptValue
Individual encrypted values can be decrypted using the wallet's encryption key. This is used for on demand access to encrypted state entries.
const value = await wallet.decryptValue(encryptedData);
Wallet Compatibility
CAIP-372
CAIP-372 defines the namespace and reference format for Midnight wallet addresses and chain identifiers. Wallets implement CAIP-372 for interoperability with cross chain tools and explorers.
The Midnight chain identifier follows the format midnight:<network> where network is one of mainnet, preprod, preview, or undeployed. Wallet addresses use the CAIP-10 account identifier format within this namespace.
Community Wallets
Several community wallets support the Midnight Network providing users with different options for managing tokens.
Lace wallet provides Midnight support through its multi chain extension with built in proving and DApp Connector integration.
The 1AM wallet is purpose built for Midnight with native support for shielded transactions and DUST management.
Kuira wallet offers Midnight compatibility through its modular extension architecture supporting both shielded and unshielded operations.
Fireblocks provides institutional grade wallet infrastructure with Midnight support for enterprise custodial workflows.
Best Practices
Key Security
Never store wallet mnemonics or private keys in plaintext. Always use encrypted storage with strong passwords. The mnemonic is the root of all wallet keys and its compromise means total loss of funds.
Use hardware security modules or secure enclaves for production wallet key storage. Development and testing should use separate wallets with no real value.
Rotate encryption passwords periodically especially if there is suspicion of compromise. Password rotation does not affect the underlying wallet keys only the encryption of the local state.
State Backup
Backup wallet state files regularly since shielded coin information exists only in the local state. Without a state backup shielded coins become permanently inaccessible even though they still exist in the Zswap protocol.
Store backups in multiple secure locations. Consider using encrypted cloud storage with additional access controls. Test restore procedures periodically to ensure backups are valid.
Password Management
Use strong unique passwords for wallet encryption. Consider using a password manager to generate and store passwords securely.
Never reuse wallet encryption passwords across different wallets. A compromise of one wallet should not affect others.
Network Switching
Always verify the network ID before performing operations. Sending tokens intended for preprod to a mainnet address results in permanent loss.
DApps should display the current network prominently to users and warn before operations on mainnet.
Error Recovery
Implement retry logic for transient failures such as network timeouts and proof server unavailability. Use exponential backoff to avoid overwhelming services.
Handle permanent failures gracefully by presenting clear error messages to users. Log detailed error information for debugging while avoiding exposure of sensitive key material in logs.
Cross Reference Skills
This skill covers wallet SDK and DUST operations. For related areas see these skills.
- midnight-concepts — DUST tokenomics, Zswap protocol, shielded nature
- midnight-compact — Token contracts, shielded transfers, coin operations
- midnight-api — Wallet APIs, integration patterns, error handling
- midnight-network — DUST generation on devnet, faucet operations
- midnight-dapp-dev — Wallet connect UI, provider setup, transaction flow
- midnight-expert — Wallet health checks, key material verification
Provide recovery paths for common error scenarios such as insufficient DUST, invalid recipient addresses, and network congestion. Educate users about error causes and remediation steps.