> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploying and calling Midnight contracts

A Midnight contract call is a **zero-knowledge proof**, not a signed
transaction. So an embedded wallet cannot simply "sign the contract call" — the
work splits in two:

| Half           | Who does it                 | What it covers                                                                   |
| :------------- | :-------------------------- | :------------------------------------------------------------------------------- |
| **Contract**   | Your app, via `midnight-js` | Circuit execution, witness handling, ZK proof, private state                     |
| **Settlement** | The Dynamic wallet          | Adding coin inputs so the transaction balances, paying DUST, signing, submitting |

`primaryWallet.getWalletProvider()` returns the settlement half as a
`midnight-js`–compatible provider. Balancing and MPC signing happen inside the
wallet iframe, so shielded, DUST and MPC key material never leaves it — only
public keys and serialized transactions cross the boundary.

<Note>
  This is for **embedded** Midnight wallets. Injected wallets (1am) expose their
  own provider surface — see
  [Using Midnight wallets](/docs/react/wallets/using-wallets/midnight/using-midnight-wallets).
</Note>

## What the wallet gives you

```tsx React theme={"system"}
// Parameterise with the transaction types from your own installed midnight-js
// and ledger versions, and the result is assignable to `MidnightProviders`
// with no cast. The connector deliberately declares these structurally, so it
// pins no midnight-js version on your app.
const walletProvider = await primaryWallet.getWalletProvider<
  UnboundTransaction,
  FinalizedTransaction
>();

walletProvider.getCoinPublicKey();       // shielded coin public key
walletProvider.getEncryptionPublicKey(); // shielded encryption public key
walletProvider.balanceTx(tx, ttl);       // balance + pay DUST + sign, in the iframe
walletProvider.submitTx(tx);             // broadcast
```

That satisfies both `walletProvider` and `midnightProvider` in
`MidnightProviders`. The remaining four are yours.

| Provider               | Supplied by | Notes                                    |
| :--------------------- | :---------- | :--------------------------------------- |
| `walletProvider`       | **Dynamic** | `getWalletProvider()`                    |
| `midnightProvider`     | **Dynamic** | the same object                          |
| `privateStateProvider` | you         | your contract's witnesses                |
| `publicDataProvider`   | you         | indexer; required to read contract state |
| `zkConfigProvider`     | you         | your compiled ZK artefacts               |
| `proofProvider`        | you         | a Midnight proof server                  |

## Prerequisites

<Steps>
  <Step title="Fund the wallet and register DUST">
    Contract calls cost DUST. Fund the wallet with unshielded NIGHT, call
    `registerDust()`, then wait for a non-zero DUST balance — see
    [Registering for DUST](/docs/react/wallets/using-wallets/midnight/midnight-embedded-wallets#registering-for-dust).
  </Step>

  <Step title="Compile your contract">
    `compact compile` produces the ZK artefacts your `zkConfigProvider` serves:
    `zkir/<circuit>.bzkir`, `keys/<circuit>.prover`, `keys/<circuit>.verifier`.
    These are deterministic, public build outputs — not secrets.

    <Warning>
      Prover keys are large: expect single-digit to tens of megabytes per
      circuit. They are uploaded to the proof server on every proof, so keep
      them served from somewhere fast.
    </Warning>
  </Step>

  <Step title="Point at a proof server">
    Your `proofProvider` needs a Midnight proof server for **your** circuits.
    The wallet proves its own shielded and DUST work separately, inside the
    iframe.
  </Step>
</Steps>

## Deploying and calling

```tsx React theme={"system"}
import { CompiledContract } from '@midnight-ntwrk/compact-js';
import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider';
import { httpClientProofProvider } from '@midnight-ntwrk/midnight-js-http-client-proof-provider';
import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';
import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider';
import {
  deployContract,
  findDeployedContract,
} from '@midnight-ntwrk/midnight-js/contracts';
import { setNetworkId } from '@midnight-ntwrk/midnight-js/network-id';

import { Contract } from './my-contract/index.js'; // compact compile output

const PRIVATE_STATE_ID = 'my-contract-state';

setNetworkId('preview');

// Absolute URL: FetchZkConfigProvider runs `new URL(...)` on this and rejects
// anything without an http/https scheme.
const zkArtifactBaseUrl = `${window.location.origin}/my-contract`;

const compiledContract = CompiledContract.make('my-contract', Contract)
  .withWitnesses({
    // your witness implementations
  })
  .withCompiledFileAssets(zkArtifactBaseUrl);

const walletProvider = await primaryWallet.getWalletProvider();
const accountId = walletProvider.getCoinPublicKey();

const zkConfigProvider = new FetchZkConfigProvider(
  zkArtifactBaseUrl,
  // Pass the browser's fetch explicitly — the default is `cross-fetch`, which
  // a bundler may resolve to its Node build and fail with an opaque
  // ZKConfigurationReadError. See the notes below for the content-type guard.
  window.fetch.bind(window),
);

const providers = {
  privateStateProvider: levelPrivateStateProvider({
    accountId,
    privateStateStoreName: PRIVATE_STATE_ID,
  }),
  publicDataProvider: indexerPublicDataProvider(indexerHttpUrl, indexerWsUrl),
  zkConfigProvider,
  proofProvider: httpClientProofProvider(proofServerUrl, zkConfigProvider),
  walletProvider,
  midnightProvider: walletProvider,
};

// Deploy
const deployed = await deployContract(providers, {
  compiledContract,
  privateStateId: PRIVATE_STATE_ID,
  initialPrivateState: { /* your witnesses' backing state */ },
});
const { contractAddress } = deployed.deployTxData.public;

// Call a circuit on an already-deployed instance
const found = await findDeployedContract(providers, {
  compiledContract,
  contractAddress,
  privateStateId: PRIVATE_STATE_ID,
});
await found.callTx.myCircuit(...args);
```

<Note>
  Both calls go through `balanceTx` and `submitTx` on the wallet provider, so the
  user's shielded coins and DUST pay for the transaction and the unshielded
  segment is MPC-signed — without your app ever holding key material.
</Note>

## Things that will catch you out

These are not obvious from the `midnight-js` types, and each one fails in a way
that points somewhere unhelpful.

<AccordionGroup>
  <Accordion title="Your zkConfigProvider must fail for built-in circuits">
    A circuit that creates a shielded coin needs prover keys for Midnight's
    own `midnight/zswap/output`, `input` and `spend` circuits. Those are **not**
    in your compiled artefacts — the proof server has them.

    The preimage marks proving data as optional, so your provider should
    **throw** for circuit ids it does not own. `midnight-js` then omits proving
    data and the server uses its own keys. If your provider returns something
    for those ids instead, it ends up in the preimage and the proof server
    rejects the request with a bare `400`.
  </Accordion>

  <Accordion title="Validate the content type of artefact responses">
    `createProverKey` does not inspect what you hand it. A dev server's
    single-page-app fallback answers unknown paths with **200 and `index.html`**,
    so a wrong artefact path becomes HTML embedded in your proof preimage — and
    the only symptom is a bare `400` from the proof server.

    Reject HTML in the custom `fetch` and a bad path becomes an obvious error:

    ```ts theme={"system"}
    const guardedFetch = async (input, init) => {
      const response = await window.fetch(input, init);
      const contentType = response.headers.get('content-type') ?? '';
      if (response.ok && contentType.includes('text/html')) {
        throw new Error(`ZK artefact request returned HTML: ${String(input)}`);
      }
      return response;
    };
    ```

    Throwing here is also what makes the built-in zswap circuits work, since
    those requests 404 into the same fallback.
  </Accordion>

  <Accordion title="Deploy needs no proof; circuit calls do">
    Deploying publishes state and verifier keys — there is no circuit execution
    to prove, so a deploy can succeed with no proof server reachable. The first
    `callTx` is where `proofProvider` is actually used, which is why a
    misconfigured proof server often looks like "deploy worked, calls broke".
  </Accordion>

  <Accordion title="The first call is slow">
    `getWalletProvider()` initialises the wallet inside the iframe, which
    includes a full sync on a cold cache. Expect well over a minute on first
    use, with no intermediate progress. Later calls reuse the synced wallet.
  </Accordion>

  <Accordion title="Private state is yours to keep">
    Witnesses live in your app's private state, never in the wallet. If they are
    lost, gated circuits on that contract can no longer be called by anyone —
    the wallet cannot help recover them, because it never held them. Persist
    anything you cannot regenerate.
  </Accordion>

  <Accordion title="Newly minted shielded coins take time to appear">
    A shielded coin exists on chain as soon as the transaction is included, but
    the wallet has to sync the Zswap tree before it shows in balances. A zero
    balance straight after a successful mint usually means sync, not failure.
  </Accordion>
</AccordionGroup>

## Confirming a transaction landed

`submitTx` returns the **submission identifier**, which is what inclusion is
polled by. It is a different value from the canonical transaction hash that
block explorers index — querying an indexer by the wrong one returns an empty
result rather than an error, which reads like a failed transaction.

```graphql theme={"system"}
query ($id: HexEncoded!) {
  transactions(offset: { identifier: $id }) {
    hash
    block { height }
    ... on RegularTransaction {
      transactionResult { status }
      contractActions { __typename address }
    }
  }
}
```

`contractActions` returns `ContractDeploy` or `ContractCall` with the contract
address — the simplest way to confirm a deploy and recover its address.

## Resources

* [Using Midnight embedded wallets](/docs/react/wallets/using-wallets/midnight/midnight-embedded-wallets)
* [Using Midnight wallets (injected 1am extension)](/docs/react/wallets/using-wallets/midnight/using-midnight-wallets)
* [Midnight developer docs](https://docs.midnight.network/)
