Midnight providers are modular, pluggable components that each handle a specific capability required for transaction construction and submission to the Midnight blockchain. The provider packages include functionality for proof generation, private state management, public data queries, and transaction balancing and submission. In plain terms, they're the toolset that powers Midnight.js's architecture.
There are seven provider slots in total. Five of them — covering proof generation, private state, and public data — live in the Midnight.js API. The remaining two — for transaction balancing and submission — live in the Wallet SDK.
The five providers in Midnight.js
midnight-js-indexer-public-data-provider: This is a GraphQL-based blockchain data provider that offers query and subscription operations against public blockchain data.midnight-js-level-private-state-provider: This provides AES-256-GCM encrypted persistent state storage via LevelDB.midnight-js-http-client-proof-provider: This provides an HTTP client for the Midnight proof server.midnight-js-fetch-zk-config-provider: This provides a browser-compatible zero-knowledge artifact provider, using the Fetch API to retrieve the ZK configuration (proving key, verifier key, and ZKIR) for the proof provider.midnight-js-node-zk-config-provider: This is a Node.js filesystem-based zero-knowledge artifact provider. This can be used in place of the fetch-zk-config-provider when running in a Node.js environment rather than a browser.
There's also a variant worth knowing about: midnight-js-dapp-connector-proof-provider, a proof provider that delegates proof generation to the DApp Connector wallet instead of talking to an HTTP proof server — an alternative to httpClientProofProvider depending on your setup.
The two providers in the Wallet SDK
-
walletProvider: Handles transaction balancing and signing. -
midnightProvider: Handles transaction submission to the network.
Both of these are supplied by the Wallet SDK (@midnight-ntwrk/wallet-sdk-facade), not by Midnight.js itself. In practice, a single wallet class can implement both slots at once.
Putting it together
const providers: MidnightProviders = {
privateStateProvider: levelPrivateStateProvider({
privateStoragePasswordProvider: () => password,
accountId: walletAddress,
}),
publicDataProvider: indexerPublicDataProvider(queryUrl, subscriptionUrl),
zkConfigProvider,
proofProvider: httpClientProofProvider(proofServerUrl, zkConfigProvider),
walletProvider, // from @midnight-ntwrk/wallet-sdk-facade
midnightProvider, // from @midnight-ntwrk/wallet-sdk-facade
};
There's also an optional eighth slot, loggerProvider, for diagnostics logging — not required, but available if you want structured logs across the provider layer.
Why this separation matters
Splitting providers across Midnight.js and the Wallet SDK isn't arbitrary. Proof generation, private state, and public data queries are concerns internal to how a dApp talks to the chain — they don't need to know anything about a specific wallet implementation. Balancing and submitting transactions, on the other hand, are inherently wallet concerns: they touch keys, signing, and account state. Keeping that boundary clean means you can swap out a wallet implementation without touching your proof or state logic, and vice versa.
That's the provider layer in a nutshell — seven required pieces, one optional logger, split cleanly between "how the dApp computes and stores" and "how the wallet signs and submits." Once you've wired these up correctly, the rest of Midnight.js — contract deployment, circuit calls — just builds on top.
If you're setting this up yourself, the official MidnightProviders interface in midnight-js-types is the fastest way to confirm you've got every slot filled correctly.
Top comments (0)