diff --git a/.vale.ini b/.vale.ini index 7f3944d43..0e100dbf9 100644 --- a/.vale.ini +++ b/.vale.ini @@ -13,4 +13,4 @@ IgnoredPaths = **/_* # Markdown and MDX [*.{md,mdx}] BasedOnStyles = Midnight -TokenIgnores = (?s), (?s)\{/\*.*?\*/\} \ No newline at end of file +TokenIgnores = (?s), (?s)\{/\*.*?\*/\}, (\{#[-\w]+\}) \ No newline at end of file diff --git a/docs/examples/_bboard/local-testing.mdx b/docs/examples/_bboard/local-testing.mdx index 71b8fbae4..51b700ce7 100644 --- a/docs/examples/_bboard/local-testing.mdx +++ b/docs/examples/_bboard/local-testing.mdx @@ -82,7 +82,7 @@ cleaned up automatically. When you are ready to try out your DApp on the Midnight Testnet, be sure you have not left it running and that your standalone Docker configuration has exited. Then, -[start your proof server](../../guides/run-proof-server.mdx) again. When it is +[start your proof server](../../guides/development-environment.mdx#run-the-proof-server) again. When it is ready, start the DApp with this command: ```shell @@ -90,7 +90,7 @@ yarn testnet-remote ``` This time, you will need real tDUST, so either create a new wallet and -transfer some tDUST to it from your [Lace wallet](../../guides/lace-wallet.mdx), +transfer some tDUST to it from your [Lace wallet](/getting-started/installation), or look back at the logs from your experiments with the counter DApp in part 2 to find the seed for your headless wallet: diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index 8aca8f6d3..5e30644ee 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -121,7 +121,7 @@ export circuit storeMessage(customMessage: Opaque<"string">): [] { [Compact](../compact/) is Midnight's smart contract language used to define contract logic. It is similar to TypeScript but is designed for use with the Midnight runtime. -The `docker-compose.yml` file defines a local devnet, a Midnight node, indexer, and [proof server](../guides/run-proof-server) that runs in Docker. The proof server generates Zero Knowledge (ZK) proofs for smart contracts on the Midnight network. The devnet must be running before you can deploy or interact with the contract. +The `docker-compose.yml` file defines a local devnet, a Midnight node, indexer, and [proof server](../guides/development-environment#run-the-proof-server) that runs in Docker. The proof server generates Zero Knowledge (ZK) proofs for smart contracts on the Midnight network. The devnet must be running before you can deploy or interact with the contract. ## Set up the project diff --git a/docs/guides/_category_.yaml b/docs/guides/_category_.yaml index 1f42e094e..81580e047 100644 --- a/docs/guides/_category_.yaml +++ b/docs/guides/_category_.yaml @@ -3,6 +3,5 @@ label: 'Guides' collapsible: true # make the category collapsible collapsed: true # keep the category closed by default link: - type: generated-index - title: Guides - description: Step-by-step guides for building on Midnight Network. \ No newline at end of file + type: doc + id: guides/index diff --git a/docs/guides/_lace-wallet.mdx b/docs/guides/_lace-wallet.mdx deleted file mode 100644 index 70ba21024..000000000 --- a/docs/guides/_lace-wallet.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -SPDX-License-Identifier: Apache-2.0 -copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -sidebar_label: "Set up the Lace wallet" -description: Learn how to set up the Lace wallet for the Midnight Preview network. -toc_max_heading_level: 2 -sidebar_position: 10 ---- - -import Step, { StepsProvider } from '@site/src/components/Step/Step'; - -# Lace wallet - -The Lace wallet is a Chrome extension. This guide is aimed at Testnet DApp users who are setting up the Lace wallet for the first time. - -:::important Chrome browser - -You must use the Chrome web browser or its derivatives to complete -web-based transactions on the Midnight testnet. - -Only the Chrome browser itself is fully supported. The Lace -wallet may not be able to connect to your local proof server from -Chrome derivatives. If you choose to use Brave, for example, it is -necessary to disable Brave shields when running the Hello World DApp, so that the DApp (hosted at one address) can contact -your local proof server (hosted on your system, at a different -address) through the Lace extension. - -::: - -## Install the Lace Midnight Preview wallet - -This section walks you through the process of installing the Lace Midnight Preview wallet, creating a new wallet, and getting test tokens. - -### Download and install the Lace wallet extension - -Lace Midnight Preview wallet is a browser extension for the Midnight Network. -It is currently only compatible with Google Chrome. Here's how to install it: - - - Open the Google Chrome browser. - Install the Midnight Lace wallet extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/lace-beta/hgeekaiplokcnmakghbdfbgnlfheichg). - Click **Add to Chrome** and confirm by clicking **Add extension**. - Pin the extension to your toolbar for easy access (recommended). - - -**Verification**: The Lace wallet icon appears in your Chrome toolbar. - -### Create your wallet - -To create a new wallet, follow these steps: - - - Click the Lace wallet icon in your browser's toolbar. - Select the **Create** button to create a new wallet. - Copy the seed phrase to your clipboard, and then click **Next** to continue. - ![Seed phrase](./img/copy-to-clipboard.png) - - :::warning - Write down your seed phrase on paper and store it in a secure, offline location. Never store it digitally or share it. - ::: - - Paste the seed phrase into the confirmation field, and then click **Confirm** to complete the setup. - Enter your wallet name, choose a strong password, and click **Next** to continue. - - ![Wallet name](./img/wallet-name-password.png) - - Choose your network and proof server address. - - ![Enter wallet](./img/configure-midnight.png) - - Click **Enter wallet** to complete the setup. - - -**Verification**: Your wallet dashboard opens, showing your shielded and unshielded wallet addresses. - -### Get test tokens (tDUST) - -tDUST is the network resource used to process transactions and interact with DApps on the Midnight Network. -It has no real-world value and is used for testing transactions and interacting with DApps. - - - In your Lace wallet, click **Receive**, and then copy your Unshielded wallet address. - ![Unshielded wallet address](./img/copy-unshielded-address.png) - Go to the Preprod Faucet: https://faucet.preprod.midnight.network/. - Paste your address into the form and click **Request tokens**. Wait a few minutes for the tokens to arrive. - Once the tokens are received, you must delegate them to your wallet to generate tDUST. Click **Generate tDUST** and select your wallet address. - ![tDUST balance](../guides/img/delegate-dust.png) - Click **Review transaction** to proceed, and then select **Confirm** to submit the transaction. - ![Generate tDUST](../guides/img/review-transaction.png) - Once the transaction is confirmed, you'll see your tDUST balance in your Lace wallet. - - -**Verification**: Your Lace wallet shows a new balance of tDUST tokens. - -## Next Steps - -At this point, the Lace wallet displays a balance of tokens for interacting with testnet DApps. In order to process transactions, Lace requires a proof server to be running on your machine. Follow the [proof server guide](./run-proof-server) to get it up and running. - diff --git a/docs/guides/acquire-tokens.mdx b/docs/guides/acquire-tokens.mdx index 9142fca88..7a353dd61 100644 --- a/docs/guides/acquire-tokens.mdx +++ b/docs/guides/acquire-tokens.mdx @@ -1,31 +1,32 @@ --- SPDX-License-Identifier: Apache-2.0 copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -sidebar_label: "Get faucet tokens" -description: Learn how to acquire test tokens for the Midnight Network. +title: Fund your wallet +sidebar_label: "Fund your wallet" +description: Get tNIGHT from the faucet and delegate it to generate the tDUST that pays for transactions on the Midnight test networks. toc_max_heading_level: 2 -sidebar_position: 15 +sidebar_position: 2 --- -# Get faucet tokens +# Fund your wallet -In the production Midnight network, to run a DApp you need +With your [development environment set up](./development-environment), the next thing you need is tokens. In the production Midnight network, to run a DApp you need to hold some DUST in a persistent wallet and spend some of it on each transaction. However, a supply of free tDUST is available for developers who want to experiment with Midnight DApps. :::tip -If you are using the local undeployed network, then use the [local network faucet](./midnight-local-network#fund-wallets) to fund your wallet and generate tDUST. +If you are using the local undeployed network, then use the [local network faucet](./local-to-testnet#fund-wallets) to fund your wallet and generate tDUST. ::: -## Get tNIGHT +## Get tNIGHT {#get-tnight} tNIGHT is the test token for the Midnight network. It is used to generate tDUST for testing purposes. Here are the steps to get tNIGHT: 1. Log in to your Lace Midnight wallet and copy your Unshielded wallet address. :::note Bech32m address format - All wallet addresses are shown in Bech32m format by default. The faucet supports both formats, however the legacy (HEX) format will be dropped in future versions. + All wallet addresses are shown in Bech32m format by default. The faucet also accepts the legacy (HEX) format, but prefer Bech32m. ::: 2. Visit [Midnight Preprod faucet](https://midnight-tmnight-preprod.nethermind.dev/) and enter your Unshielded wallet address. @@ -40,7 +41,7 @@ Transaction submitted. You will receive 1000 tNight, this should take about 2 mi You should have received 1000.0 tNIGHT from the faucet. You can verify the balance in your wallet. -## Delegate tDUST +## Delegate tDUST {#delegate-tdust} After you have received tNIGHT, you need to delegate it to generate tDUST. Here's how to do it if you are using the Lace wallet: @@ -56,6 +57,10 @@ You should see your tDUST tank generating test tokens for your wallet. ![Confirm transaction](./img/dust-tank-generation.png) +:::tip +If your application creates wallets from code, you can also [generate DUST programmatically](./generating-dust-programmatically) with the Wallet SDK instead of using the Lace UI. +::: + ## Troubleshooting Below are some common issues you might encounter when using the faucet and how to resolve them. @@ -68,6 +73,10 @@ Below are some common issues you might encounter when using the faucet and how t ### Rate limit exceeded -- **Error**: `{“status”:“rate_limit_error”,“message”:“Reached maximum number of requests for this address”}` +- **Error**: `{"status":"rate_limit_error","message":"Reached maximum number of requests for this address"}` + +- **Solution**: Wait a few hours and try again. If the issue persists, then open a ticket at the [Midnight Service Desk](https://midnightntwrk.github.io/servicedesk/) or ask for help in the [Midnight Discord server](https://discord.com/invite/midnightnetwork). + +## Next steps {#next-steps} -- **Solution**: Wait a few hours and try again. If the issue persists, then open a ticket at the [Midnight Service Desk](https://midnightntwrk.github.io/servicedesk/) or ask for help in the [Midnight Discord server](https://discord.gg/midnightnetwork). \ No newline at end of file +With tNIGHT in your wallet and tDUST generating, you are ready to build and deploy: continue with [from local network to testnet](./local-to-testnet). \ No newline at end of file diff --git a/docs/guides/configure-providers.mdx b/docs/guides/configure-providers.mdx deleted file mode 100644 index 6cef9623a..000000000 --- a/docs/guides/configure-providers.mdx +++ /dev/null @@ -1,340 +0,0 @@ ---- -SPDX-License-Identifier: Apache-2.0 -copyright: This file is part of Midnight Docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -sidebar_label: How to configure providers -title: How to configure providers -description: "Learn how to configure the MidnightProviders object for deploying and interacting with Compact smart contracts." -sidebar_position: 22 -toc_max_heading_level: 2 -keywords: [midnight, javascript, providers, configure Midnight providers] -tags: [midnight, javascript, wallet] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# How to configure providers - -Providers are the modular components that the Midnight.js packages use to interact with the Midnight Network. - -This guide covers how to configure the Midnight.js providers before deploying or interacting with a Compact smart contract. - -## Prerequisites - -To follow along with this guide, ensure you have: - -- A compiled Compact smart contract with the `keys/` and `zkir/` directories generated. If you haven't done so yet, then follow the [build your first contract](../getting-started/hello-world) tutorial to get started. -- Node.js version 22.x or higher installed. Install it using [NVM](https://github.com/nvm-sh/nvm). -- Docker installed and running. This is required to run the [proof server](./run-proof-server) and generate zero-knowledge (ZK) proofs. - -## The `MidnightProviders` type - -`MidnightProviders` is a generic type imported from `@midnight-ntwrk/midnight-js-types`. It accepts three arguments that are specific to your contract: - -```typescript -import { type MidnightProviders } from '@midnight-ntwrk/midnight-js-types'; - -// CircuitKeys — union of circuit names from your compiled contract -// PrivateStateId — literal type of your private state storage key -// PrivateState — shape of your contract's private state object -type MyProviders = MidnightProviders; -``` - -A `common-types.ts` file is a good place to keep these aliases, especially if your API and UI packages share the same contract types: - -```typescript -import { type MidnightProviders } from '@midnight-ntwrk/midnight-js-types'; -import { type FoundContract } from '@midnight-ntwrk/midnight-js-contracts'; - -export const myPrivateStateKey = 'myPrivateState'; -export type PrivateStateId = typeof myPrivateStateKey; - -export type MyCircuitKeys = 'circuitA' | 'circuitB'; -export type MyProviders = MidnightProviders; -``` - -## Set the network ID - -Call `setNetworkId` before initializing any providers. All Midnight.js packages read this value to target the correct network. - -```typescript -import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id'; - -setNetworkId('preprod'); - -/** Supported network IDs: 'mainnet', 'preview', 'preprod', 'undeployed' */ -``` - -In Node.js environments, also polyfill `WebSocket` so that GraphQL subscriptions to the indexer work: - -```typescript -import { WebSocket } from 'ws'; - -globalThis.WebSocket = WebSocket as unknown as typeof globalThis.WebSocket; -``` - -## Configure the providers - -Each provider handles one specific capability in the transaction pipeline: - -- Storing private state -- Querying the indexer -- Generating ZK proofs -- Balancing transactions -- Submitting transactions on-chain - -The following sections cover how to configure each provider. - -### `privateStateProvider` - -The private state provider stores and retrieves the contract's private state on the local device. Private state is never sent to the network. - -Use `levelPrivateStateProvider` from `@midnight-ntwrk/midnight-js-level-private-state-provider`. It persists private state to a LevelDB database encrypted with AES-256-GCM. - -```typescript -import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider'; - -const privateStateProvider = levelPrivateStateProvider({ - privateStateStoreName: 'my-contract-private-state', - signingKeyStoreName: 'my-contract-private-state-signing-keys', - privateStoragePasswordProvider: () => 'your-encryption-password', -}); -``` - -The table below describes the parameters for the `levelPrivateStateProvider` function. - -| Parameter | Description | -|-----------|-------------| -| `privateStateStoreName` | Name of the LevelDB store for private state | -| `signingKeyStoreName` | Name of the LevelDB store for signing keys | -| `privateStoragePasswordProvider` | Function returning the encryption password | - -:::warning Password security - -Do not use a hardcoded password in production. Derive it from wallet credentials or a secure key management system. - -::: - -### `publicDataProvider` - -The public data provider queries and subscribes to on-chain contract state via the Midnight indexer's GraphQL API. Use `indexerPublicDataProvider` from `@midnight-ntwrk/midnight-js-indexer-public-data-provider` in both Node.js and browser environments. - -```typescript -import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider'; - -const publicDataProvider = indexerPublicDataProvider( - 'https://indexer.preprod.midnight.network/api/v4/graphql', // HTTP query URL - 'wss://indexer.preprod.midnight.network/api/v4/graphql/ws', // WebSocket subscription URL -); -``` - -For local development, use: - -```typescript -const publicDataProvider = indexerPublicDataProvider( - 'http://localhost:8088/api/v4/graphql', - 'ws://localhost:8088/api/v4/graphql/ws', -); -``` - -:::info Local development - -Midnight provides a local network for development and testing purposes. Use the local network to test your contract and providers before deploying to the main network. -For more information, see the [Midnight local network](./midnight-local-network) guide. - -::: - -### `zkConfigProvider` - -The ZK configuration provider supplies the prover key, verifier key, and ZKIR artifacts that the proof provider needs to generate zero-knowledge proofs. The right implementation depends on where you store your ZK artifacts. - - - - -If you are running your contract in a Node.js environment, then use `NodeZkConfigProvider` from `@midnight-ntwrk/midnight-js-node-zk-config-provider`. It reads artifacts from the local filesystem. The path should point to the directory containing the compiled contract's `keys/` and `zkir/` output. - -```typescript -import { NodeZkConfigProvider } from '@midnight-ntwrk/midnight-js-node-zk-config-provider'; - -const zkConfigProvider = new NodeZkConfigProvider<'circuitA' | 'circuitB'>( - '/path/to/contract/src/managed/my-contract', -); -``` - -The type parameter is the union of circuit names your contract exposes. This is typically the same type as your `CircuitKeys` alias. - - - - -If your ZK artifacts are hosted on a remote server, then you can use `FetchZkConfigProvider` -from the `@midnight-ntwrk/midnight-js-fetch-zk-config-provider` package. It fetches artifacts over HTTP from a URL. - -```typescript -import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider'; - -const zkConfigProvider = new FetchZkConfigProvider<'circuitA' | 'circuitB'>('https://example.com/zk-artifacts'); -``` - - - - -### `proofProvider` - -The proof provider calls the Midnight proof server to generate zero-knowledge proofs from unproven transactions. Use `httpClientProofProvider` from `@midnight-ntwrk/midnight-js-http-client-proof-provider`. - -It takes the proof server URL and the `zkConfigProvider` instance. The proof server needs access to the same ZK artifacts, which it retrieves via the `zkConfigProvider`. - -```typescript -import { httpClientProofProvider } from '@midnight-ntwrk/midnight-js-http-client-proof-provider'; - -const proofProvider = httpClientProofProvider( - 'http://localhost:6300', // proof server URL - zkConfigProvider, -); -``` - -:::info Proof server - -The proof server is a Docker container you run locally or point to a hosted instance. See the [proof server guide](./run-proof-server) for setup instructions. - -::: - -### `walletProvider` - -The wallet provider exposes the public keys needed to receive shielded tokens and decrypts transaction data. It also balances unbound transactions by selecting UTXOs to cover fees and adding change outputs. - -In a Node.js CLI, you can implement `WalletProvider` using the [Wallet SDK](/sdks/official/wallet-developer-guide) facade. - -```typescript -import { - type CoinPublicKey, - type EncPublicKey, - type FinalizedTransaction, - ZswapSecretKeys, - DustSecretKey, -} from '@midnight-ntwrk/ledger-v8'; -import { type WalletProvider, UnboundTransaction } from '@midnight-ntwrk/midnight-js-types'; -import { ttlOneHour } from '@midnight-ntwrk/midnight-js-utils'; -import { type WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade'; - -class MyWalletProvider implements WalletProvider { - constructor( - private readonly wallet: WalletFacade, - private readonly zswapSecretKeys: ZswapSecretKeys, - private readonly dustSecretKey: DustSecretKey, - ) {} - - getCoinPublicKey(): CoinPublicKey { - return this.zswapSecretKeys.coinPublicKey; - } - - getEncryptionPublicKey(): EncPublicKey { - return this.zswapSecretKeys.encryptionPublicKey; - } - - async balanceTx(tx: UnboundTransaction, ttl: Date = ttlOneHour()): Promise { - const recipe = await this.wallet.balanceUnboundTransaction( - tx, - { shieldedSecretKeys: this.zswapSecretKeys, dustSecretKey: this.dustSecretKey }, - { ttl }, - ); - return await this.wallet.finalizeRecipe(recipe); - } -} -``` - -The class takes a `WalletFacade` instance together with the `ZswapSecretKeys` and `DustSecretKey` derived from the wallet seed. It exposes three methods that the SDK calls during the transaction lifecycle: - -- `getCoinPublicKey`: Returns the shielded address used to receive tokens. -- `getEncryptionPublicKey`: Returns the key used to decrypt incoming shielded transaction data. -- `balanceTx`: Selects UTXOs to cover fees, adds change outputs, and finalizes the transaction ready for proof generation and submission. - -:::warning DUST requirements - -DUST is the network resource that fuels transactions on the Midnight Network. You must have DUST in your wallet to pay for transaction fees. For more information, see the [Generating DUST programmatically](./generating-dust-programmatically) guide. - -::: - -The same class that implements `WalletProvider` can also implement `MidnightProvider`, since the `WalletFacade` exposes both capabilities. - -```typescript -import { type MidnightProvider } from '@midnight-ntwrk/midnight-js-types'; -import { type FinalizedTransaction } from '@midnight-ntwrk/ledger-v8'; - -class MyWalletProvider implements WalletProvider, MidnightProvider { - // ...other methods... - - submitTx(tx: FinalizedTransaction): Promise { - return this.wallet.submitTransaction(tx); - } -} -``` - -A single instance is then passed as both `walletProvider` and `midnightProvider` in the providers object. - -## Assemble the providers object - -Once each provider is initialized, assemble them into the `MidnightProviders` object and pass it to your smart contract API. - -```typescript -import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider'; -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'; - -const zkConfigProvider = new NodeZkConfigProvider('/path/to/contract/managed/my-contract'); - -const walletProvider = new MyWalletProvider(wallet, zswapSecretKeys, dustSecretKey); - -const providers: MyProviders = { - privateStateProvider: levelPrivateStateProvider({ - privateStateStoreName: 'my-contract-private-state', - signingKeyStoreName: 'my-contract-private-state-signing-keys', - privateStoragePasswordProvider: () => 'your-encryption-password', - }), - publicDataProvider: indexerPublicDataProvider( - 'https://indexer.preprod.midnight.network/api/v4/graphql', - 'wss://indexer.preprod.midnight.network/api/v4/graphql/ws', - ), - zkConfigProvider, - proofProvider: httpClientProofProvider('http://localhost:6300', zkConfigProvider), - walletProvider, - midnightProvider: walletProvider, -}; -``` - -Pass this object to `deployContract` or `findDeployedContract`: - -```typescript -import { deployContract, findDeployedContract } from '@midnight-ntwrk/midnight-js-contracts'; - -// Deploy a new contract -const deployed = await deployContract(providers, { - compiledContract: CompiledMyContract, - privateStateId: myPrivateStateKey, - initialPrivateState: myInitialPrivateState, -}); - -// Or connect to an existing one -const deployed = await findDeployedContract(providers, { - contractAddress, - compiledContract: CompiledMyContract, - privateStateId: myPrivateStateKey, - initialPrivateState: myInitialPrivateState, -}); -``` - -The `deployContract` and `findDeployedContract` functions take the `MidnightProviders` object and the contract details as parameters. - -:::note Note - -For more information on using these functions, see the [Midnight.js SDK](/sdks/official/midnight-js) documentation. - -::: - -## Next steps - -With your providers configured, you are ready to deploy and interact with smart contracts on the Midnight Network. The following resources cover the next steps in that workflow: - -- [Deploy your first contract](./deploy-mn-app) -- [Leaderboard tutorial](../tutorials/leaderboard/overview) \ No newline at end of file diff --git a/docs/guides/connect-wallet-frontend.mdx b/docs/guides/connect-wallet-frontend.mdx new file mode 100644 index 000000000..9e67c273b --- /dev/null +++ b/docs/guides/connect-wallet-frontend.mdx @@ -0,0 +1,621 @@ +--- +SPDX-License-Identifier: Apache-2.0 +copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +title: Connect a wallet from your frontend +description: Build a wallet connection flow with the Midnight DApp Connector API in a Vite + React or Next.js application, from discovery to a connected address. +sidebar_label: "Connect a wallet from your frontend" +sidebar_position: 6 +tags: [wallet, react, nextjs, dapp-connector, tutorial] +keywords: [midnight, wallet, dapp connector, react, nextjs, connect wallet] +toc_max_heading_level: 2 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import Step, { StepsProvider } from "@site/src/components/Step/Step"; + +# Connect a wallet from your frontend + +With a contract deployed, the next step is giving users a way to interact with it, and that starts with connecting their wallet. In this guide, you build a frontend that connects to a Midnight wallet using the DApp Connector API: an interface that displays connection status and wallet addresses, providing a foundation for more complex decentralized applications. + +The same connection flow works in any frontend framework. This guide covers two popular setups side by side: pick the **Vite + React** tab for a plain single-page app, or the **Next.js** tab if you use server-side rendering and the App Router. + +The code examples focus on core functionality and intentionally omit CSS styling. You can add your preferred styling solution (Tailwind, styled-components, CSS modules, etc.) to match your application's design. + +## Prerequisites {#prerequisites} + +Before you begin, make sure you have: + +- Basic knowledge of TypeScript and JavaScript +- Familiarity with React fundamentals (components, state, hooks) +- Node.js and npm installed on your system +- A Midnight wallet extension, such as the [Midnight Lace wallet](https://chromewebstore.google.com/detail/lace/gafhhkghbfjjkeiendhlofajokpaflmk), installed in your browser + +:::note +This guide uses the DApp Connector API v4.0.1. For more information, see the [DApp Connector API documentation](/api-reference/dapp-connector). +::: + +## Build the wallet connection flow {#build-the-wallet-connection-flow} + + + + +If you don't have a React project yet, create one using Vite: + +```bash +npm create vite@latest my-wallet-app -- --template react-ts +cd my-wallet-app +``` + +Then install the DApp Connector API package: + +```bash +npm install @midnight-ntwrk/dapp-connector-api +``` + +After completing this tutorial, you'll understand: + +- The Midnight wallet connection flow +- How to use the DApp Connector API +- Best practices for managing wallet state in React applications + + + + +### Define TypeScript interfaces {#define-typescript-interfaces} + +Start by creating type definitions for your components. These interfaces provide type safety and clear contracts between components. + +Create a new file `types.ts` within the `src` directory and add the following code: + +```typescript +export interface WalletCardProps { + isConnected: boolean; + walletAddress: string | null; + onConnect: () => void; + onDisconnect: () => void; +} +``` + +The `WalletCardProps` interface defines the shape of data your `WalletCard` component will receive. +It includes the connection state, the wallet address, and callback functions for connect and disconnect actions. + + + + +### Create the WalletCard component {#create-the-walletcard-component} + +Now you'll build the `WalletCard` component, which displays the wallet connection status and provides connect/disconnect buttons. + +Create `WalletCard.tsx` within the `src` directory and add the following code: + +```typescript +import React from "react"; +import type { WalletCardProps } from "./types"; + +const WalletCard: React.FC = ({ + isConnected, + walletAddress, + onConnect, + onDisconnect, +}) => { + return ( +
+
+

Connection Status

+
+ {isConnected ? "Connected" : "Disconnected"} +
+
+ +
+ {isConnected && walletAddress ? ( + <> +

Wallet Address:

+

{walletAddress}

+ + ) : ( +

Please connect your wallet to proceed.

+ )} +
+ +
+ {isConnected ? ( + + ) : ( + + )} +
+
+ ); +}; + +export default WalletCard; +``` + +This component handles the presentation layer of your wallet connection. It shows the current status, +displays the address when connected, and provides appropriate action buttons based on the connection state. + +
+ + +### Select a wallet {#select-a-wallet} + +Wallets inject their Initial API instances under the global `window.midnight` object, each keyed by a freshly generated UUID rather than a fixed name. A single browser may also have more than one wallet available at once, for example when two wallets are loaded from the same seed phrase. Because of this, you read the entries on `window.midnight` and choose one, rather than reaching for a hardcoded key. + +Create a new file `selectWallet.ts` within the `src` directory and add the following code: + +```typescript +import type { InitialAPI } from '@midnight-ntwrk/dapp-connector-api'; + +export const listWallets = (): InitialAPI[] => { + const injected = window.midnight; + return injected ? Object.values(injected) : []; +}; + +export const selectWallet = (): InitialAPI => { + const wallets = listWallets(); + + if (wallets.length === 0) { + throw new Error('No Midnight wallet found. Please install a Midnight wallet extension.'); + } + + return wallets[0]; +}; +``` + +The `selectWallet` function reads the installed wallets and returns the first one, which keeps this example focused on the connection flow. When more than one wallet is available, the DApp Connector API specification expects you to let the user choose rather than picking for them. Use `listWallets` to render the options, and when displaying each wallet, render its `name` and `icon` safely to prevent XSS. + + + + +### Integrate the DApp Connector API {#integrate-the-dapp-connector-api} + +Next, we'll use the `App` component to manage the wallet connection logic using the DApp Connector API and the `selectWallet` helper from the previous step. + +Create or open `App.tsx` within the `src` directory and replace the existing code with the following: + +```typescript App.tsx +import React, { useState } from 'react'; +import WalletCard from './WalletCard'; +import '@midnight-ntwrk/dapp-connector-api'; +import { selectWallet } from './selectWallet'; + +const App: React.FC = () => { + const [isConnected, setIsConnected] = useState(false); + const [walletAddress, setWalletAddress] = useState(null); + + const handleConnect = async () => { + console.log('Connect button clicked'); + let isConnected = false; + let address = null; + + try { + const wallet = selectWallet(); + + // Connect to the specified network (use 'undeployed' for local development) + const connectedApi = await wallet.connect('preprod'); + + // Retrieve the unshielded address from the wallet + const { unshieldedAddress } = await connectedApi.getUnshieldedAddress(); + address = unshieldedAddress; + + // Optional: Get the service URI configuration + const serviceUriConfig = await connectedApi.getConfiguration(); + console.log('Service URI Config:', serviceUriConfig); + + // Check if the connection is established + const connectionStatus = await connectedApi.getConnectionStatus(); + if (connectionStatus.status === 'connected') { + isConnected = true; + console.log("Connected to the wallet:", address); + } + } catch (error) { + console.log("An error occurred:", error); + } + + setIsConnected(isConnected); + setWalletAddress(address); + }; + + const handleDisconnect = () => { + setWalletAddress(null); + setIsConnected(false); + }; + + return ( +
+
+

Midnight Wallet Connector

+
+
+ +
+
+ ); +}; + +export default App; +``` + +Let's break down the wallet connection process: + +1. **Select a wallet**: The `selectWallet` helper reads the installed wallets from `window.midnight` and returns one to connect to. It throws if no wallet is found, which the surrounding `try/catch` handles. +2. **Connect to network**: Call the `connect()` method and pass the network ID as an argument. +In our example, we used `'preprod'`. You can use `'undeployed'` for local development or `'preview'` for the Preview network. +3. **Retrieve the address**: After connecting to the network, call the `getUnshieldedAddress()` method to get the wallet's unshielded address. Request the shielded address only when your application actually needs it. +4. **Check status**: Verify the connection with `getConnectionStatus()`, which resolves to an object whose `status` is `'connected'` or `'disconnected'`. + +The `handleConnect` event handler manages all these steps and updates your component's state accordingly. +When users click the **Connect Wallet** button, their wallet will prompt them to authorize the connection. + +
+ + +### Set up the entry point {#set-up-the-entry-point} + +Create the entry point that bootstraps your React application. For this, create or open `main.tsx` within the `src` directory +and replace the existing code with the following: + +```typescript +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App.tsx'; + +createRoot(document.getElementById('root')!).render( + + + , +); +``` + +Finally, create or open `index.html` in the project root and replace the existing code with the following: + +```html + + + + + + Midnight Wallet Connector + + +
+ + + +``` + +
+ + +### Run your application {#run-your-application} + +After setting up the entry point, start your development server: + +```bash +npm run dev +``` + +Now, open your browser and navigate to the local development URL (typically `http://localhost:5173`). + +When you click **Connect Wallet**, your wallet extension will prompt you to authorize the connection. + +![Connect Wallet](./img/connect-wallet.png) + +After approval, your application will display the connection status and your wallet's unshielded address. + +![Connected Wallet](./img/connected-wallet.png) + + +
+ +
+ + +If you don't have a Next.js project yet, create one using the following command: + +```bash +npm create-next-app@latest +``` + +When prompted, select the following options: +- **TypeScript**: Yes +- **ESLint**: Yes +- **Tailwind CSS**: Yes (optional, but recommended) +- **App Router**: Yes +- **Other options**: Choose based on your preference + +Then navigate to the project directory and install the DApp Connector API package: + +```bash +cd +npm install @midnight-ntwrk/dapp-connector-api +``` + +After completing this tutorial, you'll understand: + +- How to integrate wallet connections in Next.js applications +- The differences between client and server components when working with wallets +- How to manage wallet state in Next.js +- Best practices for using the DApp Connector API in Next.js + + + + +### Create the wallet selection helper {#nextjs-create-the-wallet-selection-helper} + +Wallets inject their Initial API instances under the global `window.midnight` object, each keyed by a freshly generated UUID rather than a fixed name such as `mnLace`. A single browser may also have more than one wallet available at once. Because of this, you read the entries on `window.midnight` and choose one, rather than reaching for a hardcoded key. + +Create `app/components/selectWallet.ts`: + +```typescript +import type { InitialAPI } from '@midnight-ntwrk/dapp-connector-api'; + +export const listWallets = (): InitialAPI[] => { + const injected = window.midnight; + return injected ? Object.values(injected) : []; +}; + +export const selectWallet = (): InitialAPI => { + const wallets = listWallets(); + + if (wallets.length === 0) { + throw new Error('No Midnight wallet found. Please install a Midnight wallet extension.'); + } + + return wallets[0]; +}; +``` + +The `selectWallet` function reads the installed wallets and returns the first one, which keeps this example focused on the connection flow. When more than one wallet is available, the DApp Connector API specification expects you to let the user choose rather than picking for them. Use `listWallets` to render the options, and when displaying each wallet, render its `name` and `icon` safely to prevent XSS. + + + + +### Create the wallet connection component {#create-the-wallet-connection-component} + +You'll build a client-side component that handles wallet connection. Since wallet interactions require browser APIs, this component must run on the client side using Next.js's `"use client"` directive. + +Create `app/components/ConnectWalletButton.tsx`: + +```typescript +"use client"; // Next.js directive for client-side rendering + +import { useState } from "react"; +import "@midnight-ntwrk/dapp-connector-api"; +import { selectWallet } from "./selectWallet"; + +export default function ConnectWalletButton() { + const [connected, setConnected] = useState(false); + const [walletAddress, setWalletAddress] = useState(null); + + const handleConnect = async () => { + try { + // Select an installed Midnight wallet from window.midnight + const wallet = selectWallet(); + + // Connect to the specified network (use 'undeployed' for local development) + const connectedApi = await wallet.connect('preprod'); + + // Retrieve the shielded addresses from the wallet + const addresses = await connectedApi.getShieldedAddresses(); + const address = addresses.shieldedAddress; + + // Check if the connection is established + const connectionStatus = await connectedApi.getConnectionStatus(); + + if (connectionStatus.status === 'connected') { + setConnected(true); + setWalletAddress(address); + console.log("Connected to wallet:", address); + } + } catch (error) { + console.log("Failed to connect:", error); + } + }; + + const handleDisconnect = () => { + setConnected(false); + setWalletAddress(null); + }; + + return ( + + ); +} +``` + +This component manages the wallet connection flow: + +1. **Client-side rendering**: The `"use client"` directive ensures this component runs in the browser where wallet APIs are available. +2. **State management**: Uses React's `useState` hook to track connection status and wallet address. +3. **Connection logic**: The `handleConnect` function selects an installed wallet with the `selectWallet` helper, connects to the specified network, and retrieves the wallet's shielded address. +4. **User feedback**: Displays the wallet address (truncated) and provides connect/disconnect actions. + + + + +### Add the component to your layout {#add-the-component-to-your-layout} + +Now integrate the wallet button into your application's layout so it appears on every page. + +Update `app/layout.tsx`: + +```typescript +import type { Metadata } from "next"; +import "./globals.css"; +import ConnectWalletButton from "./components/ConnectWalletButton"; + +export const metadata: Metadata = { + title: "Midnight Wallet Connector", + description: "Connect to a Midnight wallet", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + +
+ {children} +
+ + + ); +} +``` + +The `ConnectWalletButton` component now appears at the top of every page in your application. Next.js's layout system makes it easy to create persistent UI elements across routes. + +
+ + +### Create a welcome page {#create-a-welcome-page} + +Create a simple landing page that encourages users to connect their wallet. + +For this, replace the content of `app/page.tsx` with the following: + +```typescript +export default function Home() { + return ( +
+

+ Welcome to Midnight +

+

+ Connect your Lace wallet to get started with privacy-preserving + decentralized applications on the Midnight Network. +

+
+

+ Click the "Connect Wallet" button in the top right corner to authorize + this application to access your Midnight Lace wallet. +

+
+
+ ); +} +``` + +
+ + +### Run your application {#nextjs-run-your-application} + +Start the Next.js development server: + +```bash +npm run dev +``` + +Open your browser and navigate to `http://localhost:3000`. + +When you click **Connect Wallet**, the Midnight Lace wallet extension prompts you to authorize the connection. + +![Connect Wallet](./img/connect-wallet-next.png) + +The wallet asks you to choose your preferred authorization level: + +- **Always**: Grants persistent authorization. The application remains authorized even after closing your browser, and you won't need to reconnect on future visits. +- **Only once**: Grants temporary authorization. You must reauthorize the connection each time you visit the application. + +![Authorize Connection](./img/dapp-connection-option.png) + +After approval, the button changes to "Disconnect" and displays your truncated wallet address. + +![Connected Wallet](./img/connected-wallet-next.png) + + + + +### Verify the connection {#verify-the-connection} + +You can verify that your wallet is connected to the application: + +1. Open the Midnight Lace wallet extension in your browser. +2. Click on your wallet name in the top right corner, then select **Settings**. +3. Navigate to **Authorized DApps**. + +You should see `http://localhost:3000` listed as an authorized application. + +![Authorized DApps](./img/authorized-dapps.png) + +You can revoke access at any time from this panel by clicking the **trash** icon next to the application. + + +
+ +
+
+ +## Troubleshooting {#troubleshooting} + +The following are some common issues you might encounter and how to resolve them. + +### "window is not defined" error (Next.js only) + +This error occurs because Next.js tries to render components on the server by default, but wallet APIs only exist in the browser environment. + +**How to fix it**: Make sure your wallet component includes the `"use client"` directive at the top of the file. + +### Wallet not detected + +If you see errors about `window.midnight` being undefined, or `No Midnight wallet found`: + +- Check that your browser has a Midnight wallet extension installed and enabled. +- Refresh the page after installing or enabling the extension. +- Confirm you are reading the wallet from `window.midnight` by enumeration (`Object.values(window.midnight)`) and not from a fixed key such as `window.midnight.mnLace`. Wallets inject their Initial API under a UUID key, so a hardcoded name resolves to `undefined`. +- Check the browser console for any extension-related errors. +- In Next.js, ensure you're testing in a browser, not during server-side rendering. + +### Connection fails + +If the connection attempt fails: + +- Ensure the network ID specified in the `connect()` method matches the network ID configured in your wallet. For local development, use `'undeployed'`. For the Preprod environment, use `'preprod'`. +- Make sure you have unlocked and synced the wallet. +- Review the browser console for specific error messages. +- Verify the DApp Connector API package is correctly installed. + +## Next steps {#next-steps} + +Now that you have a working wallet connector, you can extend your application with additional functionality: + +- **Transfer coins**: Implement a form that allows users to send tokens to other addresses. +- **Sign messages**: Add a text input where users can sign arbitrary messages with their wallet. +- **Display balances**: Show token balances for different assets. +- **Transaction history**: Query and display past transactions. +- **Multi-network support**: Add a network selector to switch between different Midnight networks. +- **Create protected routes** (Next.js): Use Next.js middleware to restrict access to pages that require wallet connection. + +## Reference {#reference} + +- [DApp Connector API documentation](/api-reference/dapp-connector) +- [Next.js documentation](https://nextjs.org/docs) +- [Midnight Lace wallet](https://chromewebstore.google.com/detail/lace/gafhhkghbfjjkeiendhlofajokpaflmk) diff --git a/docs/guides/deploy-mn-app.mdx b/docs/guides/deploy-mn-app.mdx deleted file mode 100644 index 08d293fc7..000000000 --- a/docs/guides/deploy-mn-app.mdx +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: Deploy the hello world contract -description: Deploy a compiled hello world smart contract to Midnight's Preprod network. -sidebar_position: 25 -sidebar_label: Deploy the hello world contract -tags: [deployment, smart-contract, JavaScript] -toc_max_heading_level: 2 ---- - -import Step, { StepsProvider } from "@site/src/components/Step/Step"; - -# Deploy the hello world contract - -The [Hello World tutorial](../getting-started/hello-world) deploys the contract to a Docker-based local devnet with pre-funded wallets. In this guide, you deploy the same `example-hello-world` contract to **Preprod**, the public Midnight testnet used for final testing before mainnet. After deployment, the contract is visible on the Preprod block explorer and reachable from any Preprod indexer. - -## Prerequisites - -Before you begin, ensure you have: - -- Completed the [Hello World tutorial](../getting-started/hello-world) against the local devnet (`yarn test:local` passing). -- A clone of [`example-hello-world`](https://github.com/midnightntwrk/example-hello-world) with the contract already compiled (`contracts/managed/hello-world/` populated). -- A Midnight-compatible wallet (such as Lace or 1AM) configured for the Preprod network, or an existing 24-word mnemonic / 64-character hex seed you control. -- Docker engine running (the proof server runs as a container). - - - - -## Generate a wallet - -You need a wallet on the Preprod network to sign the deployment and call transactions. The test suite accepts either a 24-word BIP-39 mnemonic or a 64-character hex seed; pick whichever your wallet exports. - -**Using a Midnight-compatible wallet** (such as Lace or 1AM): -1. In your wallet, switch the network to **Preprod** and create a new wallet. Save the seed phrase securely. -2. Note your **Unshielded** address. You will paste it into the faucet in the next step. - -Paste this seed phrase into `.env.preprod` in step 3. - -**Bring your own key**: Any 24-word BIP-39 mnemonic or 64-hex-character seed works. The test suite derives both the shielded and unshielded keys from it. - - - - -## Fund the wallet with tNIGHT and tDUST - -Preprod transactions are paid in **tDUST**, which is generated by holding (and delegating) **tNIGHT**. Funding is a two-stage process: - -1. **Get tNIGHT from the faucet**: Open the [Preprod faucet](https://midnight-tmnight-preprod.nethermind.dev/), paste your Unshielded address, and click **Request tokens**. The faucet sends 1000 tNIGHT within a couple of minutes. - -2. **Delegate to start generating tDUST**: Use your wallet's delegation flow to delegate your tNIGHT and begin generating spendable tDUST. The exact label varies by wallet. Without tDUST the test script fails with `Wallet.InsufficientFunds`. - -For faucet troubleshooting, see [Get faucet tokens](./acquire-tokens). - -:::tip Verify balances before continuing -Confirm both tNIGHT and tDUST balances in your wallet before running the test. Wait until the tDUST balance is non-zero. DUST generation begins only after the network confirms the delegation transaction on-chain. -::: - - - - -## Configure `.env.preprod` - -The repo ships with an example file. From the repository root: - -```bash -cp .env.preprod.example .env.preprod -``` - -Open `.env.preprod` and **set only one** of the two variables. Delete the other line entirely. Defining both raises an error. - -```bash -# Use this line if your wallet exports a 24-word phrase: -MIDNIGHT_PREPROD_MNEMONIC=word1 word2 word3 ... word24 - -# Or this line if you have a raw seed (hex, no 0x prefix): -MIDNIGHT_PREPROD_SEED=abcd1234... # 64 hex characters -``` - -:::warning Keep `.env.preprod` private -The file contains the secret that controls your Preprod wallet. It is already listed in `.gitignore`; do not commit it, share it, or paste it into chat. -::: - - - - -## Start the proof server - -The proof server generates the zero-knowledge proofs the test submits to the network. On Preprod you only need the proof server itself, not the rest of the local devnet stack (the test connects to the public Preprod endpoints, so no local node or indexer is required). - -In a separate terminal, from the project root: - -```bash -yarn proof:up -``` - -This starts only the `proof-server` service from `compose.yml` and waits for it to become healthy on `http://127.0.0.1:6300`. - - - - -## Run the test against Preprod - -Back in your main terminal: - -```bash -yarn test:preprod -``` - -The script: - -1. Build a wallet from the secret in `.env.preprod`. -2. Sync the wallet against the Preprod indexer. This can take a long time on first run (the default sync timeout is 60 minutes for remote networks, versus 10 for local). The log emits one line per state emission so you can watch progress. -3. Verify funds are available and register the wallet for DUST generation if not already registered. -4. Deploy the `hello-world` contract and submit a call to `storeMessage("Hello World!")`. - -A successful run ends with output similar to: - -```bash -INFO: Wallet sync complete after 23 emissions -INFO: Wallet NIGHT balance on 'preprod': ... -INFO: Providers initialized on 'preprod'. Ready to test! -INFO: Creating private state... -INFO: Setting the contract address... -INFO: Contract deployed at: bba6579743ae23b44301d4a9f8df30dbd5244d63a59d8fbc2c9fc7ea521a04f8 - ✓ src/test/hw.test.ts (2 tests) - ✓ Hello World Contract (preprod) > Deploys the contract - ✓ Hello World Contract (preprod) > Stores Hello World! -``` - - - - -## Verify on the block explorer - -Copy the contract address from the log line `Contract deployed at: ...` and look it up on a Preprod explorer: - -- [preprod.midnightexplorer.com](https://preprod.midnightexplorer.com/) -- [midnight-preprod.subscan.io](https://midnight-preprod.subscan.io/) - -The explorer shows the deploy transaction and the subsequent call transaction. - - - - -## Shut down - -When you're done, stop the proof server: - -```bash -yarn proof:down -``` - -The container exits but its image stays cached for the next run. - - - - -## Troubleshooting - -**`Wallet.InsufficientFunds`**: The wallet has no spendable tDUST. Confirm in your wallet that the tDUST balance is non-zero (not just tNIGHT). DUST generation only starts after the delegation transaction is confirmed. - -**Sync never completes**: Preprod sync from a brand-new wallet can be slow. If you hit the default 60-minute timeout, raise it with the `MIDNIGHT_SYNC_TIMEOUT_MS` environment variable: - -```bash -MIDNIGHT_SYNC_TIMEOUT_MS=7200000 yarn test:preprod # 2 hours -``` - -**`Set only one of MIDNIGHT_PREPROD_MNEMONIC or MIDNIGHT_PREPROD_SEED`**: You defined both variables in `.env.preprod`. Delete one. - -**Proof server unreachable**: Check that `yarn proof:up` finished and `http://127.0.0.1:6300` responds. If port 6300 is in use, stop the conflicting process or change the host port in `compose.yml`. - -## Deploy to Preview - -The repository supports Preview with the same pattern: - -1. Copy `.env.preview.example` to `.env.preview` and fill in `MIDNIGHT_PREVIEW_MNEMONIC` or `MIDNIGHT_PREVIEW_SEED`. -2. Fund the wallet from the [Preview faucet](https://midnight-tmnight-preview.nethermind.dev/) and delegate for tDUST. -3. Run `yarn test:preview`. - -See [Environments and endpoints](../relnotes/network) for the full list of network URLs and explorers. diff --git a/docs/guides/development-environment.mdx b/docs/guides/development-environment.mdx new file mode 100644 index 000000000..50725275f --- /dev/null +++ b/docs/guides/development-environment.mdx @@ -0,0 +1,638 @@ +--- +SPDX-License-Identifier: Apache-2.0 +copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +title: Set up your Midnight development environment +description: Install Docker, the Compact compiler, the VS Code extension, and a local proof server on macOS, Linux, or Windows (WSL), with optional Bun setup. +sidebar_label: "Set up your environment" +sidebar_position: 1 +tags: [installation, compact, proof-server, setup, bun, wsl] +keywords: [midnight, development environment, compact compiler, proof server, docker, wsl, bun] +toc_max_heading_level: 3 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Set up your Midnight development environment + +Every Midnight project relies on the same small toolkit: Docker to run infrastructure such as the proof server, the Compact compiler to build smart contracts, and an editor that understands the Compact language. This guide installs all of them once, so the rest of the guides in this section can focus on building and deploying. + +The toolchain runs natively on macOS and Linux. On Windows, you develop inside the Windows Subsystem for Linux (WSL), and this guide covers that path too. + +## Prerequisites + +- Basic knowledge of JavaScript/TypeScript +- Familiarity with command-line interfaces + +## Set up your platform {#set-up-your-platform} + + + + +Download and install [Docker Desktop](https://www.docker.com/products/docker-desktop/) for your operating system. New users might need to set up an account. + +Docker hosts completed code in images. Midnight components such as the proof server are distributed as Docker images, so Docker must be running before you start them. + + + + +### Install Ubuntu on WSL {#install-ubuntu} + +Developing DApps for the Midnight network often requires compiling and running components (like the proof server or complex scripts) that are traditionally Linux-based. The Windows Subsystem for Linux (WSL) allows you to run a native Linux environment directly within Windows without the overhead of a traditional virtual machine. + +1. Open the Start Menu, search for Windows PowerShell (or Command Prompt), right-click the result, and select **Run as administrator**. This elevation is necessary to install system-level features like WSL. + +2. In the command terminal, enter the following installation command: + +```bash +wsl --install -d ubuntu +``` + +This command enables the required Windows features (Virtual Machine Platform and Windows Subsystem for Linux), downloads and installs the recommended Ubuntu distribution, and restarts the necessary services. + +:::info +The initial download and installation of the required components and the Ubuntu image can take anywhere from 5 to 15 minutes, depending on your internet connection and system speed. Do not close the window until the process is complete. +::: + +3. After the installation completes, a new Ubuntu terminal window opens automatically and prompts you to create a UNIX username and a password. + +:::note +When entering your password, the characters are not displayed on the screen for security reasons (this is standard Linux terminal behavior). Type your desired password carefully and press Enter. +::: + +Once configured, your new username appears as part of the terminal prompt (for example, `yourusername@DESKTOP-XXXXXX:~$`). This confirms that WSL is correctly installed and the Ubuntu distribution is ready for use. + +4. To ensure WSL is running the correct version, execute this command in Windows PowerShell: + +```bash +wsl -l -v +``` + +You should see a list showing Ubuntu with STATE Running (or Stopped) and VERSION 2. WSL 2 is required for optimal performance and Docker integration. + +### Install and configure Docker Desktop {#install-and-configure-docker-desktop} + +1. Navigate to the official Docker website at [docker.com/products/docker-desktop](https://www.docker.com/products/docker-desktop/) and download **Docker Desktop for Windows**. Ensure you download the installer that matches your CPU architecture (for example, **Windows - AMD64** for modern systems). + +2. Run the downloaded installer (for example, `Docker Desktop Installer.exe`). During the installation, make sure the box for **Use WSL 2 instead of Hyper-V** is checked. This is vital for Docker to integrate seamlessly with the Linux environment you just set up. + +3. Follow the prompts to complete the installation. A system restart may be required. After the reboot, launch **Docker Desktop** from your Start Menu. It takes a few moments to start up, showing the whale icon in your system tray. + +4. Once Docker Desktop is running, open the **Settings** menu (the gear icon), click **Resources**, then navigate to the **WSL integration** tab. Ensure that **Enable integration with my default WSL distro** is toggled **ON**, and verify that your **Ubuntu** distribution is enabled. This connection allows Docker to manage containers directly from your Linux terminal. + +:::note +Run all remaining commands in this guide inside your Ubuntu (WSL) terminal, not in PowerShell. +::: + + + + +## Install the Compact compiler {#install-the-compact-compiler} + +[Compact](/compact) is Midnight's smart contract language. The Compact developer tools manage the compiler toolchain for you and keep it up to date. + +1. Run the installer script (on Windows, inside your Ubuntu terminal): + +```bash +curl --proto '=https' --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh +``` + +The installer places the `compact` CLI in `$HOME/.local/bin` and automatically updates your shell's `PATH`. The compiler toolchain versions that `compact update` downloads later live under `$HOME/.compact/`. + +2. To apply the `PATH` changes to your current session, reload your shell configuration: + +```bash +source ~/.bashrc +``` + +3. Verify the installation by checking the version: + +```bash +compact --version +``` + +4. Download the latest compiler toolchain: + +```bash +compact update +``` + +5. Confirm the installation and check that the toolchain is current: + +```bash +compact check +``` + +If the installation succeeded, you should see output similar to the following: + +```bash +compact: aarch64-darwin -- Up to date -- 0.31.1 +``` + +See the [Compact developer tools reference](/compact/compilation-and-tooling/dev-tool-usage) for the full set of subcommands, and the [compatibility matrix](../relnotes/support-matrix) for the compiler version that matches the rest of the toolchain. + +## Install the Compact VS Code extension {#install-the-compact-vs-code-extension} + +The Compact VS Code extension provides syntax highlighting and real-time error checking, which is essential for writing smart contracts. + +1. Download the Compact VS Code extension VSIX package from the [releases page](https://raw.githubusercontent.com/midnight-ntwrk/releases/gh-pages/artifacts/vscode-extension/compact-0.2.13/compact-0.2.13.vsix). +2. Open VS Code. +3. Go to the **Extensions** view (press `Ctrl+Shift+X`), click the **...** (More Actions) menu at the top-right of the extensions pane, and select **Install from VSIX...**. +4. Select the `.vsix` file you just downloaded to complete the installation. + +## Run the proof server {#run-the-proof-server} + +Midnight uses zero-knowledge (ZK) cryptography to enable shielded transactions and data protection. An essential element of this architecture is ZK functionality provided by a Midnight *proof server*, which generates proofs locally for the network to verify on-chain. + +The information that a DApp sends to the proof server includes private data, such as details of token ownership or a DApp's private state. To protect your data, you should access only a local proof server, or perhaps one on a remote machine that you control, over an encrypted channel. + +Your wallet communicates with the proof server to invoke ZK functionality and generate ZK proofs for your transactions, so the proof server must be running whenever you process transactions on the Midnight network. + + + + +Start the proof server with Docker: + +```bash +docker run -p 6300:6300 midnightntwrk/proof-server:latest midnight-proof-server -v +``` + +You should see output similar to: + +``` +actix_server::server: starting service: "actix-web-service-0.0.0.0:6300", workers: 14, listening on: 0.0.0.0:6300 +``` + +:::tip +Keep this terminal window open. The proof server must stay active to compile and deploy contracts. To stop it, press `Ctrl+C` or stop the container. +::: + +If you need a specific proof server version instead of `latest`, check the [compatibility matrix](../relnotes/support-matrix) for the version that matches your node and SDK. + + + + +1. Inside Docker Desktop, use the search bar to locate `midnightntwrk/proof-server:latest`. +2. Pull the image. +3. Start the proof server by clicking the **Run** button in the same search result, or by navigating to your Containers and clicking the **Run** button there. + +To inspect the proof server, navigate to Containers and **View Details**. There is no action required here, but you should see some output indicating that the server has started. + +To stop the proof server, simply stop the container. + + + + +The proof server listens on port 6300; do not change this port. + +### Your privacy {#your-privacy} + +The proof server exists to protect your privacy. It does not open any network connections; it listens on its assigned port for requests from your wallet. + +## Optional: use Bun as your runtime {#install-bun} + +The guides and tutorials in this documentation use Node.js with npm or Yarn. [Bun](https://bun.sh) is a modern JavaScript runtime and toolkit that can offer significantly faster performance, and it works with Midnight development too. This section covers installing Bun, setting up a Midnight project with it, and the compatibility considerations to keep in mind. If you are happy with Node.js, skip ahead to the [next steps](#next-steps). + +### Install Bun on macOS {#install-bun-on-mac} + +1. Run the command below: + +```bash +curl -fsSL https://bun.sh/install | bash +``` + +This script downloads the appropriate Bun binary for your system and installs it to `~/.bun/bin`. + +2. Verify the installation. Close and reopen your terminal, then run: + +```bash +bun --version +``` + +If `bun --version` returns "command not found", manually add Bun to your `PATH`: + +```bash +export PATH="$HOME/.bun/bin:$PATH" +``` + +### Install Bun on Linux or WSL {#install-bun-on-linux-or-wsl} + +1. Install required dependencies. Bun's installer requires `unzip` to extract the binary: + +```bash +sudo apt update +sudo apt install unzip -y +``` + +2. Run the official Bun installation script: + +```bash +curl -fsSL https://bun.sh/install | bash +``` + +3. Reload your shell configuration so the `bun` command is available: + +```bash +source ~/.bashrc +``` + +4. Verify the installation: + +```bash +bun --version +``` + +You should see the installed Bun version printed to the terminal. + +5. If `bun` is not found, add Bun's install directory to your `PATH`: + +```bash +export PATH="$HOME/.bun/bin:$PATH" +``` + +### Set up a Midnight project with Bun {#install-midnight-packages-with-bun} + +With both Bun and the Compact compiler installed, you can create a Midnight application project. + +1. Initialize a new project: + +```bash +mkdir my-midnight-app +cd my-midnight-app +bun init -y +``` + +This creates a basic `package.json`. + +2. Create the required directories. The project requires separate folders for smart contracts and application source code: + +```bash +mkdir src contracts +``` + +3. Install the Midnight runtime package. Install a specific version to avoid compatibility issues: + +```bash +bun add @midnight-ntwrk/compact-runtime@0.16.0 +``` + +This gives you everything you need to: + +- Run your smart contracts +- Manage your app's data +- Work with zero-knowledge proofs +- Get type definitions for TypeScript + +:::info +Always refer to the [compatibility matrix](../relnotes/support-matrix) for the correct version of the runtime package to install. +::: + +4. Update the `tsconfig.json` file: + +```json title="tsconfig.json" +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "node", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"] +} +``` + +This configuration ensures: + +- Bun can resolve imports correctly. +- TypeScript compiles cleanly. +- Midnight's runtime packages work without extra setup. + +### Configure the project scripts {#configure-the-project} + +Update your `package.json` with the Bun-specific scripts below: + +```json title="package.json" + "scripts": { + "dev": "bun run --hot src/index.ts", + "start": "bun run dist/index.js", + "test": "bun test", + "install:midnight": "bun install", + "compile:contract": "compact compile contracts/message.compact contracts/managed", + "build:contract": "bun run compile:contract && bun build src/index.ts --outdir dist" + }, +``` + +Script explanations: + +- `dev`: Runs the app in development mode with hot reload on `src/index.ts`. +- `start`: Runs the compiled production app from `dist/index.js`. +- `test`: Runs all tests using Bun's test runner. +- `install:midnight`: Installs project dependencies with Bun. +- `compile:contract`: Compiles the Compact smart contract `message.compact` into the `contracts/managed` output directory. +- `build:contract`: First compiles the smart contract, then bundles the app entry file into the `dist` folder for production. + +### Create a smart contract with Compact {#create-smart-contract-with-compact} + +In this section, you create a Compact smart contract that stores and reads a message on the Midnight blockchain. Then, you compile it for use in your TypeScript/Bun application. + +1. Create a file named `message.compact` in the contracts directory: + +```bash +touch contracts/message.compact +``` + +2. Add the code below to the file: + +```compact title="message.compact" +pragma language_version 0.23; + +import CompactStandardLibrary; + +// Public ledger state - visible on blockchain +export ledger message: Opaque<"string">; + +// Circuit to store a message on the blockchain +// The message will be publicly visible +export circuit storeMessage(customMessage: Opaque<"string">): [] { + message = disclose(customMessage); +} + +``` + +Explanation: + +- `pragma language_version 0.23;` + Specifies the exact Compact language version required for this smart contract. Using version 0.23 ensures compatibility with the runtime version 0.16.0 you installed earlier. +- `import CompactStandardLibrary;` + Loads standard functions and types provided by Midnight for contracts. +- `export ledger message: Opaque<"string">;` + Declares a public state variable called `message` on the blockchain. Its value will be visible to everyone. +- `export circuit storeMessage(customMessage: Opaque<"string">): [] { ... }` + Defines a function (circuit) to store a message on the blockchain. `disclose()` makes the message publicly readable. + +3. Compile with `compact`: + +```bash +compact compile contracts/message.compact contracts/managed +``` + +What this does: + +- Converts the human-readable `.compact` contract into a compiled module that your application can interact with. +- Saves the compiled contract in `contracts/managed`, making it ready for integration with your Bun/TypeScript app. + +### Integrate the compiled contract with Bun {#integrate-compiled-smart-contracts-with-bun} + +Now that you have compiled your Compact contract, the next step is to interact with it from a Bun-powered TypeScript application. Here you create a client that simulates storing and reading a message from your Midnight contract. + +1. Create `src/message-client.ts`: + +```bash +touch src/message-client.ts +``` + +Add the following code: + +```typescript title="message-client.ts" +import { Contract, ledger } from "../contracts/managed/contract/index.js"; + +export class MessageClient { + private contract: Contract; + + constructor() { + this.contract = new Contract({}); + } + + async storeMessage(customMessage: string) { + console.log(`📦 (Simulated) Storing message: "${customMessage}"`); + // Real implementation needs proper Midnight context + return { success: true, message: customMessage }; + } + + async getMessage() { + console.log("📥 (Simulated) Fetching message from ledger..."); + return "Hello Midnight!"; + } +} +``` + +What this does: + +- Creates a `MessageClient` class. +- Loads your compiled Compact contract. +- Provides two methods: + - `storeMessage()`: simulates writing data. + - `getMessage()`: simulates reading data. +- Keeps the example minimal while preserving the project structure that real Midnight apps require. + +2. Create `src/index.ts` to serve as your application entry point: + +```bash +touch src/index.ts +``` + +Add the following code: + +```typescript title="index.ts" +import { MessageClient } from "./message-client"; + +async function main() { + console.log("🚀 Starting Midnight Message App..."); + + const client = new MessageClient(); + + console.log("📝 Storing message..."); + await client.storeMessage("Hello Midnight!"); + + console.log("📖 Reading message..."); + const message = await client.getMessage(); + console.log("✅ Message:", message); +} + +main().catch(console.error); +``` + +This file boots your Midnight app, creates an instance of `MessageClient`, calls the store and retrieve functions, and prints the results in your terminal. + +3. Run the app with Bun: + +```bash +bun run --hot src/index.ts +``` + +### Known limitations and workarounds {#known-limitations-and-workarounds} + +While Bun offers significant performance improvements, there are some limitations you should be aware of when building Midnight applications. + +**Native module compatibility.** Some npm packages with native Node.js addons may not work correctly with Bun. Workaround: + +```bash +# If a package fails, try running it with Node.js compatibility mode +bun --bun run your-script.ts + +# Or fall back to Node.js for specific scripts +node your-script.js +``` + +**Package manager lock files.** Mixing Bun and npm in the same project can cause conflicts with lock files. Workaround: + +```bash +# Pick one and stick with it. If using Bun, remove npm files +rm package-lock.json + +# If using npm, remove Bun files +rm bun.lock +``` + +:::tip +Choose one package manager for your project and have your team use only that one. +::: + +**Environment variables.** Bun automatically handles `.env` files, which might cause unexpected behavior if you're using other environment variable tools. Workaround: + +```bash +# Bun loads .env automatically, no library needed. If you need to disable this: +bun --env-file= run your-script.ts + +# Or specify a different env file: +bun --env-file=.env.production run your-script.ts +``` + +### Migrate an existing project from npm to Bun {#migration-guide-npm--bun} + +If your Midnight project was originally set up using Node.js and npm, you can migrate to Bun with a few steps. + +1. Confirm that you have Bun on your system: + +```bash +bun --version +``` + +If not found, [install Bun](#install-bun). + +2. Remove npm artifacts: + +```bash +cd your-midnight-project +rm -rf node_modules package-lock.json +``` + +:::warning +Back up your `package-lock.json` first if you need to revert. +::: + +3. Install dependencies with Bun: + +```bash +bun install +``` + +4. Verify the migration by testing that your application works correctly with Bun: + +```bash +bun run dev +``` + +## Troubleshoot {#troubleshoot-common-issues} + +1. Module not found errors + +Problem: + +```bash +bun run --hot src/index.ts +``` + +Cannot find module `'@midnight-ntwrk/compact-runtime'` + +Solution: + +```bash +bun add @midnight-ntwrk/compact-runtime@0.16.0 +``` + +2. Version mismatch errors + +Problem: + +```bash +CompactError: Version mismatch: compiled code expects 0.16.0, runtime is 0.9.0 +``` + +:::note +The Compact compiler and runtime versions must match exactly. Always check [version compatibility](../relnotes/support-matrix) before compiling. +::: + +Solution: + +Install the matching runtime version: + +```bash +bun add @midnight-ntwrk/compact-runtime@0.16.0 +``` + +Then recompile your smart contracts: + +```bash +compact compile contracts/message.compact contracts/managed +``` + +3. Compact compiler language version errors + +Problem: + +```bash +Exception: message.compact line 1 char 1: + language version 0.22.0 mismatch +``` + +Solution: + +Update your smart contract to use the exact language version: + +```compact +pragma language_version 0.23; +``` + +:::note +Using an exact version number (such as 0.23) instead of a range prevents compatibility issues with different compiler versions. +::: + +4. Compact compiler installation issues + +Problem: + +The Compact compiler fails to install or doesn't work after installation. + +Solutions: + +- Verify the installer script ran successfully: + +```bash +compact --version +``` + +- If the command isn't found, manually add it to your `PATH`: + +```bash +export PATH="$HOME/.local/bin:$PATH" +``` + +- Try reinstalling: + +```bash +curl --proto '=https' --tlsv1.2 -LsSf \ + https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh +``` + +## Next steps {#next-steps} + +Your environment is ready. Before you can deploy anything, your wallet needs test tokens to pay for transactions: + +- [Fund your wallet](./acquire-tokens) with tNIGHT and tDUST. +- Prefer to start with the language instead? Write your first contract with the [hello world](../getting-started/hello-world) tutorial. diff --git a/docs/guides/generating-dust-programmatically.mdx b/docs/guides/generating-dust-programmatically.mdx index aeb6961bb..bc0e3af8c 100644 --- a/docs/guides/generating-dust-programmatically.mdx +++ b/docs/guides/generating-dust-programmatically.mdx @@ -1,7 +1,7 @@ --- title: How to generate DUST programmatically on Preprod description: Guide on generating DUST programmatically on the Midnight Preprod environment using the Midnight wallet SDK. -sidebar_position: 20 +sidebar_position: 3 sidebar_label: How to generate DUST programmatically on Preprod tags: [deployment, Dust, Night, JavaScript] toc_max_heading_level: 2 @@ -11,13 +11,15 @@ toc_max_heading_level: 2 This tutorial walks you through the entire process from scratch: setting up a blank project, creating or restoring a wallet, funding it with tNIGHT, and registering your tokens for DUST generation. By the end, you'll have a working TypeScript script that sets up a wallet and starts generating DUST. +If you only need to fund a wallet you manage through the Lace UI, the faucet-based flow in [Fund your wallet](./acquire-tokens) is the shorter path. This guide is for when you want the same result from code, for example as part of your application's wallet setup. + ## Prerequisites Midnight development is supported on **macOS, Linux, and Windows via WSL**. Make sure you have the following installed: -- [Node.js](https://nodejs.org/) (v22 or later) +- [Node.js](https://nodejs.org/en) (v22 or later) - [Docker Desktop](https://www.docker.com/products/docker-desktop/) — needed to run the proof server Verify your installations: @@ -90,7 +92,7 @@ This downloads the Midnight SDK packages and their dependencies. ### Step 3: Create the proof server config -The proof server runs in Docker. Define how to run it in a small Compose file to start it later with a single command. +The proof server runs in Docker. Define how to run it in a small Compose file to start it later with a single command. (For background on what the proof server does and other ways to run it, see [Run the proof server](./development-environment#run-the-proof-server).) ```bash touch proof-server.yml @@ -101,7 +103,7 @@ Open `proof-server.yml` and paste in the following: ```yaml services: proof-server: - image: 'midnightntwrk/proof-server:8.0.3' + image: 'midnightntwrk/proof-server:8.1.0' command: ['midnight-proof-server -v'] ports: - '6300:6300' @@ -120,7 +122,7 @@ mkdir src touch src/index.ts ``` -Open `src/index.ts` and paste all the code blocks below or scroll to the bottom for the [full script](#full-script). Every section is commented to explain what it does: +Open `src/index.ts` and paste all the code blocks below in order. Each section includes comments that explain what it does: ### Imports @@ -647,409 +649,7 @@ main().catch((err) => { ## Full script -If you'd rather copy the entire script at once instead of pasting each section individually, expand the block below for the full `src/index.ts`. - -
-Click to expand the complete src/index.ts - -```typescript -// This file is part of midnight-dust-generator. -// Copyright (C) Midnight Foundation -// SPDX-License-Identifier: Apache-2.0 -// Licensed under the Apache License, Version 2.0 (the "License"); -// You may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * Midnight DUST Generation Tutorial - * - * This script: - * 1. Creates a new wallet or restores an existing one - * 2. Displays all wallet addresses (shielded, unshielded, dust) - * 3. Waits for you to send tNight from the faucet - * 4. Registers your NIGHT tokens for DUST generation - * 5. Monitors your DUST balance as it accrues - */ - -// ─── Imports ─────────────────────────────────────────────────────────────────── - -import { WebSocket } from 'ws'; -(globalThis as any).WebSocket = WebSocket; - -import { Buffer } from 'buffer'; -import * as readline from 'readline'; -import * as Rx from 'rxjs'; - -import { HDWallet, Roles, generateRandomSeed, WalletFacade, ShieldedWallet, DustWallet, UnshieldedWallet, createKeystore, PublicKey, NoOpTransactionHistoryStorage, DustAddress, MidnightBech32m, ShieldedAddress, ShieldedCoinPublicKey, ShieldedEncryptionPublicKey } from '@midnightntwrk/wallet-sdk'; -import type { UnshieldedKeystore } from '@midnightntwrk/wallet-sdk'; -import { toHex } from '@midnight-ntwrk/midnight-js-utils'; -import * as ledger from '@midnight-ntwrk/midnight-js-protocol/ledger'; -import { unshieldedToken } from '@midnight-ntwrk/midnight-js-protocol/ledger'; -import { setNetworkId, getNetworkId } from '@midnight-ntwrk/midnight-js-network-id'; - - -// ─── Configuration ───────────────────────────────────────────────────────────── - -const CONFIG = { - networkId: 'preprod' as const, - indexerHttpUrl: 'https://indexer.preprod.midnight.network/api/v4/graphql', - indexerWsUrl: 'wss://indexer.preprod.midnight.network/api/v4/graphql/ws', - node: 'https://rpc.preprod.midnight.network', - proofServer: 'http://localhost:6300', - faucetUrl: 'https://midnight-tmnight-preprod.nethermind.dev/', -}; - - -// ─── Helpers: Format raw balances to human-readable ──────────────────────────── -// NIGHT is divided into 10^6 STAR. DUST is divided into 10^15 SPECK. - -const formatNight = (raw: bigint): string => { - const whole = raw / 1_000_000n; - const fraction = (raw % 1_000_000n).toString().padStart(6, '0'); - return `${whole.toLocaleString()}.${fraction}`; -}; - -const formatDust = (raw: bigint): string => { - const whole = raw / 1_000_000_000_000_000n; - const fraction = (raw % 1_000_000_000_000_000n).toString().padStart(15, '0'); - return `${whole.toLocaleString()}.${fraction}`; -}; - - -// ─── Helper: Clock Spinner ───────────────────────────────────────────────────── - -const withStatus = async (message: string, fn: () => Promise): Promise => { - const clocks = ['🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚', '🕛']; - let i = 0; - const interval = setInterval(() => { - process.stdout.write(`\r ${clocks[i++ % clocks.length]} ${message}`); - }, 150); - try { - const result = await fn(); - clearInterval(interval); - process.stdout.write(`\r ✅ ${message}\n`); - return result; - } catch (e) { - clearInterval(interval); - process.stdout.write(`\r ❌ ${message}\n`); - throw e; - } -}; - - -// ─── Helper: Prompt for user input ───────────────────────────────────────────── - -const prompt = (question: string): Promise => { - const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); - return new Promise((resolve) => { - rl.question(question, (answer) => { - rl.close(); - resolve(answer.trim()); - }); - }); -}; - - -// ─── Prompt for a valid Dust address ──────────────────────────────────────────── - -const isValidDustAddress = (addr: string): boolean => { - if (!addr.startsWith('mn_dust_')) return false; - try { - MidnightBech32m.parse(addr).decode(DustAddress, getNetworkId()); - return true; - } catch { - return false; - } -}; - -const promptForDustAddress = async (ownDustAddress: string): Promise => { - while (true) { - const input = await prompt(` Paste your Dust address to designate (Enter for this wallet's): `); - const target = input || ownDustAddress; - - if (isValidDustAddress(target)) { - if (target !== ownDustAddress) { - console.log(`\n Using external dust address: ${target}\n`); - } else { - console.log(''); - } - return target; - } - - console.log(' ❌ Invalid dust address. Dust addresses start with "mn_dust_" followed by the network.'); - console.log(' Make sure you\'re not pasting a shielded or unshielded address.\n'); - } -}; - - -// ─── Create or Restore a Wallet Seed ─────────────────────────────────────────── - -const getOrCreateSeed = async (): Promise => { - const choice = await prompt(' Create a new wallet or restore an existing one? (n/r): '); - if (choice.toLowerCase() === 'r') { - const seed = await prompt(' Enter your seed: '); - if (!seed || seed.length < 32) { - throw new Error('Invalid seed. The seed should be a 64-character hex string.'); - } - console.log(' Restoring wallet from seed...\n'); - return seed; - } - const seed = toHex(Buffer.from(generateRandomSeed())); - console.log('\n Created new wallet.'); - console.log(' ⚠️ Save this seed — it is the ONLY way to restore your wallet:\n'); - console.log(` ${seed}\n`); - return seed; -}; - - -// ─── Derive Keys from the Seed ───────────────────────────────────────────────── - -const deriveKeys = (seed: string) => { - const hdWallet = HDWallet.fromSeed(Buffer.from(seed, 'hex')); - if (hdWallet.type !== 'seedOk') { - throw new Error('Failed to initialize HDWallet from seed. Is the seed a valid hex string?'); - } - const derivationResult = hdWallet.hdWallet - .selectAccount(0) - .selectRoles([Roles.Zswap, Roles.NightExternal, Roles.Dust]) - .deriveKeysAt(0); - if (derivationResult.type !== 'keysDerived') { - throw new Error('Failed to derive keys from seed.'); - } - hdWallet.hdWallet.clear(); - return derivationResult.keys; -}; - - -// ─── Build the Wallet ────────────────────────────────────────────────────────── - -const buildWallet = async (keys: ReturnType) => { - setNetworkId(CONFIG.networkId); - const shieldedSecretKeys = ledger.ZswapSecretKeys.fromSeed(keys[Roles.Zswap]); - const dustSecretKey = ledger.DustSecretKey.fromSeed(keys[Roles.Dust]); - const unshieldedKeystore = createKeystore(keys[Roles.NightExternal], getNetworkId()); - const shieldedConfig = { - networkId: getNetworkId(), - indexerClientConnection: { - indexerHttpUrl: CONFIG.indexerHttpUrl, - indexerWsUrl: CONFIG.indexerWsUrl, - }, - provingServerUrl: new URL(CONFIG.proofServer), - relayURL: new URL(CONFIG.node.replace(/^http/, 'ws')), - }; - const unshieldedConfig = { - networkId: getNetworkId(), - indexerClientConnection: { - indexerHttpUrl: CONFIG.indexerHttpUrl, - indexerWsUrl: CONFIG.indexerWsUrl, - }, - txHistoryStorage: new NoOpTransactionHistoryStorage(), - }; - const dustConfig = { - ...shieldedConfig, - costParameters: { - additionalFeeOverhead: 300_000_000_000_000n, - feeBlocksMargin: 5, - }, - }; - const wallet = await WalletFacade.init({ - configuration: { ...shieldedConfig, ...unshieldedConfig, ...dustConfig }, - shielded: (cfg) => ShieldedWallet(cfg).startWithSecretKeys(shieldedSecretKeys), - unshielded: (cfg) => UnshieldedWallet(cfg).startWithPublicKey(PublicKey.fromKeyStore(unshieldedKeystore)), - dust: (cfg) => - DustWallet(cfg).startWithSecretKey(dustSecretKey, ledger.LedgerParameters.initialParameters().dust), - }); - await wallet.start(shieldedSecretKeys, dustSecretKey); - return { wallet, shieldedSecretKeys, dustSecretKey, unshieldedKeystore }; -}; - - -// ─── Wait for the Wallet to Sync ─────────────────────────────────────────────── - -const waitForSync = (wallet: WalletFacade) => - Rx.firstValueFrom( - wallet.state().pipe( - Rx.throttleTime(5_000), - Rx.filter((state) => state.isSynced), - ), - ); - - -// ─── Wait for Incoming Funds ─────────────────────────────────────────────────── - -const waitForFunds = (wallet: WalletFacade): Promise => - Rx.firstValueFrom( - wallet.state().pipe( - Rx.throttleTime(10_000), - Rx.filter((state) => state.isSynced), - Rx.map((s) => s.unshielded.balances[unshieldedToken().raw] ?? 0n), - Rx.filter((balance) => balance > 0n), - ), - ); - - -// ─── Register NIGHT Tokens for DUST Generation ───────────────────────────────── - -const registerForDustGeneration = async ( - wallet: WalletFacade, - unshieldedKeystore: UnshieldedKeystore, - targetDustAddress: string, - isExternalAddress: boolean = false, -): Promise => { - const state = await Rx.firstValueFrom( - wallet.state().pipe(Rx.filter((s) => s.isSynced)), - ); - if (state.dust.availableCoins.length > 0) { - const dustBalance = state.dust.balance(new Date()); - console.log(` DUST already available: ${formatDust(dustBalance)}\n`); - return; - } - const unregisteredCoins = state.unshielded.availableCoins.filter( - (coin: any) => coin.meta?.registeredForDustGeneration !== true, - ); - if (unregisteredCoins.length === 0) { - console.log(' All NIGHT already registered. Waiting for DUST to generate...'); - } else { - const dustReceiver = MidnightBech32m.parse(targetDustAddress).decode(DustAddress, getNetworkId()); - await withStatus( - `Registering NIGHT for dust generation → ${targetDustAddress}`, - async () => { - const recipe = await wallet.registerNightUtxosForDustGeneration( - unregisteredCoins, - unshieldedKeystore.getPublicKey(), - (payload) => unshieldedKeystore.signData(payload), - dustReceiver, - ); - const finalized = await wallet.finalizeRecipe(recipe); - await wallet.submitTransaction(finalized); - }, - ); - } - if (!isExternalAddress) { - await withStatus('Waiting for DUST to generate (this may take 1–2 minutes)', () => - Rx.firstValueFrom( - wallet.state().pipe( - Rx.throttleTime(5_000), - Rx.filter((s) => s.isSynced), - Rx.filter((s) => s.dust.balance(new Date()) > 0n), - ), - ), - ); - } -}; - - -// ─── Check DUST Balance ──────────────────────────────────────────────────────── - -const checkDustBalance = async (wallet: WalletFacade): Promise => { - const state = await Rx.firstValueFrom( - wallet.state().pipe(Rx.filter((s) => s.isSynced)), - ); - return state.dust.balance(new Date()); -}; - - -// ─── Main ────────────────────────────────────────────────────────────────────── - -const main = async () => { - console.log(''); - const seed = await getOrCreateSeed(); - const keys = deriveKeys(seed); - const { wallet, unshieldedKeystore } = await withStatus('Building wallet', () => buildWallet(keys)); - - const initialState = await Rx.firstValueFrom(wallet.state()); - const networkId = getNetworkId(); - const coinPubKey = ShieldedCoinPublicKey.fromHexString(initialState.shielded.coinPublicKey.toHexString()); - const encPubKey = ShieldedEncryptionPublicKey.fromHexString(initialState.shielded.encryptionPublicKey.toHexString()); - const shieldedAddress = MidnightBech32m.encode(networkId, new ShieldedAddress(coinPubKey, encPubKey)).toString(); - const unshieldedAddress = unshieldedKeystore.getBech32Address(); - const dustAddress = DustAddress.encodePublicKey(networkId, initialState.dust.publicKey); - - console.log(''); - console.log(' Wallet Addresses:'); - console.log(` Shielded: ${shieldedAddress}`); - console.log(` Unshielded: ${unshieldedAddress} ← send tNight here`); - console.log(` Dust: ${dustAddress}`); - console.log(''); - console.log(` Faucet: ${CONFIG.faucetUrl}`); - console.log(''); - - await withStatus('Syncing wallet with network', () => waitForSync(wallet)); - - const state = await Rx.firstValueFrom(wallet.state()); - const nightBalance = state.unshielded.balances[unshieldedToken().raw] ?? 0n; - const dustBalance = state.dust.balance(new Date()); - - let usedExternalAddress = false; - - if (nightBalance > 0n && dustBalance > 0n) { - console.log(` tNight Balance: ${formatNight(nightBalance)}`); - console.log(` DUST Balance: ${formatDust(dustBalance)}\n`); - console.log(' Your wallet is already generating DUST. No action needed.'); - } else if (nightBalance > 0n && dustBalance === 0n) { - console.log(` tNight Balance: ${formatNight(nightBalance)}`); - console.log(' DUST Balance: 0\n'); - console.log(' You have tNight but no DUST yet. Let\'s register for DUST generation.\n'); - const targetDustAddress = await promptForDustAddress(dustAddress); - usedExternalAddress = targetDustAddress !== dustAddress; - await registerForDustGeneration(wallet, unshieldedKeystore, targetDustAddress, usedExternalAddress); - } else { - console.log(' Waiting for tNight — copy the unshielded address above and paste it into the faucet.'); - console.log(' ⚠️ Make sure you copy only the address with no extra spaces.\n'); - const balance = await withStatus('Waiting for incoming tNight', () => waitForFunds(wallet)); - console.log(` tNight Balance: ${formatNight(balance)}\n`); - const targetDustAddress = await promptForDustAddress(dustAddress); - usedExternalAddress = targetDustAddress !== dustAddress; - await registerForDustGeneration(wallet, unshieldedKeystore, targetDustAddress, usedExternalAddress); - } - - if (usedExternalAddress) { - console.log(''); - console.log(' DUST is being generated to the external address you designated.'); - console.log(' Because DUST is a shielded token, only the wallet holding that dust'); - console.log(' secret key can see the balance. Check the receiving wallet to verify'); - console.log(' DUST is accruing.'); - } else { - const currentDust = await checkDustBalance(wallet); - console.log(''); - console.log(` DUST Balance: ${formatDust(currentDust)}`); - console.log(' DUST generates continuously over time.'); - console.log(' Press Enter to re-check, or type "q" to quit.\n'); - let running = true; - while (running) { - const answer = await prompt(' > '); - if (answer.toLowerCase() === 'q' || answer.toLowerCase() === 'quit' || answer.toLowerCase() === 'exit') { - running = false; - } else { - const updated = await checkDustBalance(wallet); - const time = new Date().toLocaleTimeString(); - console.log(` [${time}] DUST Balance: ${formatDust(updated)}\n`); - } - } - } - - console.log(''); - console.log(' To restore this wallet later, run the script again and choose "r".'); - console.log(''); - await wallet.stop(); - process.exit(0); -}; - -main().catch((err) => { - console.error('\n ❌ Error:', err.message || err); - process.exit(1); -}); -``` - -
+The complete `src/index.ts` is the concatenation of the code blocks above, in the order shown. Paste them into the file from top to bottom. ## Step 5: Start the proof server @@ -1165,4 +765,4 @@ midnight-dust-tutorial/ ## Next steps -With DUST generation active, your wallet is ready to pay transaction fees. From here you can explore [deploying a smart contract on Midnight](https://docs.midnight.network/guides/deploy-mn-app) or integrating DUST generation into your own application's wallet setup flow. Check out the [Awesome DApps repo](https://github.com/midnightntwrk/midnight-awesome-dapps) and the [Midnight documentation](https://docs.midnight.network/) to keep building. \ No newline at end of file +With DUST generation active, your wallet is ready to pay transaction fees. From here you can explore [deploying a smart contract on Midnight](./local-to-testnet#deploy-to-preprod) or integrating DUST generation into your own application's wallet setup flow. Check out the [Awesome DApps repo](https://github.com/midnightntwrk/midnight-awesome-dapps) and the [Midnight documentation](https://docs.midnight.network/) to keep building. \ No newline at end of file diff --git a/docs/guides/index.mdx b/docs/guides/index.mdx new file mode 100644 index 000000000..981c3e388 --- /dev/null +++ b/docs/guides/index.mdx @@ -0,0 +1,69 @@ +--- +SPDX-License-Identifier: Apache-2.0 +copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +title: Guides +description: Task-focused guides that take you from a fresh machine to a deployed Midnight DApp with a wallet-connected frontend. +tags: + - guides + - setup + - deployment + - wallet + - midnight-network +--- + +import PersonaTiles from '@site/src/components/PersonaTiles'; + +# Guides + +These guides follow the path a Midnight DApp takes from a fresh machine to production: set up your tools, fund a wallet, deploy your contract, connect a frontend, and plan for the long term. Follow them in order for the full journey, or jump straight to the task in front of you. + + diff --git a/docs/guides/install-bun-runtime-midnight.mdx b/docs/guides/install-bun-runtime-midnight.mdx deleted file mode 100644 index 35c5b95ca..000000000 --- a/docs/guides/install-bun-runtime-midnight.mdx +++ /dev/null @@ -1,616 +0,0 @@ ---- -title: Set up Bun for Midnight development -description: Set up Bun package manager and the Compact compiler to build smart contracts and applications on the Midnight blockchain. -sidebar_label: Set up Bun for Midnight development -sidebar_position: 65 -tags: [installation, bun, compact, setup] -slug: /how-to/bun-runtime-midnight -toc_max_heading_level: 2 ---- - -# Set up Bun for Midnight development - -Bun is a modern JavaScript runtime and toolkit that can offer significantly faster performance compared to Node.js and npm. -This guide walks you through using Bun with Midnight Network development, covering installation, configuration, compatibility considerations, best practices, and much more. - -## Prerequisites - -Before starting this guide, ensure you have: - -- Basic knowledge of JavaScript/TypeScript -- Familiarity with command-line interfaces -- Understanding of package managers (npm, yarn) - -:::note -Docker is not required for this tutorial. It's only needed when running actual Midnight proof servers. -::: - -Here's what your complete project will look like by the end of this guide: - -Image - -## Install Bun - -In this section, you'll install Bun on your development machine. - -### Install Bun on Mac - -1. Run the command below: - -```bash -curl -fsSL https://bun.sh/install | bash -``` - -This script: - -- Downloads the appropriate Bun binary for your system. -- Installs it to `~/.bun/bin`. - -2. Verify the installation: - -Close and reopen your terminal, then run: - -```bash -bun --version -``` - -Example of expected output: - -Image - -If bun --version returns "command not found." manually add Bun to your PATH. - -```bash -export PATH="$HOME/.bun/bin:$PATH” -``` - -### Install Bun on linux or WSL - -Follow the steps below to install Bun on Linux or Windows Subsystem for Linux (WSL). - -1. Install required dependencies - -Bun’s installer requires unzip to extract the binary. - -```bash -sudo apt update -sudo apt install unzip -y -``` - -2. Install Bun - -Run the official Bun installation script: - -``` -curl -fsSL https://bun.sh/install | bash -``` - -3. Reload your shell configuration - -Update your environment so the bun command is available: - -```bash -source ~/.bashrc -``` - -4. Verify the installation - -```bash -bun --version -``` - -You should see the installed Bun version printed to the terminal. - -5. Fix “command not found” (if needed) - -If bun is not found, add Bun’s install directory to your `PATH`: - -```bash -export PATH="$HOME/.local/bin:$PATH" -``` - -## Install using the installer script - -Here, you will install the Compact compiler. This is a standalone tool (separate from Bun) used to turn Compact smart contracts (written in the Compact language) into code that can run on the Midnight blockchain. - -1. Install using the Compact installer: - -```bash -curl --proto '=https' --tlsv1.2 -LsSf \ - https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh -``` - -By default, the installer places the compact binary in `$HOME/.local/bin` - -2. Update the compiler - -After installation, update the Compact compiler to the version used in this tutorial: - -```bash -compact update -``` - -3. Verify the installation - -Confirm that Compact is installed and correctly configured: - -```bash -compact check -``` - -If the installation is successful, you should see output similar to the following: - -```bash -compact: aarch64-darwin -- Up to date -- 0.31.0 -``` - -4. Fix “command not found” (PATH issue) - -If you see an error such as: - -``` -compact: command not found -``` - -it means the directory where Compact was installed `($HOME/.local/bin)` is not included in your PATH. - -Add it to your current terminal session: - -```bash -export PATH="$HOME/.local/bin:$PATH" -``` - -## Install Midnight packages with Bun - -With both Bun and the Compact compiler installed, you can now create a Midnight application project. - -1. Initialize a new project: - -```bash -mkdir my-midnight-app -cd my-midnight-app -bun init -y -``` - -This creates a basic `package.json`. - -2. Create the required directories: - -The project requires separate folders for smart contracts and application source code. - -```bash -mkdir src contracts -``` - -3. Install the Midnight runtime package - -Install a specific version to avoid compatibility issues: - -```bash -bun add @midnight-ntwrk/compact-runtime@0.16.0 -``` - -This gives you everything you need to: - -- Run your smart contracts -- Manage your app's data -- Work with zero-knowledge proofs -- Get type definitions for TypeScript - -:::info -Always refer to the [compatibility matrix](../relnotes/support-matrix) for the correct version of the runtime package to install. -::: - -4. Update the `tsconfig.json` file: - -```json title="tsconfig.json" -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "node", - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "resolveJsonModule": true - }, - "include": ["src/**/*"] -} -``` - -This configuration ensures: - -- Bun can resolve imports correctly. -- TypeScript compiles cleanly. -- Midnight’s runtime packages work without extra setup. - -## Configure the project - -Update your `package.json` with the Bun-specific scripts below: - -```json title="package.json" - "scripts": { - "dev": "bun run --hot src/index.ts", - "start": "bun run dist/index.js", - "test": "bun test", - "install:midnight": "bun install", - "compile:contract": "compact compile contracts/message.compact contracts/managed", - "build:contract": "bun run compile:contract && bun build src/index.ts --outdir dist" - }, -``` - -Script explanations: - -- dev: Runs the app in development mode with hot reload on src/index.ts. - -- start: Runs the compiled production app from dist/index.js. - -- test: Runs all tests using Bun’s test runner. - -- install:midnight: Installs project dependencies with Bun. - -- compile:contract: Compiles the Compact smart contract `message.compact` into the contracts/managed output directory. - -- build:contract: First compiles the smart contract, then bundles the app entry file into the dist folder for production. - -## Create smart contract with Compact - -In this section, you will create a simple Compact smart contract that allows you to store and read a message on the Midnight blockchain. Then, you will compile it so it can be used in your TypeScript/Bun application. - -1. Create the smart contract file. - -Create a file named `message.compact` in the contracts directory: - -```bash -touch contracts/message.compact -``` - -2. Add the code below to the file: - -```compact title="message.compact" -pragma language_version 0.23; - -import CompactStandardLibrary; - -// Public ledger state - visible on blockchain -export ledger message: Opaque<"string">; - -// Circuit to store a message on the blockchain -// The message will be publicly visible -export circuit storeMessage(customMessage: Opaque<"string">): [] { - message = disclose(customMessage); -} - -``` - -Explanation: - -- `pragma language_version 0.23;` - Specifies the exact Compact compiler version required for this smart contract. Using version 0.23 ensures compatibility with the runtime version 0.16.0 you installed earlier. -- `import CompactStandardLibrary;` - Loads standard functions and types provided by Midnight for contracts. -- `export ledger message: Opaque<"string">;` - Declares a public state variable called `message` on the blockchain. Its value will be visible to everyone. -- `export circuit storeMessage(customMessage: Opaque<"string">): [] { ... }` - Defines a function (circuit) to store a message on the blockchain. `disclose()` makes the message publicly readable. - -3. Compile with compact - -```bash -compact compile contracts/message.compact contracts/managed -``` - -Image - -What this does: - -- Converts the human-readable `.compact` contract into a compiled module that your application can interact with. -- Saves the compiled contract in `contracts/managed`, making it ready for integration with your Bun/TypeScript app. - -## Integrate compiled smart contracts with Bun - -Now that your Compact contracts are compiled, the next step is to interact with them from a Bun-powered TypeScript application. - -Here you’ll create a client that simulates storing and reading a message from your Midnight contract. - -1. Create `src/message-client.ts` - -```bash -touch src/message-client.ts -``` - -Add the following code: - -```typescript title="message-client.ts" -import { Contract, ledger } from "../contracts/managed/contract/index.js"; - -export class MessageClient { - private contract: Contract; - - constructor() { - this.contract = new Contract({}); - } - - async storeMessage(customMessage: string) { - console.log(`📦 (Simulated) Storing message: "${customMessage}"`); - // Real implementation needs proper Midnight context - return { success: true, message: customMessage }; - } - - async getMessage() { - console.log("📥 (Simulated) Fetching message from ledger..."); - return "Hello Midnight!"; - } -} -``` - -What this does: - -- Creates a `MessageClient` class. -- Loads your compiled Compact contract. -- Provides two methods: - - `storeMessage()`: simulates writing data. - - `getMessage()`: simulates reading data. -- Keeps things simple while preserving the project structure required by real Midnight apps. - -2. Create `src/index.ts` - -This file will serve as your application entry point. - -```bash -touch src/index.ts -``` - -Add the following code: - -```typescript title="index.ts" -import { MessageClient } from "./message-client"; - -async function main() { - console.log("🚀 Starting Midnight Message App..."); - - const client = new MessageClient(); - - console.log("📝 Storing message..."); - await client.storeMessage("Hello Midnight!"); - - console.log("📖 Reading message..."); - const message = await client.getMessage(); - console.log("✅ Message:", message); -} - -main().catch(console.error); -``` - -### What this file does: - -- Boot your Midnight app -- Creates an instance of `MessageClient` -- Calls the store and retrieves functions -- Prints the results nicely in your terminal - -3. Run the app with Bun: - -```bash -bun run --hot src/index.ts -``` - -Image - -## Known limitations and workarounds - -While Bun offers significant performance improvements, there are some limitations you should be aware of when building Midnight applications. Here are the common issues and how to work around them. - -### Native module compatibility - -Issue: -some npm packages with native Node.js addons may not work correctly with Bun. - -Workaround: - -```bash -# If a package fails, try running it with Node.js compatibility mode -bun --bun run your-script.ts - -# Or fall back to Node.js for specific scripts -node your-script.js -``` - -### Package manager lock files - -Issue: -mixing Bun and npm in the same project can cause conflicts with lock files. - -Workaround: - -```bash -# Pick one and stick with it. If using Bun, remove npm files -rm package-lock.json - -# If using npm, remove Bun files -rm bun.lock -``` - -:::tip -Choose one package manager for your project and have your team use only that one. -::: - -### Environment variables - -Issue: -bun automatically handles `.env` files, which might cause unexpected behavior if you're using other environment variable tools. - -Workaround: - -```bash -# Bun loads .env automatically, no library needed# If you need to disable this: -bun --env-file= run your-script.ts - -# Or specify a different env file: -bun --env-file=.env.production run your-script.ts -``` - -## Migration guide (npm → Bun) - -If your Midnight project was originally set up using Node.js + npm, you can migrate to Bun with just a few steps. This guide walks you through the process safely and cleanly. - -1. Install Bun - -Make sure Bun is installed on your system. - -To check - -```bash - bun --version -``` - -If not found, go to this step and [install Bun](#install-bun). - -2. Remove npm Artifacts: - -```bash -cd your-midnight-project -rm -rf node_modules package-lock.json -``` - -:::warning -Back up your `package-lock.json` first if you need to revert. -::: - -3. Install dependencies with Bun: - -```bash -bun install -``` - -4. Verify the Migration - -Test that your application works correctly with Bun: - -```bash -bun run dev -``` - -## Troubleshoot common issues - -1. Module Not Found Errors - -Problem: - -```bash -bun run --hot src/index.ts -``` - -Cannot find module `'@midnight-ntwrk/compact-runtime’` - -Solution: - -```bash -bun add @midnight-ntwrk/compact-runtime@0.16.0 -``` - -2. Version mismatch errors - -Problem: - -```bash -CompactError: Version mismatch: compiled code expects 0.16.0, runtime is 0.9.0 -``` - -:::note -The Compact compiler and runtime versions must match exactly. Always check [version compatibility](../relnotes/support-matrix) before compiling. -::: - -Solution: - -Install the matching runtime version: - -```bash -bun add @midnight-ntwrk/compact-runtime@0.16.0 -``` - -Then recompile your smart contracts: - -```bash -compact compile contracts/message.compact contracts/managed -``` - -3. Compact compiler language version errors - -Problem: - -```bash -Exception: message.compact line 1 char 1: - language version 0.22.0 mismatch -``` - -Solution: - -Update your smart contract to use the exact language version: - -```compact -pragma language_version 0.23; -``` - -:::note -Using an exact version number (such as 0.22) instead of a range prevents compatibility issues with different compiler versions. -::: - -4. Compact compiler installation issues - -Problem: - -The Compact compiler fails to install or doesn't work after installation. - -Solutions: - -- Verify the installer script ran successfully: - -```bash -compact --version -``` - -- If the command isn't found, manually add it to your PATH: - -```bash -export PATH="$HOME/.compact/bin:$PATH" -``` - -- Try reinstalling: - -```bash -curl --proto '=https' --tlsv1.2 -LsSf \ - https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh -``` - -## Next steps - -Now that you have Bun installed and configured, you can start building DApps on Midnight. See the following guides for more information: - -- [DApp connector](/api-reference/dapp-connector) -- [Deploy a contract](../guides/deploy-mn-app) -- [Interact with a contract](../guides/interact-with-mn-app) - - diff --git a/docs/guides/local-to-testnet.mdx b/docs/guides/local-to-testnet.mdx new file mode 100644 index 000000000..2853965db --- /dev/null +++ b/docs/guides/local-to-testnet.mdx @@ -0,0 +1,751 @@ +--- +SPDX-License-Identifier: Apache-2.0 +copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +title: From local network to testnet +description: Run a local Midnight network, configure the Midnight.js providers, and deploy your contract to the Preprod and Preview testnets. +sidebar_label: "From local network to testnet" +sidebar_position: 4 +tags: [local, network, testing, providers, deployment, smart-contract, javascript] +keywords: [midnight, local network, providers, configure Midnight providers, deploy, preprod, preview] +toc_max_heading_level: 3 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import Step, { StepsProvider } from "@site/src/components/Step/Step"; + +# From local network to testnet + +A Midnight DApp usually grows through three stages: you test it against a local network with funded test wallets, you wire up the Midnight.js providers that connect your code to network services, and then you deploy to **Preprod**, the public Midnight testnet used for final testing before mainnet. This guide walks through all three stages in order. + +## Prerequisites {#prerequisites} + +Before you proceed, ensure you have: + +- [Docker Desktop](https://docs.docker.com/get-started/get-docker/) installed and running. If you haven't set up your environment yet, start with [Set up your environment](./development-environment). +- Node.js version 22.x or higher installed. Install it using [NVM](https://github.com/nvm-sh/nvm). +- A compiled Compact smart contract with the `keys/` and `zkir/` directories generated. If you haven't done so yet, then follow the [build your first contract](../getting-started/hello-world) tutorial to get started. + +## Run a local Midnight network {#local-network} + +The local network is a standalone tool for running a local Midnight development network and funding test wallets in the undeployed network. + +### Install the local network tool {#installation} + +To get started, clone the [Midnight local network repository](https://github.com/midnightntwrk/midnight-local-dev): + +```bash +git clone https://github.com/midnightntwrk/midnight-local-dev.git +cd midnight-local-dev +``` + +Install the dependencies: + +```bash +npm install +``` + +### Network services {#network-services} + +The local network runs three Docker containers on the following ports: + +| Service | Container Name | Port | URL | +|:---:|:---:|:---:|:---:| +| **Midnight Node** | `midnight-node` | `9944` | `http://localhost:9944` | +| **Indexer** (GraphQL) | `midnight-indexer` | `8088` | `http://localhost:8088/api/v4/graphql` | +| **Indexer** (WebSocket) | `midnight-indexer` | `8088` | `ws://localhost:8088/api/v4/graphql/ws` | +| **Proof Server** | `midnight-proof-server` | `6300` | `http://localhost:6300` | + +All services use the `undeployed` network ID with the `dev` node preset. + +#### Docker images {#docker-images} + +The `standalone.yml` file in the root of the repository defines the Docker images and the pinned versions pulled when you start the local network: `midnightntwrk/midnight-node`, `midnightntwrk/indexer-standalone`, and `midnightntwrk/proof-server`. Check that file in your checkout for the exact versions, and the [compatibility matrix](../relnotes/support-matrix) for how they line up with the rest of the toolchain. + +#### Wallet SDK packages {#wallet-sdk-packages} + +The local network's funding tool is built on the [Wallet SDK](/sdks/official/wallet-developer-guide) and Midnight.js. The repository's `package.json` pins the exact versions of the key packages: `@midnight-ntwrk/wallet-sdk`, `@midnight-ntwrk/midnight-js-network-id`, and `@midnight-ntwrk/testkit-js`. + +### Start the local network {#start-the-local-network} + +Once the installation is complete, start the local network by running the following command: + +```bash +npm start +``` + +This single command does the following: + +- **Pull** the latest Docker images for the Midnight node, indexer, and proof server. +- **Start** all three containers with health checks. +- **Initialize** the genesis master wallet (seed `0x00...001`) which holds all minted NIGHT tokens. +- **Register DUST** for the master wallet (required to pay transaction fees). +- **Display** the master wallet balance. +- **Present an interactive menu** for funding wallets in the undeployed network. + +Once the network is running, the output shows: + +``` +Choose an option: + [1] Fund accounts from config file (NIGHT + DUST registration) + [2] Fund accounts by public key (NIGHT transfer only) + [3] Display master wallet balances + [4] Exit +> +``` + +### Fund wallets {#fund-wallets} + +The local network provides two options for funding wallets: + +- Fund from a config file +- Fund by public key + +Each funding operation transfers _50,000 tNIGHT_ from the genesis master wallet. You can fund up to _10 accounts_ per operation. + +#### Option 1: Fund from a config file {#option-1-fund-from-a-config-file} + +The config option handles wallet operations such as generating the wallet seed from mnemonic, transferring tNIGHT to the unshielded address, syncing the wallet, and registering tNIGHT for DUST generation. + +It pulls wallet information from a config file, which is a JSON file with the following structure: + +```json +{ + "accounts": [ + { + "name": "Alice", + "mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" + } + ] +} +``` +Here's a breakdown of the fields: + +| Field | Type | Description | +|:---:|:---:|:---:| +| `accounts` | `array` | List of accounts to fund (max 10) | +| `accounts[].name` | `string` | Display name for logging | +| `accounts[].mnemonic` | `string` | BIP39 mnemonic phrase (24 words) | + +The repository provides an example file at `accounts.example.json`. Copy the file to `accounts.json` and edit it to add your wallet mnemonics. + +```bash +cp accounts.example.json accounts.json +``` + +After editing the file, select option `[1] Fund accounts from config file (NIGHT + DUST registration)` from the interactive menu. + +The tool prompts you for the path to the config file. + +``` +> 1 +Path to accounts JSON file: ./accounts.json +``` + +The tool funds the wallets from the config file. + +#### Option 2: Fund by public key {#option-2-fund-by-public-key} + +The public key option allows you to fund wallets by their Bech32 addresses. You can fund up to 10 wallets at a time. + +Select option `[2] Fund accounts by public key (NIGHT transfer only)` from the interactive menu. + +The tool prompts you for the Bech32 addresses of the wallets you want to fund, separated by commas. + +``` +> 2 +Enter Bech32 addresses (comma-separated): mn1q..., mn1q... +``` + +Each address receives _50,000 tNIGHT_. Recipients must register for DUST generation themselves before they can pay transaction fees. + +### Connect a DApp {#connect-a-dapp} + +Once the network is running, any Midnight DApp can connect using the standard localhost endpoints. + +Here's an example of how to connect a DApp to the local network: + +```typescript +import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id'; + +setNetworkId('undeployed'); + +const config = { + indexer: 'http://127.0.0.1:8088/api/v4/graphql', + indexerWS: 'ws://127.0.0.1:8088/api/v4/graphql/ws', + node: 'http://127.0.0.1:9944', + proofServer: 'http://127.0.0.1:6300', + networkId: 'undeployed', +}; + +// Use these endpoints with the Midnight wallet SDK, contract deployment, and other DApp operations +``` + +### Run the network standalone {#run-the-network-standalone} + +If you only need the Docker containers running without the interactive menu for wallet initialization or funding, then you can use Docker Compose directly: + +```bash +docker compose -f standalone.yml up -d +``` + +To check the status of the containers: + +```bash +docker compose -f standalone.yml ps +``` + +To view the logs: + +```bash +docker compose -f standalone.yml logs -f +``` + +This is useful when: +- Your DApp handles its own wallet initialization and funding. +- You want to keep the network running across multiple test sessions. +- You're debugging container-level issues. + +:::note +In this mode, you must handle genesis wallet funding and DUST registration yourself. +::: + +## Configure the Midnight.js providers {#configure-providers} + +Providers are the modular components that the Midnight.js packages use to interact with the Midnight Network. Whether your contract targets the local network above or a public testnet, the providers are what connect your code to the right services. This section covers how to configure them before deploying or interacting with a Compact smart contract. + +### The `MidnightProviders` type {#the-midnightproviders-type} + +`MidnightProviders` is a generic type imported from `@midnight-ntwrk/midnight-js-types`. It accepts three arguments that are specific to your contract: + +```typescript +import { type MidnightProviders } from '@midnight-ntwrk/midnight-js-types'; + +// CircuitKeys — union of circuit names from your compiled contract +// PrivateStateId — literal type of your private state storage key +// PrivateState — shape of your contract's private state object +type MyProviders = MidnightProviders; +``` + +A `common-types.ts` file is a good place to keep these aliases, especially if your API and UI packages share the same contract types: + +```typescript +import { type MidnightProviders } from '@midnight-ntwrk/midnight-js-types'; +import { type FoundContract } from '@midnight-ntwrk/midnight-js-contracts'; + +export const myPrivateStateKey = 'myPrivateState'; +export type PrivateStateId = typeof myPrivateStateKey; + +export type MyCircuitKeys = 'circuitA' | 'circuitB'; +export type MyProviders = MidnightProviders; +``` + +### Set the network ID {#set-the-network-id} + +Call `setNetworkId` before initializing any providers. All Midnight.js packages read this value to target the correct network. + +```typescript +import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id'; + +setNetworkId('preprod'); + +/** Supported network IDs: 'mainnet', 'preview', 'preprod', 'undeployed' */ +``` + +In Node.js environments, also polyfill `WebSocket` so that GraphQL subscriptions to the indexer work: + +```typescript +import { WebSocket } from 'ws'; + +globalThis.WebSocket = WebSocket as unknown as typeof globalThis.WebSocket; +``` + +### Configure the providers {#configure-the-providers} + +Each provider handles one specific capability in the transaction pipeline: + +- Storing private state +- Querying the indexer +- Generating ZK proofs +- Balancing transactions +- Submitting transactions on-chain + +The following sections cover how to configure each provider. + +#### `privateStateProvider` {#privatestateprovider} + +The private state provider stores and retrieves the contract's private state on the local device. Private state is never sent to the network. + +Use `levelPrivateStateProvider` from `@midnight-ntwrk/midnight-js-level-private-state-provider`. It persists private state to a LevelDB database encrypted with AES-256-GCM. + +```typescript +import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider'; + +const privateStateProvider = levelPrivateStateProvider({ + privateStateStoreName: 'my-contract-private-state', + signingKeyStoreName: 'my-contract-private-state-signing-keys', + privateStoragePasswordProvider: () => 'your-encryption-password', +}); +``` + +The table below describes the parameters for the `levelPrivateStateProvider` function. + +| Parameter | Description | +|-----------|-------------| +| `privateStateStoreName` | Name of the LevelDB store for private state | +| `signingKeyStoreName` | Name of the LevelDB store for signing keys | +| `privateStoragePasswordProvider` | Function returning the encryption password | + +:::warning Password security + +Do not use a hardcoded password in production. Derive it from wallet credentials or a secure key management system. + +::: + +#### `publicDataProvider` {#publicdataprovider} + +The public data provider queries and subscribes to on-chain contract state via the Midnight indexer's GraphQL API. Use `indexerPublicDataProvider` from `@midnight-ntwrk/midnight-js-indexer-public-data-provider` in both Node.js and browser environments. + +```typescript +import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider'; + +const publicDataProvider = indexerPublicDataProvider( + 'https://indexer.preprod.midnight.network/api/v4/graphql', // HTTP query URL + 'wss://indexer.preprod.midnight.network/api/v4/graphql/ws', // WebSocket subscription URL +); +``` + +For local development, use: + +```typescript +const publicDataProvider = indexerPublicDataProvider( + 'http://localhost:8088/api/v4/graphql', + 'ws://localhost:8088/api/v4/graphql/ws', +); +``` + +:::info Local development + +These are the endpoints of the [local Midnight network](#local-network) described earlier in this guide. Use the local network to test your contract and providers before deploying to a public network. + +::: + +#### `zkConfigProvider` {#zkconfigprovider} + +The ZK configuration provider supplies the prover key, verifier key, and ZKIR artifacts that the proof provider needs to generate zero-knowledge proofs. The right implementation depends on where you store your ZK artifacts. + + + + +If you are running your contract in a Node.js environment, then use `NodeZkConfigProvider` from `@midnight-ntwrk/midnight-js-node-zk-config-provider`. It reads artifacts from the local filesystem. The path should point to the directory containing the compiled contract's `keys/` and `zkir/` output. + +```typescript +import { NodeZkConfigProvider } from '@midnight-ntwrk/midnight-js-node-zk-config-provider'; + +const zkConfigProvider = new NodeZkConfigProvider<'circuitA' | 'circuitB'>( + '/path/to/contract/src/managed/my-contract', +); +``` + +The type parameter is the union of circuit names your contract exposes. This is typically the same type as your `CircuitKeys` alias. + + + + +If your ZK artifacts are hosted on a remote server, then you can use `FetchZkConfigProvider` +from the `@midnight-ntwrk/midnight-js-fetch-zk-config-provider` package. It fetches artifacts over HTTP from a URL. + +```typescript +import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider'; + +const zkConfigProvider = new FetchZkConfigProvider<'circuitA' | 'circuitB'>('https://example.com/zk-artifacts'); +``` + + + + +#### `proofProvider` {#proofprovider} + +The proof provider calls the Midnight proof server to generate zero-knowledge proofs from unproven transactions. Use `httpClientProofProvider` from `@midnight-ntwrk/midnight-js-http-client-proof-provider`. + +It takes the proof server URL and the `zkConfigProvider` instance. The proof server needs access to the same ZK artifacts, which it retrieves via the `zkConfigProvider`. + +```typescript +import { httpClientProofProvider } from '@midnight-ntwrk/midnight-js-http-client-proof-provider'; + +const proofProvider = httpClientProofProvider( + 'http://localhost:6300', // proof server URL + zkConfigProvider, +); +``` + +:::info Proof server + +The proof server is a Docker container you run locally or point to a hosted instance. See the [proof server guide](./development-environment#run-the-proof-server) for setup instructions. + +::: + +#### `walletProvider` {#walletprovider} + +The wallet provider exposes the public keys needed to receive shielded tokens and decrypts transaction data. It also balances unbound transactions by selecting UTXOs to cover fees and adding change outputs. + +In a Node.js CLI, you can implement `WalletProvider` using the [Wallet SDK](/sdks/official/wallet-developer-guide) facade. + +```typescript +import { + type CoinPublicKey, + type EncPublicKey, + type FinalizedTransaction, + ZswapSecretKeys, + DustSecretKey, +} from '@midnight-ntwrk/ledger-v8'; +import { type WalletProvider, UnboundTransaction } from '@midnight-ntwrk/midnight-js-types'; +import { ttlOneHour } from '@midnight-ntwrk/midnight-js-utils'; +import { type WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade'; + +class MyWalletProvider implements WalletProvider { + constructor( + private readonly wallet: WalletFacade, + private readonly zswapSecretKeys: ZswapSecretKeys, + private readonly dustSecretKey: DustSecretKey, + ) {} + + getCoinPublicKey(): CoinPublicKey { + return this.zswapSecretKeys.coinPublicKey; + } + + getEncryptionPublicKey(): EncPublicKey { + return this.zswapSecretKeys.encryptionPublicKey; + } + + async balanceTx(tx: UnboundTransaction, ttl: Date = ttlOneHour()): Promise { + const recipe = await this.wallet.balanceUnboundTransaction( + tx, + { shieldedSecretKeys: this.zswapSecretKeys, dustSecretKey: this.dustSecretKey }, + { ttl }, + ); + return await this.wallet.finalizeRecipe(recipe); + } +} +``` + +The class takes a `WalletFacade` instance together with the `ZswapSecretKeys` and `DustSecretKey` derived from the wallet seed. It exposes three methods that the SDK calls during the transaction lifecycle: + +- `getCoinPublicKey`: Returns the shielded address used to receive tokens. +- `getEncryptionPublicKey`: Returns the key used to decrypt incoming shielded transaction data. +- `balanceTx`: Selects UTXOs to cover fees, adds change outputs, and finalizes the transaction ready for proof generation and submission. + +:::warning DUST requirements + +DUST is the network resource that fuels transactions on the Midnight Network. You must have DUST in your wallet to pay for transaction fees. For more information, see the [Generating DUST programmatically](./generating-dust-programmatically) guide. + +::: + +The same class that implements `WalletProvider` can also implement `MidnightProvider`, since the `WalletFacade` exposes both capabilities. + +```typescript +import { type MidnightProvider } from '@midnight-ntwrk/midnight-js-types'; +import { type FinalizedTransaction } from '@midnight-ntwrk/ledger-v8'; + +class MyWalletProvider implements WalletProvider, MidnightProvider { + // ...other methods... + + submitTx(tx: FinalizedTransaction): Promise { + return this.wallet.submitTransaction(tx); + } +} +``` + +A single instance is then passed as both `walletProvider` and `midnightProvider` in the providers object. + +### Assemble the providers object {#assemble-the-providers-object} + +Once you have initialized each provider, assemble them into the `MidnightProviders` object and pass it to your smart contract API. + +```typescript +import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider'; +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'; + +const zkConfigProvider = new NodeZkConfigProvider('/path/to/contract/managed/my-contract'); + +const walletProvider = new MyWalletProvider(wallet, zswapSecretKeys, dustSecretKey); + +const providers: MyProviders = { + privateStateProvider: levelPrivateStateProvider({ + privateStateStoreName: 'my-contract-private-state', + signingKeyStoreName: 'my-contract-private-state-signing-keys', + privateStoragePasswordProvider: () => 'your-encryption-password', + }), + publicDataProvider: indexerPublicDataProvider( + 'https://indexer.preprod.midnight.network/api/v4/graphql', + 'wss://indexer.preprod.midnight.network/api/v4/graphql/ws', + ), + zkConfigProvider, + proofProvider: httpClientProofProvider('http://localhost:6300', zkConfigProvider), + walletProvider, + midnightProvider: walletProvider, +}; +``` + +Pass this object to `deployContract` or `findDeployedContract`: + +```typescript +import { deployContract, findDeployedContract } from '@midnight-ntwrk/midnight-js-contracts'; + +// Deploy a new contract +const deployed = await deployContract(providers, { + compiledContract: CompiledMyContract, + privateStateId: myPrivateStateKey, + initialPrivateState: myInitialPrivateState, +}); + +// Or connect to an existing one +const deployed = await findDeployedContract(providers, { + contractAddress, + compiledContract: CompiledMyContract, + privateStateId: myPrivateStateKey, + initialPrivateState: myInitialPrivateState, +}); +``` + +The `deployContract` and `findDeployedContract` functions take the `MidnightProviders` object and the contract details as parameters. + +:::note Note + +For more information on using these functions, see the [Midnight.js SDK](/sdks/official/midnight-js) documentation. + +::: + +## Deploy to Preprod {#deploy-to-preprod} + +With a local run under your belt and the providers understood, the last stage is deploying to a public testnet. The [Hello World tutorial](../getting-started/hello-world) deploys the contract to a Docker-based local devnet with pre-funded wallets. In this section, you deploy the same `example-hello-world` contract to **Preprod**. After deployment, the contract is visible on the Preprod block explorer and reachable from any Preprod indexer. + +Before you begin, ensure you have: + +- Completed the [Hello World tutorial](../getting-started/hello-world) against the local devnet (`yarn test:local` passing). +- A clone of [`example-hello-world`](https://github.com/midnightntwrk/example-hello-world) with the contract already compiled (`contracts/managed/hello-world/` populated). +- A Midnight-compatible wallet (such as Lace or 1AM) configured for the Preprod network, or an existing 24-word mnemonic / 64-character hex seed you control. +- Docker engine running (the proof server runs as a container). + + + + +### Generate a wallet {#generate-a-wallet} + +You need a wallet on the Preprod network to sign the deployment and call transactions. The test suite accepts either a 24-word BIP-39 mnemonic or a 64-character hex seed; pick whichever your wallet exports. + +**Using a Midnight-compatible wallet** (such as Lace or 1AM): +1. In your wallet, switch the network to **Preprod** and create a new wallet. Save the seed phrase securely. +2. Note your **Unshielded** address. You will paste it into the faucet in the next step. + +Paste this seed phrase into `.env.preprod` in step 3. + +**Bring your own key**: Any 24-word BIP-39 mnemonic or 64-hex-character seed works. The test suite derives both the shielded and unshielded keys from it. + + + + +### Fund the wallet with tNIGHT and tDUST {#fund-the-wallet-with-tnight-and-tdust} + +Preprod transactions are paid in **tDUST**, which is generated by holding (and delegating) **tNIGHT**. Request tNIGHT from the faucet for your Unshielded address, then delegate it to start generating tDUST. [Fund your wallet](./acquire-tokens) covers both stages and the common faucet issues. Without tDUST the test script fails with `Wallet.InsufficientFunds`. + +:::tip Verify balances before continuing +Confirm both tNIGHT and tDUST balances in your wallet before running the test. Wait until the tDUST balance is non-zero. DUST generation begins only after the network confirms the delegation transaction on-chain. +::: + + + + +### Configure `.env.preprod` {#configure-envpreprod} + +The repo ships with an example file. From the repository root: + +```bash +cp .env.preprod.example .env.preprod +``` + +Open `.env.preprod` and **set only one** of the two variables. Delete the other line entirely. Defining both raises an error. + +```bash +# Use this line if your wallet exports a 24-word phrase: +MIDNIGHT_PREPROD_MNEMONIC=word1 word2 word3 ... word24 + +# Or this line if you have a raw seed (hex, no 0x prefix): +MIDNIGHT_PREPROD_SEED=abcd1234... # 64 hex characters +``` + +:::warning Keep `.env.preprod` private +The file contains the secret that controls your Preprod wallet. It is already listed in `.gitignore`; do not commit it, share it, or paste it into chat. +::: + + + + +### Start the proof server {#start-the-proof-server} + +The [proof server](./development-environment#run-the-proof-server) generates the zero-knowledge proofs the test submits to the network. On Preprod you only need the proof server itself, not the rest of the local devnet stack (the test connects to the public Preprod endpoints, so no local node or indexer is required). + +In a separate terminal, from the project root: + +```bash +yarn proof:up +``` + +This starts only the `proof-server` service from `compose.yml` and waits for it to become healthy on `http://127.0.0.1:6300`. + + + + +### Run the test against Preprod {#run-the-test-against-preprod} + +Back in your main terminal: + +```bash +yarn test:preprod +``` + +The script: + +1. Build a wallet from the secret in `.env.preprod`. +2. Sync the wallet against the Preprod indexer. This can take a long time on first run (the default sync timeout is 60 minutes for remote networks, versus 10 for local). The log emits one line per state emission so you can watch progress. +3. Verify funds are available and register the wallet for DUST generation if not already registered. +4. Deploy the `hello-world` contract and submit a call to `storeMessage("Hello World!")`. + +A successful run ends with output similar to: + +```bash +INFO: Wallet sync complete after 23 emissions +INFO: Wallet NIGHT balance on 'preprod': ... +INFO: Providers initialized on 'preprod'. Ready to test! +INFO: Creating private state... +INFO: Setting the contract address... +INFO: Contract deployed at: bba6579743ae23b44301d4a9f8df30dbd5244d63a59d8fbc2c9fc7ea521a04f8 + ✓ src/test/hw.test.ts (2 tests) + ✓ Hello World Contract (preprod) > Deploys the contract + ✓ Hello World Contract (preprod) > Stores Hello World! +``` + + + + +### Verify on the block explorer {#verify-on-the-block-explorer} + +Copy the contract address from the log line `Contract deployed at: ...` and look it up on a Preprod explorer: + +- [preprod.midnightexplorer.com](https://preprod.midnightexplorer.com/) +- [midnight-preprod.subscan.io](https://midnight-preprod.subscan.io/) + +The explorer shows the deploy transaction and the subsequent call transaction. + + + + +### Shut down {#shut-down} + +When you're done, stop the proof server: + +```bash +yarn proof:down +``` + +The container exits but its image stays cached for the next run. + + + + +## Deploy to Preview {#deploy-to-preview} + +The repository supports Preview with the same pattern: + +1. Copy `.env.preview.example` to `.env.preview` and fill in `MIDNIGHT_PREVIEW_MNEMONIC` or `MIDNIGHT_PREVIEW_SEED`. +2. Fund the wallet from the [Preview faucet](https://midnight-tmnight-preview.nethermind.dev/) and delegate for tDUST. +3. Run `yarn test:preview`. + +See [Environments and endpoints](../relnotes/network) for the full list of network URLs and explorers. + +## Troubleshoot {#troubleshoot} + +### Local network issues {#local-network-issues} + +#### Port already in use {#port-already-in-use} + +``` +Error: Bind for 0.0.0.0:9944 failed: port is already allocated +``` + +Another process or a previous run is using the port. Stop it: + +```bash +docker compose -f standalone.yml down +# or find and kill the process +lsof -i :9944 +``` + +#### Invalid wallet address {#invalid-wallet-address} + +If you receive an error similar to this: + +``` +Operation failed: Expected undeployed address, got Preprod address +``` + +It means you're trying to fund a wallet with a Preprod address. You need to use the Undeployed network address instead. + +If you're using the Lace wallet, then you must configure the wallet to use the Undeployed network. + +1. In the Lace wallet extension, click the **Settings** icon and then select the **Network** tab. +2. Under the Midnight section, select the **Undeployed** network and then click **Confirm**. + +![Lace wallet network settings with the Undeployed network selected](./img/set-undeployed-network.png) + +After that, Lace displays your wallet addresses for the local Undeployed network. Make sure to use the Unshielded wallet address for the local Undeployed network. + +![Lace wallet addresses with the Unshielded undeployed address highlighted](./img/unshielded-undeployed-address.png) + +#### Containers not starting {#containers-not-starting} + +Check Docker is running and you have access to the Midnight Docker registry: + +```bash +docker compose -f standalone.yml pull +docker compose -f standalone.yml up +``` + +Watch the logs for specific errors: + +```bash +docker compose -f standalone.yml logs -f node +docker compose -f standalone.yml logs -f indexer +docker compose -f standalone.yml logs -f proof-server +``` + +#### Wallet sync takes too long {#wallet-sync-takes-too-long} + +The indexer needs time to catch up with the node after startup. If the sync seems stuck: + +1. Check the indexer logs: `docker compose -f standalone.yml logs -f indexer` +2. Verify the node is producing blocks: `curl http://localhost:9944/health` +3. Set `DEBUG_LEVEL=debug` in `.env` for more detailed wallet logs + +### Preprod deployment issues {#preprod-deployment-issues} + +**`Wallet.InsufficientFunds`**: The wallet has no spendable tDUST. Confirm in your wallet that the tDUST balance is non-zero (not just tNIGHT). DUST generation only starts after the network confirms the delegation transaction. + +**Sync never completes**: Preprod sync from a brand-new wallet can be slow. If you hit the default 60-minute timeout, raise it with the `MIDNIGHT_SYNC_TIMEOUT_MS` environment variable: + +```bash +MIDNIGHT_SYNC_TIMEOUT_MS=7200000 yarn test:preprod # 2 hours +``` + +**`Set only one of MIDNIGHT_PREPROD_MNEMONIC or MIDNIGHT_PREPROD_SEED`**: You defined both variables in `.env.preprod`. Delete one. + +**Proof server unreachable**: Check that `yarn proof:up` finished and `http://127.0.0.1:6300` responds. If port 6300 is in use, stop the conflicting process or change the host port in `compose.yml`. + +## Next steps {#next-steps} + +Your contract is live on a public testnet. From here the journey continues in two directions: + +- [Work with your compiled contract](./work-with-compiled-contract): implement witnesses, call circuits, and write unit tests against the generated JavaScript implementation. +- Give your DApp a user interface by [connecting a wallet from your frontend](./connect-wallet-frontend). diff --git a/docs/guides/making-decision-on-contract-updatability.mdx b/docs/guides/making-decision-on-contract-updatability.mdx index 1217d6892..85a6378ca 100644 --- a/docs/guides/making-decision-on-contract-updatability.mdx +++ b/docs/guides/making-decision-on-contract-updatability.mdx @@ -1,9 +1,9 @@ --- SPDX-License-Identifier: Apache-2.0 copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -sidebar_label: "Making decision on contract updatability" +sidebar_label: "Plan for contract updatability" description: Learn about Compact contracts updatability -sidebar_position: 40 +sidebar_position: 7 --- # Making decision on contract updatability @@ -135,7 +135,7 @@ property. ## Example The following example demonstrates how to perform maintenance operations on a deployed contract. -See the [deploy guide](./deploy-mn-app) for how to set up `providers` and `compiledContract`. +See the [deploy guide](./local-to-testnet#deploy-to-preprod) for how to set up `providers` and `compiledContract`. ```typescript import { findDeployedContract } from "@midnight-ntwrk/midnight-js-contracts"; diff --git a/docs/guides/midnight-local-network.mdx b/docs/guides/midnight-local-network.mdx deleted file mode 100644 index 65bd4602d..000000000 --- a/docs/guides/midnight-local-network.mdx +++ /dev/null @@ -1,282 +0,0 @@ ---- -title: Midnight local network -description: Learn how to set up a local Midnight network for development and testing. -sidebar_position: 04 -sidebar_label: Midnight local network -tags: [local, network, testing] ---- - -# Midnight local network - -The local network is a standalone tool for running a local Midnight development network and funding test wallets. - -This guide walks you through the process of setting up a local Midnight network and funding test wallets in the undeployed network. - -## Prerequisites - -Before you proceed, ensure you have:: - -- [Docker Desktop](https://docs.docker.com/get-docker/) -- Node.js >= 22.0.0 - -## Installation - -To get started, clone the [Midnight local network repository](https://github.com/midnightntwrk/midnight-local-dev): - -```bash -git clone https://github.com/midnightntwrk/midnight-local-dev.git -cd midnight-local-dev -``` - -Install the dependencies: - -```bash -npm install -``` - -## Network services - -The local network runs three Docker containers on the following ports: - -| Service | Container Name | Port | URL | -|:---:|:---:|:---:|:---:| -| **Midnight Node** | `midnight-node` | `9944` | `http://localhost:9944` | -| **Indexer** (GraphQL) | `midnight-indexer` | `8088` | `http://localhost:8088/api/v4/graphql` | -| **Indexer** (WebSocket) | `midnight-indexer` | `8088` | `ws://localhost:8088/api/v4/graphql/ws` | -| **Proof Server** | `midnight-proof-server` | `6300` | `http://localhost:6300` | - -All services use the `undeployed` network ID with the `dev` node preset. - -### Docker images - -The `standalone.yml` file in the root of the repository defines the Docker images and specific versions pulled when you start the local network. - -| Service | Image | Version | -|:---:|:---:|:---:| -| Node | `midnightntwrk/midnight-node` | `0.22.3` | -| Indexer | `midnightntwrk/indexer-standalone` | `4.0.1` | -| Proof Server | `midnightntwrk/proof-server` | `8.0.3` | - -### Wallet SDK packages - -The local network uses the following Wallet SDK packages: - -| Package | Version | -|:---:|:---:| -| `@midnight-ntwrk/wallet-sdk-facade` | 3.0.0 | -| `@midnight-ntwrk/wallet-sdk-abstractions` | 2.0.0 | -| `@midnight-ntwrk/wallet-sdk-shielded` | 2.1.0 | -| `@midnight-ntwrk/wallet-sdk-dust-wallet` | 3.0.0 | -| `@midnight-ntwrk/wallet-sdk-unshielded-wallet` | 2.1.0 | -| `@midnight-ntwrk/wallet-sdk-address-format` | 3.1.0 | -| `@midnight-ntwrk/wallet-sdk-hd` | 3.0.1 | -| `@midnight-ntwrk/ledger-v8` | 8.0.3 | -| `@midnight-ntwrk/midnight-js-network-id` | 4.0.2 | - -## Start the local network - -Once the installation is complete, start the local network by running the following command: - -```bash -npm start -``` - -This single command does the following: - -- **Pull** the latest Docker images for the Midnight node, indexer, and proof server. -- **Start** all three containers with health checks. -- **Initialize** the genesis master wallet (seed `0x00...001`) which holds all minted NIGHT tokens. -- **Register DUST** for the master wallet (required to pay transaction fees). -- **Display** the master wallet balance. -- **Present an interactive menu** for funding wallets in the undeployed network. - -Once running, you'll see: - -``` -Choose an option: - [1] Fund accounts from config file (NIGHT + DUST registration) - [2] Fund accounts by public key (NIGHT transfer only) - [3] Display master wallet balances - [4] Exit -> -``` - -## Fund wallets - -The local network provides two options for funding wallets: - -- Fund from a config file -- Fund by public key - -Each funding operation transfers _50,000 tNIGHT_ from the genesis master wallet. You can fund up to _10 accounts_ per operation. - -### Option 1: Fund from a config file - -The config option handles wallet operations such as generating the wallet seed from mnemonic, transferring tNIGHT to the unshielded address, syncing the wallet, and registering tNIGHT for DUST generation. - -It pulls wallet information from a config file, which is a JSON file with the following structure: - -```json -{ - "accounts": [ - { - "name": "Alice", - "mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" - } - ] -} -``` -Here's a breakdown of the fields: - -| Field | Type | Description | -|:---:|:---:|:---:| -| `accounts` | `array` | List of accounts to fund (max 10) | -| `accounts[].name` | `string` | Display name for logging | -| `accounts[].mnemonic` | `string` | BIP39 mnemonic phrase (24 words) | - -An example file is provided at `accounts.example.json`. Copy the file to `accounts.json` and edit it to add your wallet mnemonics. - -```bash -cp accounts.example.json accounts.json -``` - -After editing the file, select option `[1] Fund accounts from config file (NIGHT + DUST registration)` from the interactive menu. - -You are prompted to enter the path to the config file. - -``` -> 1 -Path to accounts JSON file: ./accounts.json -``` - -The tool funds the wallets from the config file. - -### Option 2: Fund by public key - -The public key option allows you to fund wallets by their Bech32 addresses. You can fund up to 10 wallets at a time. - -Select option `[2] Fund accounts by public key (NIGHT transfer only)` from the interactive menu. - -You are prompted to enter the Bech32 addresses of the wallets you want to fund, separated by commas. - -``` -> 2 -Enter Bech32 addresses (comma-separated): mn1q..., mn1q... -``` - -Each address receives _50,000 tNIGHT_. Recipients must register for DUST generation themselves before they can pay transaction fees. - -## Connect a DApp - -Once the network is running, any Midnight DApp can connect using the standard localhost endpoints. - -Here's an example of how to connect a DApp to the local network: - -```typescript -import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id'; - -setNetworkId('undeployed'); - -const config = { - indexer: 'http://127.0.0.1:8088/api/v4/graphql', - indexerWS: 'ws://127.0.0.1:8088/api/v4/graphql/ws', - node: 'http://127.0.0.1:9944', - proofServer: 'http://127.0.0.1:6300', - networkId: 'undeployed', -}; - -// Use these endpoints with the Midnight wallet SDK, contract deployment, and other DApp operations -``` - -## Run the network standalone - -If you only need the Docker containers running without the interactive menu for wallet initialization or funding, then you can use Docker Compose directly: - -```bash -docker compose -f standalone.yml up -d -``` - -To check the status of the containers: - -```bash -docker compose -f standalone.yml ps -``` - -To view the logs: - -```bash -docker compose -f standalone.yml logs -f -``` - -This is useful when: -- Your DApp handles its own wallet initialization and funding. -- You want to keep the network running across multiple test sessions. -- You're debugging container-level issues. - -:::note -In this mode, you must handle genesis wallet funding and DUST registration yourself. -::: - -## Troubleshoot - -Below are some of the common issues that you might encounter and how to fix them. - -### Port already in use - -``` -Error: Bind for 0.0.0.0:9944 failed: port is already allocated -``` - -Another process or a previous run is using the port. Stop it: - -```bash -docker compose -f standalone.yml down -# or find and kill the process -lsof -i :9944 -``` - -### Invalid wallet address - -If you receive an error similar to this: - -``` -Operation failed: Expected undeployed address, got Preprod address -``` - -It means you're trying to fund a wallet with a Preprod address. You need to use the Undeployed network address instead. - -If you're using the Lace wallet, then you must configure the wallet to use the Undeployed network. - -1. In the Lace wallet extension, click the **Settings** icon and then select the **Network** tab. -2. Under the Midnight section, select the **Undeployed** network and then click **Confirm**. - -After that, you'll see your wallet addresses for the local Undeployed network. Make sure to use the Unshielded wallet address for the local Undeployed network. - -### Containers not starting - -Check Docker is running and you have access to the Midnight Docker registry: - -```bash -docker compose -f standalone.yml pull -docker compose -f standalone.yml up -``` - -Watch the logs for specific errors: - -```bash -docker compose -f standalone.yml logs -f node -docker compose -f standalone.yml logs -f indexer -docker compose -f standalone.yml logs -f proof-server -``` - -### Wallet sync takes too long - -The indexer needs time to catch up with the node after startup. If the sync seems stuck: - -1. Check the indexer logs: `docker compose -f standalone.yml logs -f indexer` -2. Verify the node is producing blocks: `curl http://localhost:9944/health` -3. Set `DEBUG_LEVEL=debug` in `.env` for more detailed wallet logs - -## Next steps - -Now that you have a local network running, you can start building your DApp. See our guide to learn how to [deploy a contract](./deploy-mn-app) to the Midnight blockchain. diff --git a/docs/guides/nextjs-wallet-connect.mdx b/docs/guides/nextjs-wallet-connect.mdx deleted file mode 100644 index 5fbfcfd26..000000000 --- a/docs/guides/nextjs-wallet-connect.mdx +++ /dev/null @@ -1,304 +0,0 @@ ---- -title: Create a Next.js wallet connector -description: Learn how to create a Next.js application that connects to a wallet using the Midnight DApp Connector API. -sidebar_position: 55 -sidebar_label: Create a Next.js wallet connector -tags: [wallet, nextjs, tutorial] -# slug: /how-to/nextjs-wallet-connect -redirects: - - /how-to/nextjs-wallet-connect -toc_max_heading_level: 2 ---- - -import Step, { StepsProvider } from "@site/src/components/Step/Step"; - -This guide explains how to create a Next.js application that connects to the Midnight Lace wallet using the DApp Connector API. -You'll build a wallet connection button that displays connection status, providing a foundation for building more complex DApps. - -The code examples focus on core functionality and intentionally omit CSS styling. You can add your preferred styling solution such as Tailwind, styled-components, or CSS modules to match your application's design. - -## Prerequisites - -Before you begin, make sure you have: - -- Basic knowledge of TypeScript and JavaScript -- Familiarity with React and Next.js fundamentals -- Node.js and npm installed on your system -- [Midnight Lace wallet extension](https://chromewebstore.google.com/detail/lace/gafhhkghbfjjkeiendhlofajokpaflmk) installed in your browser - -### Set up a Next.js project - -If you don't have a Next.js project yet, create one using the following command: - -```bash -npm create-next-app@latest -``` - -When prompted, select the following options: -- **TypeScript**: Yes -- **ESLint**: Yes -- **Tailwind CSS**: Yes (optional, but recommended) -- **App Router**: Yes -- **Other options**: Choose based on your preference - -Then navigate to the project directory and install the DApp Connector API package: - -```bash -cd -npm install @midnight-ntwrk/dapp-connector-api -``` - -:::note -This guide uses the DApp Connector API v4.0.0. For more information, see the [DApp Connector API documentation](/api-reference/dapp-connector). -::: - -After completing this tutorial, you'll understand: - -- How to integrate wallet connections in Next.js applications -- The differences between client and server components when working with wallets -- How to manage wallet state in Next.js -- Best practices for using the DApp Connector API in Next.js - - - - -## Create the wallet connection component - -You'll build a client-side component that handles wallet connection. Since wallet interactions require browser APIs, this component must run on the client side using Next.js's `"use client"` directive. - -Create `app/components/ConnectWalletButton.tsx`: - -```typescript -"use client"; // Next.js directive for client-side rendering - -import { useState } from "react"; -import "@midnight-ntwrk/dapp-connector-api"; -import type { InitialAPI } from "@midnight-ntwrk/dapp-connector-api"; - -export default function ConnectWalletButton() { - const [connected, setConnected] = useState(false); - const [walletAddress, setWalletAddress] = useState(null); - - const handleConnect = async () => { - try { - // Access the Midnight Lace wallet through the window object - const wallet: InitialAPI = await window.midnight!.mnLace; - - // Connect to the specified network (use 'undeployed' for local development) - const connectedApi = await wallet.connect('preprod'); - - // Retrieve the shielded addresses from the wallet - const addresses = await connectedApi.getShieldedAddresses(); - const address = addresses.shieldedAddress; - - // Check if the connection is established - const connectionStatus = await connectedApi.getConnectionStatus(); - - if (connectionStatus) { - setConnected(true); - setWalletAddress(address); - console.log("Connected to wallet:", address); - } - } catch (error) { - console.log("Failed to connect:", error); - } - }; - - const handleDisconnect = () => { - setConnected(false); - setWalletAddress(null); - }; - - return ( - - ); -} -``` - -This component manages the wallet connection flow: - -1. **Client-side rendering**: The `"use client"` directive ensures this component runs in the browser where wallet APIs are available. -2. **State management**: Uses React's `useState` hook to track connection status and wallet address. -3. **Connection logic**: The `handleConnect` function accesses the wallet through `window.midnight.mnLace`, connects to the specified network, and retrieves the wallet's shielded address. -4. **User feedback**: Displays the wallet address (truncated) and provides connect/disconnect actions. - - - - -## Add the component to your layout - -Now integrate the wallet button into your application's layout so it appears on every page. - -Update `app/layout.tsx`: - -```typescript -import type { Metadata } from "next"; -import "./globals.css"; -import ConnectWalletButton from "./components/ConnectWalletButton"; - -export const metadata: Metadata = { - title: "Midnight Wallet Connector", - description: "Connect to Midnight Lace wallet", -}; - -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( - - - -
- {children} -
- - - ); -} -``` - -The `ConnectWalletButton` component now appears at the top of every page in your application. Next.js's layout system makes it easy to create persistent UI elements across routes. - -
- - -## Create a welcome page - -Create a simple landing page that encourages users to connect their wallet. - -For this, replace the content of `app/page.tsx` with the following: - -```typescript -export default function Home() { - return ( -
-

- Welcome to Midnight -

-

- Connect your Lace wallet to get started with privacy-preserving - decentralized applications on the Midnight Network. -

-
-

- Click the "Connect Wallet" button in the top right corner to authorize - this application to access your Midnight Lace wallet. -

-
-
- ); -} -``` - -
- - -## Run your application - -Start the Next.js development server: - -```bash -npm run dev -``` - -Open your browser and navigate to `http://localhost:3000`. - -When you click **Connect Wallet**, the Midnight Lace wallet extension prompts you to authorize the connection. - -![Connect Wallet](./img/connect-wallet-next.png) - -The wallet asks you to choose your preferred authorization level: - -- **Always**: Grants persistent authorization. The application remains authorized even after closing your browser, and you won't need to reconnect on future visits. -- **Only once**: Grants temporary authorization. You must reauthorize the connection each time you visit the application. - -![Authorize Connection](./img/dapp-connection-option.png) - -After approval, the button changes to "Disconnect" and displays your truncated wallet address. - -![Connected Wallet](./img/connected-wallet-next.png) - - - - -## Verify the connection - -You can verify that your wallet is connected to the application: - -1. Open the Midnight Lace wallet extension in your browser. -2. Click on your wallet name in the top right corner, then select **Settings**. -3. Navigate to **Authorized DApps**. - -You should see `http://localhost:3000` listed as an authorized application. - -![Authorized DApps](./img/authorized-dapps.png) - -You can revoke access at any time from this panel by clicking the **trash** icon next to the application. - - -
- -## Troubleshooting - -These are some of the common issues you might encounter and how to resolve them. - -### "window is not defined" error - -This error occurs because Next.js tries to render components on the server by default, but wallet APIs only exist in the browser environment. - -**How to fix it**: Make sure your wallet component includes the `"use client"` directive at the top of the file. - -### Wallet not detected - -This error means the browser cannot access `window.midnight`, which indicates the Midnight Lace wallet extension is not available. - -**How to fix it**: - -- Verify the Midnight Lace wallet extension is installed and enabled. -- Refresh the page after installing the extension. -- Check the browser console for extension-related errors. -- Ensure you're testing in a browser, not during server-side rendering. - -### Connection fails - -This error occurs when the wallet connection attempt is unsuccessful, which can happen for several reasons related to configuration or wallet state. - -**How to fix it**: - -- Confirm that you're using the correct network ID. -For local development, use `'undeployed'`. For Preprod environment, use `'preprod'`. -- Make sure the Lace wallet is unlocked. -- Check the browser console for specific error messages. -- Verify the DApp Connector API package is correctly installed. - -## Next steps - -Now that you have a working wallet connector in Next.js, you can extend your application: - -- **Create protected routes**: Use Next.js middleware to restrict access to pages that require wallet connection. -- **Transfer coins**: Build a form that allows users to send tokens to other addresses. -- **Display balances**: Show the user's token balances on a dashboard page. -- **Transaction history**: Create a page that queries and displays transaction history. - -## Reference - -- [DApp Connector API documentation](/api-reference/dapp-connector) -- [Next.js documentation](https://nextjs.org/docs) -- [Midnight Lace wallet](https://chromewebstore.google.com/detail/lace/gafhhkghbfjjkeiendhlofajokpaflmk) \ No newline at end of file diff --git a/docs/guides/query-the-blockchain.mdx b/docs/guides/query-the-blockchain.mdx deleted file mode 100644 index 8b26c6076..000000000 --- a/docs/guides/query-the-blockchain.mdx +++ /dev/null @@ -1,127 +0,0 @@ ---- -SPDX-License-Identifier: Apache-2.0 -copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -title: How to query the blockchain -sidebar_label: How to query the blockchain -description: A guide on how to query the Midnight blockchain. -keywords: [midnight, blockchain, query] -sidebar_position: 47 -tags: [midnight, javascript] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -This guide walks you through connecting to the Midnight blockchain and listening for new blocks. -It also covers extracting data from these blocks using the Polkadot API and the various methods it provides to connect to Substrate-based blockchain networks. - -## Prerequisites - -Before you start, make sure you have the following: - -- [Node.js](https://nodejs.org/en/download/) installed on your machine. -- Basic knowledge of TypeScript. - -## Connect to the Midnight blockchain - -The [`@polkadot/api`](https://polkadot.js.org/docs/api/) library includes a WebSocket provider (`WsProvider`) to connect to Substrate-based blockchain networks, including Midnight. - -### Installation - -Install the dependencies: - - - - ```bash - npm install @polkadot/api - ``` - - - ```bash - yarn add @polkadot/api - ``` - - - -### Connect to the Midnight node - -Use the `WsProvider` to connect to a Midnight node using the RPC endpoint: - -```typescript -import { ApiPromise, WsProvider } from '@polkadot/api'; - -// Define the WebSocket endpoint for the Midnight node -const wsProvider = new WsProvider('wss://rpc.preview.midnight.network'); - -// Create the API instance -const api = await ApiPromise.create({ provider: wsProvider }); -``` - -:::note - -The PRC endpoint in the example above is for the preview network. However, you can use the same code and update the endpoint to match the -environment you intend to connect to. Supported environments are: -- **Preview**: `wss://rpc.preview.midnight.network` -- **Preprod**: `wss://rpc.preprod.midnight.network` -- **Mainnet**: `wss://rpc.mainnet.midnight.network` - -For more information on the supported environments, see the [Environments and endpoints](../relnotes/network) reference. - -::: - -## Listen for new blocks - -Once connected, you can subscribe to new block headers to listen for new blocks: - -```typescript -api.rpc.chain.subscribeNewHeads((lastHeader) => { - console.log(`New block #${lastHeader.number} has been added`); -}); -``` - -This function logs the block number each time a new block is added to the chain. - -## Extract data from blocks - -To extract detailed information from each new block, such as transactions and events, use the snippet below: - -```typescript -api.rpc.chain.subscribeNewHeads(async (lastHeader) => { - const blockHash = await api.rpc.chain.getBlockHash(lastHeader.number); - const signedBlock = await api.rpc.chain.getBlock(blockHash); - const allEvents = await api.query.system.events.at(blockHash); - - console.log(`nBlock #${lastHeader.number}`); - - signedBlock.block.extrinsics.forEach((extrinsic, index) => { - console.log(`nExtrinsic ${index}: ${extrinsic.method.section}.${extrinsic.method.method}`); - console.log(`Arguments: ${extrinsic.args.map((arg) => arg.toString()).join(', ')}`); - }); - - allEvents.forEach(({ event, phase }, index) => { - console.log(`nEvent ${index}: ${event.section}.${event.method}`); - console.log(`Phase: ${phase.toString()}`); - console.log(`Data: ${event.data.toString()}`); - }); -}); -``` - -The code above performs the following: -- Subscribes to new block headers -- Retrieves the full block and its associated events -- Logs detailed information about each extrinsic (transaction) and event - -## What is an extrinsic? - -An extrinsic is a piece of data sent from outside the blockchain into it, asking the network to perform an action. -The term is often used in Substrate-based blockchains to refer to transactions. - -There are three main types of extrinsics: - -- **Signed extrinsics**: These are transactions submitted by users that include a digital signature such as transferring tokens or interacting with smart contracts. -- **Unsigned extrinsics**: These are submitted without a signature and are typically used for operations that don't require user authentication. -- **Inherent extrinsics**: These are used for system-level operations, such as setting the timestamp for a new block. The blockchain usually generates them itself. - -## Next steps - -You now know how to connect to the Midnight blockchain and listen for new blocks. For more information on running a node, see the [Node RPC](../nodes/rpc-node) guide. \ No newline at end of file diff --git a/docs/guides/react-wallet-connect.mdx b/docs/guides/react-wallet-connect.mdx deleted file mode 100644 index 4a70dde70..000000000 --- a/docs/guides/react-wallet-connect.mdx +++ /dev/null @@ -1,341 +0,0 @@ ---- -title: Create a React wallet connector -description: Learn how to create a React application that connects to a wallet using the Midnight DApp Connector API. -sidebar_position: 50 -sidebar_label: Create a React wallet connector -tags: [wallet, react, tutorial] -# slug: /how-to/react-wallet-connect -redirects: - - /how-to/react-wallet-connect -toc_max_heading_level: 2 ---- - -import Step, { StepsProvider } from "@site/src/components/Step/Step"; - -In this guide, you'll create a React application that connects to a Midnight wallet using the DApp Connector API. -You'll build a simple interface that displays connection status and wallet addresses, providing a foundation for more complex decentralized applications. - -The code examples focus on core functionality and intentionally omit CSS styling. You can add your preferred styling solution (Tailwind, styled-components, CSS modules, etc.) to match your application's design. - -## Prerequisites - -Before you begin, make sure you have: - -- Basic knowledge of TypeScript and JavaScript -- Familiarity with React fundamentals (components, state, hooks) -- Node.js and npm installed on your system -- A Midnight wallet extension installed in your browser - -### Set up a React project - -If you don't have a React project yet, create one using Vite: - -```bash -npm create vite@latest my-wallet-app -- --template react-ts -cd my-wallet-app -``` - -Then install the DApp Connector API package: - -```bash -npm install @midnight-ntwrk/dapp-connector-api -``` - -After completing this tutorial, you'll understand: - -- The Midnight wallet connection flow -- How to use the DApp Connector API -- Best practices for managing wallet state in React applications - - - - -## Define TypeScript interfaces - -Start by creating type definitions for your components. These interfaces provide type safety and clear contracts between components. - -Create a new file `types.ts` within the `src` directory and add the following code: - -```typescript -export interface WalletCardProps { - isConnected: boolean; - walletAddress: string | null; - onConnect: () => void; - onDisconnect: () => void; -} -``` - -The `WalletCardProps` interface defines the shape of data your `WalletCard` component will receive. -It includes the connection state, the wallet address, and callback functions for connect and disconnect actions. - - - - -## Create the WalletCard component - -Now you'll build the `WalletCard` component, which displays the wallet connection status and provides connect/disconnect buttons. - -Create `WalletCard.tsx` within the `src` directory and add the following code: - -```typescript -import React from "react"; -import type { WalletCardProps } from "./types"; - -const WalletCard: React.FC = ({ - isConnected, - walletAddress, - onConnect, - onDisconnect, -}) => { - return ( -
-
-

Connection Status

-
- {isConnected ? "Connected" : "Disconnected"} -
-
- -
- {isConnected && walletAddress ? ( - <> -

Wallet Address:

-

{walletAddress}

- - ) : ( -

Please connect your wallet to proceed.

- )} -
- -
- {isConnected ? ( - - ) : ( - - )} -
-
- ); -}; - -export default WalletCard; -``` - -This component handles the presentation layer of your wallet connection. It shows the current status, -displays the address when connected, and provides appropriate action buttons based on the connection state. - -
- - -## Select a wallet - -Wallets inject their Initial API instances under the global `window.midnight` object, each keyed by a freshly generated UUID rather than a fixed name. A single browser may also have more than one wallet available at once, for example when two wallets are loaded from the same seed phrase. Because of this, you read the entries on `window.midnight` and choose one, rather than reaching for a hardcoded key. - -Create a new file `selectWallet.ts` within the `src` directory and add the following code: - -```typescript -import type { InitialAPI } from '@midnight-ntwrk/dapp-connector-api'; - -export const listWallets = (): InitialAPI[] => { - const injected = window.midnight; - return injected ? Object.values(injected) : []; -}; - -export const selectWallet = (): InitialAPI => { - const wallets = listWallets(); - - if (wallets.length === 0) { - throw new Error('No Midnight wallet found. Please install a Midnight wallet extension.'); - } - - return wallets[0]; -}; -``` - -The `selectWallet` function reads the installed wallets and returns the first one, which keeps this example focused on the connection flow. When more than one wallet is available, the DApp Connector API specification expects you to let the user choose rather than picking for them. Use `listWallets` to render the options, and when displaying each wallet, render its `name` and `icon` safely to prevent XSS. - - - - -## Integrate the DApp Connector API - -Next, we'll use the `App` component to manage the wallet connection logic using the DApp Connector API and the `selectWallet` helper from the previous step. - -Create or open `App.tsx` within the `src` directory and replace the existing code with the following: - -```typescript App.tsx -import React, { useState } from 'react'; -import WalletCard from './WalletCard'; -import '@midnight-ntwrk/dapp-connector-api'; -import { selectWallet } from './selectWallet'; - -const App: React.FC = () => { - const [isConnected, setIsConnected] = useState(false); - const [walletAddress, setWalletAddress] = useState(null); - - const handleConnect = async () => { - console.log('Connect button clicked'); - let isConnected = false; - let address = null; - - try { - const wallet = selectWallet(); - - // Connect to the specified network (use 'undeployed' for local development) - const connectedApi = await wallet.connect('preprod'); - - // Retrieve the unshielded address from the wallet - const { unshieldedAddress } = await connectedApi.getUnshieldedAddress(); - address = unshieldedAddress; - - // Optional: Get the service URI configuration - const serviceUriConfig = await connectedApi.getConfiguration(); - console.log('Service URI Config:', serviceUriConfig); - - // Check if the connection is established - const connectionStatus = await connectedApi.getConnectionStatus(); - if (connectionStatus.status === 'connected') { - isConnected = true; - console.log("Connected to the wallet:", address); - } - } catch (error) { - console.log("An error occurred:", error); - } - - setIsConnected(isConnected); - setWalletAddress(address); - }; - - const handleDisconnect = () => { - setWalletAddress(null); - setIsConnected(false); - }; - - return ( -
-
-

Midnight Wallet Connector

-
-
- -
-
- ); -}; - -export default App; -``` - -Let's break down the wallet connection process: - -1. **Select a wallet**: The `selectWallet` helper reads the installed wallets from `window.midnight` and returns one to connect to. It throws if no wallet is found, which the surrounding `try/catch` handles. -2. **Connect to network**: Call the `connect()` method and pass the network ID as an argument. -In our example, we used `'preprod'`. You can use `'undeployed'` for local development or `'preview'` for the Preview network. -3. **Retrieve the address**: After connecting to the network, call the `getUnshieldedAddress()` method to get the wallet's unshielded address. Request the shielded address only when your application actually needs it. -4. **Check status**: Verify the connection with `getConnectionStatus()`, which resolves to an object whose `status` is `'connected'` or `'disconnected'`. - -The `handleConnect` event handler manages all these steps and updates your component's state accordingly. -When users click the **Connect Wallet** button, their wallet will prompt them to authorize the connection. - -
- - -## Set up the entry point - -Create the entry point that bootstraps your React application. For this, create or open `main.tsx` within the `src` directory -and replace the existing code with the following: - -```typescript -import { StrictMode } from 'react'; -import { createRoot } from 'react-dom/client'; -import App from './App.tsx'; - -createRoot(document.getElementById('root')!).render( - - - , -); -``` - -Finally, create or open `index.html` in the project root and replace the existing code with the following: - -```html - - - - - - Midnight Wallet Connector - - -
- - - -``` - -
- - -## Run your application - -After setting up the entry point, start your development server: - -```bash -npm run dev -``` - -Now, open your browser and navigate to the local development URL (typically `http://localhost:5173`). - -When you click **Connect Wallet**, your wallet extension will prompt you to authorize the connection. - -![Connect Wallet](./img/connect-wallet.png) - -After approval, your application will display the connection status and your wallet's unshielded address. - -![Connected Wallet](./img/connected-wallet.png) - - -
- -## Troubleshooting - -The following are some common issues you might encounter and how to resolve them. - -### Wallet not detected - -If you see errors about `window.midnight` being undefined, or `No Midnight wallet found`: - -- Verify a Midnight wallet extension is installed and enabled in your browser. -- Refresh the page after installing or enabling the extension. -- Confirm you are reading the wallet from `window.midnight` by enumeration (`Object.values(window.midnight)`) and not from a fixed key such as `window.midnight.mnLace`. Wallets inject their Initial API under a UUID key, so a hardcoded name resolves to `undefined`. -- Check the browser console for any extension-related errors. - -### Connection fails - -If the connection attempt fails: - -- Ensure the network ID specified in the `connect()` method matches the network ID configured in your wallet. -- Check that the wallet is unlocked and synced. -- Review the browser console for specific error messages. -- Verify the DApp Connector API package is correctly installed. - -## Next steps - -Now that you have a working wallet connector, you can extend your application with additional functionality: - -- **Transfer coins**: Implement a form that allows users to send tokens to other addresses. -- **Sign messages**: Add a text input where users can sign arbitrary messages with their wallet. -- **Display balances**: Show the user's token balances for different assets. -- **Transaction history**: Query and display the user's transaction history. -- **Multi-network support**: Add a network selector to switch between different Midnight networks. - -## Reference - -- [DApp Connector API documentation](/api-reference/dapp-connector) -- [Example application with full styling](https://github.com/bochaco/react-mn-wallet-connect) diff --git a/docs/guides/run-proof-server.mdx b/docs/guides/run-proof-server.mdx index 11c34fd4c..af65fdcb5 100644 --- a/docs/guides/run-proof-server.mdx +++ b/docs/guides/run-proof-server.mdx @@ -4,7 +4,7 @@ copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation sidebar_label: "Run the proof server" description: Learn how to run the proof server for the Midnight Network. toc_max_heading_level: 2 -sidebar_position: 35 +sidebar_position: 8 --- # Proof server diff --git a/docs/guides/use-compact-javascript-implementation.mdx b/docs/guides/use-compact-javascript-implementation.mdx deleted file mode 100644 index 05d42d5a8..000000000 --- a/docs/guides/use-compact-javascript-implementation.mdx +++ /dev/null @@ -1,214 +0,0 @@ ---- -SPDX-License-Identifier: Apache-2.0 -copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -description: Learn how to use the Compact JavaScript implementation for the Midnight Network. -sidebar_label: Use the Compact JavaScript implementation -title: Use the Compact JavaScript implementation -sidebar_position: 46 ---- - -# Use the Compact JavaScript implementation - -This guide shows how to use the Compact JavaScript implementation in your development workflow. -Learn how to import the implementation, create a contract instance, and call its functions from a JavaScript or TypeScript environment. - -## Import the implementation - -Once you have compiled your Compact contract, the compiler outputs these key files in the `managed` directory: - -- `index.js`: The JavaScript implementation -- `index.d.ts`: TypeScript type definitions -- `index.js.map`: Source map for debugging - -You can load the implementation like any other ECMAScript (ES) module: - -```typescript -import { Contract, State, pureCircuits, ledger } from './managed/bboard/contract/index.js'; -``` - -If you are using TypeScript, then the accompanying declaration file `index.d.ts` automatically provides type hints for your contract and its methods. - -## Implement witnesses - -Every Compact contract with witness functions requires a witnesses object when instantiated. This object contains implementations for all witness functions declared in your Compact code. - -For the bulletin board contract, create a `witnesses.ts` file in the `contract/src` directory: - -```typescript -import { Ledger } from "./managed/bboard/contract/index.js"; -import { WitnessContext } from "@midnight-ntwrk/compact-runtime"; - -export type BBoardPrivateState = { - readonly secretKey: Uint8Array; -}; - -export const createBBoardPrivateState = (secretKey: Uint8Array) => ({ - secretKey, -}); - -export const witnesses = { - localSecretKey: ({ - privateState, - }: WitnessContext): [ - BBoardPrivateState, - Uint8Array, - ] => [privateState, privateState.secretKey], -}; -``` - -The witnesses object maps witness function names to their implementations. Each witness function receives a `WitnessContext` containing the ledger state, private state, and contract address. The function returns a tuple of the updated private state and the witness value. - -## Call contract circuits - -Each circuit is exposed as a JavaScript function under `contract.circuits` or `contract.impureCircuits`. These wrappers prepare the inputs, run the JavaScript implementation, and return structured results containing the output, updated context, and proof data. - -Here's an example of calling the `post` impure circuit: - -```typescript -const initialContext = { - originalState: { - state: State.VACANT, - message: { is_some: false, value: '' }, - sequence: 1n, - owner: new Uint8Array(32) - }, - privateState: { - secretKey: new Uint8Array(32) - }, - contractAddress: '0x...', - transactionContext: {} -}; - -const message = "Hello from Compact!"; - -const { result, context, proofData, gasCost } = - contract.circuits.post(initialContext, message); -``` - -The returned object contains: -- `result`: The circuit's return value (empty array for post) -- `context`: The updated circuit context with new ledger state -- `proofData`: Data structure containing input, output, and transcripts for proof generation -- `gasCost`: Gas cost tracking information - -Here's an example of calling the `publicKey` pure circuit: - -```typescript -const secretKey = new Uint8Array(32); -const sequenceBytes = new Uint8Array(32); - -const ownerCommitment = pureCircuits.publicKey(secretKey, sequenceBytes); -``` - -Pure circuits can be called directly without a circuit context. They perform deterministic computations and return values immediately. - -## Write unit tests - -Because the Compact implementation is a standard ES module, you can integrate it with testing frameworks such as Vitest, Jest, or Mocha. - -```typescript -import { describe, it, expect } from 'vitest'; -import { Contract, State } from './managed/bboard/contract/index.js'; -import { witnesses, createBBoardPrivateState } from './witnesses.js'; - -describe('Bulletin board contract', () => { - it('accepts a new post on vacant board', () => { - const contract = new Contract(witnesses); - - const context = { - originalState: { - state: State.VACANT, - message: { is_some: false, value: '' }, - sequence: 1n, - owner: new Uint8Array(32) - }, - privateState: createBBoardPrivateState(new Uint8Array(32)), - contractAddress: '0x0000000000000000000000000000000000000000000000000000000000000000', - transactionContext: {} - }; - - const { result, context: newContext } = contract.circuits.post(context, "Test message"); - - expect(newContext.originalState.state).toBe(State.OCCUPIED); - expect(newContext.originalState.message.is_some).toBe(true); - expect(newContext.originalState.message.value).toBe("Test message"); - }); - - it('rejects post on occupied board', () => { - const contract = new Contract(witnesses); - - const context = { - originalState: { - state: State.OCCUPIED, - message: { is_some: true, value: 'Existing message' }, - sequence: 1n, - owner: new Uint8Array(32) - }, - privateState: createBBoardPrivateState(new Uint8Array(32)), - contractAddress: '0x0000000000000000000000000000000000000000000000000000000000000000', - transactionContext: {} - }; - - expect(() => contract.circuits.post(context, "New message")) - .toThrow("Attempted to post to an occupied board"); - }); -}); -``` - -This allows you to test your contract logic off-chain with full control over inputs and without requiring a Midnight Node or proof server. - -## Why the Compact JavaScript implementation matters - -This section explains why Compact generates a JavaScript implementation and why this design is critical for building privacy-preserving smart contracts. - -### A bridge between ZK circuits and everyday code - -Zero-knowledge (ZK) circuits are powerful, but they are also complex and opaque. You cannot easily debug or test them directly. - -The JavaScript implementation acts as a bridge between the low-level proof system and the high-level contract logic. When you call `contract.circuits.post(context, "Hello world!")`, you are running exactly the same logic that the ZK circuit executes on-chain, but in a form that you can step through, log, and inspect in Node.js. - -This means you can validate the behavior of your contract locally before you need to generate proofs or submit transactions to the Midnight network. - -### Type safety and consistency across environments - -The implementation uses Compact's own type descriptors, such as `CompactTypeBoolean` and `CompactTypeBytes`, ensuring the data you pass in your JavaScript tests is encoded in exactly the same way it will be on-chain. This consistency eliminates a whole class of subtle bugs related to differences in byte order, field alignment, or encoding length. - -```typescript -const message = "Hello Midnight!"; -const proof = contract.circuits.post(context, message); -``` - -You can test and reason about your contract logic with confidence that the ZK circuit behaves identically. - -### Reproducibility and proof transparency - -Each call to a contract circuit returns a structured `proofData` object. This data is the input to the prover along with a representation of the circuit. - -That data is crucial for reproducible testing and transparent verification: - -```javascript -{ - input: { value: [...], alignment: [...] }, - output: { value: [...], alignment: [...] }, - publicTranscript: [...], - privateTranscriptOutputs: [...] -} -``` - -Having this available directly in JavaScript lets you record, replay, and verify circuit executions as part of your normal testing flow. You don't need to rely on external tools. - -### Developer productivity without compromising privacy - -The implementation design allows Compact developers to use familiar tools, such as TypeScript, Jest, VSCode, and Node.js, while working with privacy-preserving logic. - -Instead of being locked into a specialized proving environment, you can: - -- Write integration tests in the same language as your application. -- Simulate user flows off-chain. -- Validate logic changes before recompiling circuits. - -This combination provides developer-friendly ergonomics with cryptographic guarantees under the hood. - -## Next steps - -Now you understand how to use the Compact JavaScript implementation. Explore the [Bulletin board DApp](../examples/dapps/bboard) for a complete example of using the JavaScript implementation. \ No newline at end of file diff --git a/docs/guides/windows-compact-setup.mdx b/docs/guides/windows-compact-setup.mdx deleted file mode 100644 index af94cbf27..000000000 --- a/docs/guides/windows-compact-setup.mdx +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Windows Compact setup -description: Learn how to set up the Midnight development environment on Windows using Windows Subsystem for Linux (WSL). -sidebar_position: 60 -sidebar_label: "Windows Compact setup" ---- - -This guide covers setting up the Midnight development environment on Windows using Windows Subsystem for Linux (WSL). We'll walk you through the essential setup steps, including installing and configuring WSL, Docker Desktop, and the Midnight Lace wallet. - -By the end of this tutorial, you'll have a development-ready environment for building DApps on the Midnight Network, running the proof server, and interacting with the network. - -### Install Ubuntu - -Developing DApps for the Midnight network often requires compiling and running various components (like the proof server or complex scripts) that are traditionally Linux-based. The Windows Subsystem for Linux (WSL) is a critical component that allows you to run a native Linux environment directly within Windows without the overhead of a traditional virtual machine. - -Step-by-step installation: - -Open the Start Menu on your Windows desktop. - -Search for Windows PowerShell (or Command Prompt) and right-click on the result. Select **Run as administrator.** This elevation is necessary to install system-level features like WSL. - -![Windows Start menu with PowerShell right-click menu showing Run as administrator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/azunze475qiad8qvnmea.png) - -In the command terminal, enter the following streamlined installation command: - -```bash -wsl --install -d ubuntu -``` - -![PowerShell terminal executing the wsl installation command](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udzzpfa7fmvnkerruifw.png) - -This command performs three primary functions automatically: -It enables the required Windows features for WSL (Virtual Machine Platform and Windows Subsystem for Linux). -It downloads and installs the recommended Ubuntu distribution (the default and most widely supported Linux environment). - -![Ubuntu distribution download and installation progress in the terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15y290h3vgsdliqmbc69.png) - -It restarts the necessary services. - -:::info -The initial download and installation of the required components and the Ubuntu image can take anywhere from 5 to 15 minutes, depending on your internet connection and system speed. Do not close the window until the process is complete. -::: - -After the installation process completes, a new Ubuntu terminal window will open automatically. You will be prompted to create a UNIX username and a password. - -![Ubuntu terminal prompt for creating a new UNIX username and password](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ea6re5ha4k6d8z56cerz.png) - -:::note -When entering your password, the characters will not be displayed on the screen for security reasons (this is standard Linux terminal behavior). Type your desired password carefully and press Enter. -::: - -Once successfully configured, your new username will be displayed as part of your terminal prompt (for example, `yourusername@DESKTOP-XXXXXX:~$`).  - -![Ubuntu terminal showing the successful configuration and user prompt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mj4xgis8a9jpgjth6gc5.png) - -This confirms that WSL is correctly installed and the Ubuntu distribution is ready for use. - -### Verification and best practices - -To ensure WSL is running the correct version, you can execute this command in the Windows PowerShell: - -```bash -wsl -l -v -``` - -You should see a list showing Ubuntu and confirming its STATE is Running (or Stopped) and its VERSION is 2 (WSL 2 is required for optimal performance and Docker integration). - -## Install and configure Docker desktop - -Open your preferred web browser and navigate to the official **Docker** website at [docker.com/products/docker-desktop](https://www.docker.com/products/docker-desktop). - -![Official Docker Desktop download page for Windows](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tbyf3h7vt6seaff0yuma.png) - -Look for the **Docker Desktop for Windows** download link. Ensure you download the installer that matches your CPU architecture (for example, **Windows - AMD64** for modern systems). - -Run the downloaded installer (for example, `Docker Desktop Installer.exe`). During the installation, make sure the box for **Use WSL 2 instead of Hyper-V** is checked. This is vital for Docker to integrate seamlessly with the Linux environment you just set up. - -Follow the prompts to complete the installation. A system restart may be required. - -After the reboot, launch **Docker Desktop** from your **Start Menu**. It will take a few moments to start up, showing the whale icon in your system tray. - -### Verification and integration - -Once Docker Desktop is running, open the **Settings** menu (the gear icon). - -Click **Resources**, then navigate to the **WSL integration** tab. - -Ensure that the **Enable integration with my default WSL distro** option is toggled **ON**, and specifically verify that your **Ubuntu** distribution is enabled. This connection allows Docker to manage containers directly from your Linux terminal. - -## Set up and run the midnight proof server - -The **Midnight Proof Server** is the core component that your DApps will communicate with to execute zero-knowledge proofs and transactions. It is run via **Docker** for consistency. - -### Download the Docker image - -Open your **Docker Desktop** application and navigate to the **Explore** section (or use the search field at the top). Search for `midnightntwrk/proof-server` and download the latest image. - -![Docker Desktop Images tab showing the downloaded proof-server image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pmmocxc6tumxqr7k4801.png) - -### Run the proof server - -To start the proof server, open your **Ubuntu** terminal and run the following command: - -```bash -docker run -p 6300:6300 midnightntwrk/proof-server:8.0.3 -- midnight-proof-server -v -``` - -You should see output similar to: - -``` -actix_server::server: starting service: "actix-web-service-0.0.0.0:6300", workers: 14, listening on: 0.0.0.0:6300 -``` - -:::tip -Keep this terminal window open. The proof server _must_ stay active to compile and deploy contracts. -::: - -## Install the Compact compiler - -Compact is Midnight's smart contract language. Since you are using a Windows machine, you must install the compiler inside your **WSL Ubuntu terminal** to ensure it works with the rest of the Midnight toolchain. - -1. Open your Ubuntu terminal (WSL). -2. Run the following command to install the Compact binaries: - -```bash -curl --proto '=https' --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh -``` - -3. The installer script automatically updates your path. To apply these changes to your current session, reload your shell configuration: - -```bash -source ~/.bashrc -``` - -4. Verify the installation by checking the version: - -```bash -compact --version -``` - -5. Update to the latest version to ensure you have the most recent features: - -```bash -compact update -``` - -## Install the Compact VS Code extension - -The Compact VS Code extension provides syntax highlighting and real-time error checking, which is essential for writing smart contracts. - -1. Download the Compact VS Code extension VSIX package from the [releases page](https://raw.githubusercontent.com/midnight-ntwrk/releases/gh-pages/artifacts/vscode-extension/compact-0.2.13/compact-0.2.13.vsix). -2. Open VS Code. -3. Go to the **Extensions** view (press `Ctrl+Shift+X`), click the **...** (More Actions) menu at the top-right of the extensions pane, and select **Install from VSIX...**. -4. Select the `.vsix` file you just downloaded to complete the installation. - - -## Next steps - -Now that your environment is ready, you can begin building. To get started and write your first smart contract, refer to the [hello world](../getting-started/hello-world) tutorial. diff --git a/docs/guides/compact-javascript-runtime.mdx b/docs/guides/work-with-compiled-contract.mdx similarity index 59% rename from docs/guides/compact-javascript-runtime.mdx rename to docs/guides/work-with-compiled-contract.mdx index f62ee025f..f08cc3a90 100644 --- a/docs/guides/compact-javascript-runtime.mdx +++ b/docs/guides/work-with-compiled-contract.mdx @@ -1,23 +1,25 @@ --- SPDX-License-Identifier: Apache-2.0 copyright: This file is part of midnight-docs. Copyright (C) Midnight Foundation. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -description: Learn about the Compact JavaScript implementation for the Midnight Network. -# toc_max_heading_level: 2 -sidebar_label: "Compact JavaScript implementation" -sidebar_position: 45 +title: Work with your compiled contract +description: Understand the JavaScript implementation the Compact compiler generates, then import it, implement witnesses, call circuits, and write unit tests. +sidebar_label: "Work with your compiled contract" +sidebar_position: 5 +tags: [compact, javascript, witnesses, testing, smart-contract] +keywords: [midnight, compact, javascript implementation, witnesses, circuits, unit tests] --- import Step, { StepsProvider } from "@site/src/components/Step/Step"; -# The Compact JavaScript implementation +# Work with your compiled contract -If you have written smart contracts before, then you are likely familiar with languages like Solidity or Rust that compile to on-chain bytecode. +If you have written smart contracts before, then you are likely familiar with languages like Solidity or Rust that compile to on-chain bytecode. The Midnight blockchain takes a different approach by using a domain-specific language called Compact, designed from the ground up for zero-knowledge (ZK) smart contracts. -The Midnight blockchain takes a different approach by using a domain-specific language called Compact, designed from the ground up for zero-knowledge (ZK) smart contracts. +When you compile a Compact contract, the compiler emits a matching JavaScript implementation alongside the ZK circuits. That implementation is how your application code talks to the contract: you import it, implement its witnesses, call its circuits, and test it like any other module. -This guide explores the JavaScript implementation generated for the [bulletin board smart contract](https://github.com/midnightntwrk/example-bboard) in depth. +This guide first explains what that generated implementation is and how the compiler structures it, using the [bulletin board smart contract](https://github.com/midnightntwrk/example-bboard) as the running example. It then shows how to use the implementation in your development workflow. -## How the JavaScript implementation gets generated +## How the JavaScript implementation gets generated {#how-the-javascript-implementation-gets-generated} When you compile a Compact contract, the compiler produces more than just ZK circuits. It also emits a matching JavaScript implementation named `index.js`. @@ -55,19 +57,19 @@ Because of these steps, `index.js` is not a hand-written artifact, but a systema If you change your Compact contract by adding or removing functions or changing types, then recompile to regenerate `index.js` accordingly. Always treat it as generated code rather than hand-written, and avoid modifying it manually. ::: -## Understand the JavaScript implementation structure +## Understand the JavaScript implementation structure {#understand-the-javascript-implementation-structure} The generated implementation for your Compact contract appears in a file named `index.js` within the `managed` directory. This file is a self-contained ES module that mirrors your contract's structure, from type definitions to callable functions. -### Runtime initialization and version checks +### Runtime initialization and version checks {#runtime-initialization-and-version-checks} At the very top, the implementation imports the Compact runtime and verifies version compatibility: ```javascript import * as __compactRuntime from '@midnight-ntwrk/compact-runtime'; -__compactRuntime.checkRuntimeVersion('0.15.0'); +__compactRuntime.checkRuntimeVersion('0.16.0'); ``` This ensures that the version of `@midnight-ntwrk/compact-runtime` installed in your project matches the version expected by the compiler. Version mismatches can cause runtime errors or incorrect circuit behavior. @@ -76,7 +78,7 @@ This ensures that the version of `@midnight-ntwrk/compact-runtime` installed in Always refer to the [compatibility matrix](../relnotes/support-matrix) to ensure that the version of the Compact runtime matches the version of the compiler. ::: -### Type definitions and descriptors +### Type definitions and descriptors {#type-definitions-and-descriptors} The file defines enumerations and type descriptors. These tell the implementation how to encode and decode the data types used in your contract, such as integers, strings, or custom structures. @@ -102,7 +104,7 @@ Each descriptor object defines how JavaScript values are converted to and from t - `CompactTypeEnum`: Represents the State enum - `CompactTypeUnsignedInteger`: Represents Counter and other numeric types (the type of the `sequence` field) -### Composite types and data structures +### Composite types and data structures {#composite-types-and-data-structures} Complex Compact types such as `Maybe` or `Either` are represented as JavaScript classes that combine primitive descriptors. @@ -133,7 +135,7 @@ Each composite type class provides methods for converting between JavaScript obj The `Maybe` type corresponds to the `message` ledger field in the bulletin board contract, representing optional string values. -### The Contract class and circuit wrappers +### The Contract class and circuit wrappers {#the-contract-class-and-circuit-wrappers} The generated implementation defines a `Contract` class that mirrors your Compact contract's circuits. The constructor validates the witnesses object and sets up circuit methods. @@ -206,7 +208,7 @@ It then executes the Compact logic and returns structured `proofData` for verifi The `circuits` object contains all callable functions, including both impure circuits (post and takeDown) and pure circuits (publicKey). The `impureCircuits` object contains only the circuits that interact with witnesses and modify state. -### Pure circuits implementation +### Pure circuits implementation {#pure-circuits-implementation} The implementation also exports pure circuits that can be called directly without a circuit context: @@ -241,7 +243,7 @@ export const pureCircuits = { Pure circuits like `publicKey` perform deterministic computations without accessing ledger state or witnesses. They can be called independently for operations, such as generating owner commitments or computing hashes. -### Ledger state deserialization +### Ledger state deserialization {#ledger-state-deserialization} The implementation provides a function to deserialize raw ledger state into typed JavaScript objects: @@ -302,7 +304,7 @@ This function converts raw contract state from the blockchain into a structured - Each getter uses `queryLedgerState` with field index paths to retrieve the specific value lazily. - DApps use this function to interpret contract state returned from the indexer. -### Exports and type bindings +### Exports and type bindings {#exports-and-type-bindings} The implementation exports everything you need to interact with the contract: @@ -354,7 +356,205 @@ export declare const pureCircuits: PureCircuits; These type definitions enable type-safe contract interaction in TypeScript projects. Your IDE understands what functions and structures are available, providing autocomplete and compile-time error checking. -## Next steps +## Import the implementation {#import-the-implementation} -Now you understand how the Compact JavaScript implementation is generated. You can learn how to use it in the [Use the Compact JavaScript implementation](./use-compact-javascript-implementation) guide. +With the structure clear, the rest of this guide puts the implementation to work in your development workflow. +Once you have compiled your Compact contract, the compiler outputs these key files in the `managed` directory: + +- `index.js`: The JavaScript implementation +- `index.d.ts`: TypeScript type definitions +- `index.js.map`: Source map for debugging + +You can load the implementation like any other ECMAScript (ES) module: + +```typescript +import { Contract, State, pureCircuits, ledger } from './managed/bboard/contract/index.js'; +``` + +If you are using TypeScript, then the accompanying declaration file `index.d.ts` automatically provides type hints for your contract and its methods. + +## Implement witnesses {#implement-witnesses} + +Every Compact contract with witness functions requires a witnesses object when instantiated. This object contains implementations for all witness functions declared in your Compact code. + +For the bulletin board contract, create a `witnesses.ts` file in the `contract/src` directory: + +```typescript +import { Ledger } from "./managed/bboard/contract/index.js"; +import { WitnessContext } from "@midnight-ntwrk/compact-runtime"; + +export type BBoardPrivateState = { + readonly secretKey: Uint8Array; +}; + +export const createBBoardPrivateState = (secretKey: Uint8Array) => ({ + secretKey, +}); + +export const witnesses = { + localSecretKey: ({ + privateState, + }: WitnessContext): [ + BBoardPrivateState, + Uint8Array, + ] => [privateState, privateState.secretKey], +}; +``` + +The witnesses object maps witness function names to their implementations. Each witness function receives a `WitnessContext` containing the ledger state, private state, and contract address. The function returns a tuple of the updated private state and the witness value. + +## Call contract circuits {#call-contract-circuits} + +The implementation exposes each circuit as a JavaScript function under `contract.circuits` or `contract.impureCircuits`. These wrappers prepare the inputs, run the JavaScript implementation, and return structured results containing the output, updated context, and proof data. + +Here's an example of calling the `post` impure circuit: + +```typescript +const initialContext = { + originalState: { + state: State.VACANT, + message: { is_some: false, value: '' }, + sequence: 1n, + owner: new Uint8Array(32) + }, + privateState: { + secretKey: new Uint8Array(32) + }, + contractAddress: '0x...', + transactionContext: {} +}; + +const message = "Hello from Compact!"; + +const { result, context, proofData, gasCost } = + contract.circuits.post(initialContext, message); +``` + +The returned object contains: +- `result`: The circuit's return value (empty array for post) +- `context`: The updated circuit context with new ledger state +- `proofData`: Data structure containing input, output, and transcripts for proof generation +- `gasCost`: Gas cost tracking information + +Here's an example of calling the `publicKey` pure circuit: + +```typescript +const secretKey = new Uint8Array(32); +const sequenceBytes = new Uint8Array(32); + +const ownerCommitment = pureCircuits.publicKey(secretKey, sequenceBytes); +``` + +You can call pure circuits directly without a circuit context. They perform deterministic computations and return values immediately. + +## Write unit tests {#write-unit-tests} + +Because the Compact implementation is a standard ES module, you can integrate it with testing frameworks such as Vitest, Jest, or Mocha. + +```typescript +import { describe, it, expect } from 'vitest'; +import { Contract, State } from './managed/bboard/contract/index.js'; +import { witnesses, createBBoardPrivateState } from './witnesses.js'; + +describe('Bulletin board contract', () => { + it('accepts a new post on vacant board', () => { + const contract = new Contract(witnesses); + + const context = { + originalState: { + state: State.VACANT, + message: { is_some: false, value: '' }, + sequence: 1n, + owner: new Uint8Array(32) + }, + privateState: createBBoardPrivateState(new Uint8Array(32)), + contractAddress: '0x0000000000000000000000000000000000000000000000000000000000000000', + transactionContext: {} + }; + + const { result, context: newContext } = contract.circuits.post(context, "Test message"); + + expect(newContext.originalState.state).toBe(State.OCCUPIED); + expect(newContext.originalState.message.is_some).toBe(true); + expect(newContext.originalState.message.value).toBe("Test message"); + }); + + it('rejects post on occupied board', () => { + const contract = new Contract(witnesses); + + const context = { + originalState: { + state: State.OCCUPIED, + message: { is_some: true, value: 'Existing message' }, + sequence: 1n, + owner: new Uint8Array(32) + }, + privateState: createBBoardPrivateState(new Uint8Array(32)), + contractAddress: '0x0000000000000000000000000000000000000000000000000000000000000000', + transactionContext: {} + }; + + expect(() => contract.circuits.post(context, "New message")) + .toThrow("Attempted to post to an occupied board"); + }); +}); +``` + +This allows you to test your contract logic off-chain with full control over inputs and without requiring a Midnight Node or proof server. + +## Why the Compact JavaScript implementation matters {#why-the-compact-javascript-implementation-matters} + +This section explains why Compact generates a JavaScript implementation and why this design is critical for building privacy-preserving smart contracts. + +### A bridge between ZK circuits and everyday code + +Zero-knowledge (ZK) circuits are complex and opaque, and you cannot debug or test them directly. + +The JavaScript implementation acts as a bridge between the low-level proof system and the high-level contract logic. When you call `contract.circuits.post(context, "Hello world!")`, you are running exactly the same logic that the ZK circuit executes on-chain, but in a form that you can step through, log, and inspect in Node.js. + +This means you can validate the behavior of your contract locally before you need to generate proofs or submit transactions to the Midnight network. + +### Type safety and consistency across environments + +The implementation uses Compact's own type descriptors, such as `CompactTypeBoolean` and `CompactTypeBytes`, so the data you pass in your JavaScript tests uses exactly the same encoding as the chain. This consistency eliminates a whole class of subtle bugs related to differences in byte order, field alignment, or encoding length. + +```typescript +const message = "Hello Midnight!"; +const proof = contract.circuits.post(context, message); +``` + +You can test and reason about your contract logic with confidence that the ZK circuit behaves identically. + +### Reproducibility and proof transparency + +Each call to a contract circuit returns a structured `proofData` object. This data is the input to the prover along with a representation of the circuit. + +That data is crucial for reproducible testing and transparent verification: + +```javascript +{ + input: { value: [...], alignment: [...] }, + output: { value: [...], alignment: [...] }, + publicTranscript: [...], + privateTranscriptOutputs: [...] +} +``` + +Having this available directly in JavaScript lets you record, replay, and verify circuit executions as part of your normal testing flow. You don't need to rely on external tools. + +### Developer productivity without compromising privacy + +The implementation design allows Compact developers to use familiar tools, such as TypeScript, Jest, VSCode, and Node.js, while working with privacy-preserving logic. + +Instead of depending on a specialized proving environment, you can: + +- Write integration tests in the same language as your application. +- Simulate user flows off-chain. +- Validate logic changes before recompiling circuits. + +This combination provides developer-friendly ergonomics with cryptographic guarantees under the hood. + +## Next steps {#next-steps} + +Now you understand how the compiler generates the Compact JavaScript implementation and how to use it. Explore the [Bulletin board DApp](../examples/dapps/bboard) for a complete example of using the JavaScript implementation. diff --git a/docs/nodes/node-endpoints.mdx b/docs/nodes/node-endpoints.mdx index 5ca68eaef..722dfa2c6 100644 --- a/docs/nodes/node-endpoints.mdx +++ b/docs/nodes/node-endpoints.mdx @@ -187,6 +187,46 @@ curl -X POST \ https://rpc.preview.midnight.network/ ``` +## Subscribe to new blocks {#subscribe-to-new-blocks} + +For subscriptions, connect over WebSocket. Midnight nodes are Substrate-based, so the [`@polkadot/api`](https://polkadot.js.org/docs/api/) library works out of the box (`npm install @polkadot/api`): + +```typescript +import { ApiPromise, WsProvider } from '@polkadot/api'; + +// Define the WebSocket endpoint for the Midnight node +const wsProvider = new WsProvider('wss://rpc.preview.midnight.network'); + +// Create the API instance +const api = await ApiPromise.create({ provider: wsProvider }); + +// Subscribe to new block headers +api.rpc.chain.subscribeNewHeads(async (lastHeader) => { + const blockHash = await api.rpc.chain.getBlockHash(lastHeader.number); + const signedBlock = await api.rpc.chain.getBlock(blockHash); + const allEvents = await api.query.system.events.at(blockHash); + + console.log(`\nBlock #${lastHeader.number}`); + + signedBlock.block.extrinsics.forEach((extrinsic, index) => { + console.log(`\nExtrinsic ${index}: ${extrinsic.method.section}.${extrinsic.method.method}`); + console.log(`Arguments: ${extrinsic.args.map((arg) => arg.toString()).join(', ')}`); + }); + + allEvents.forEach(({ event, phase }, index) => { + console.log(`\nEvent ${index}: ${event.section}.${event.method}`); + console.log(`Phase: ${phase.toString()}`); + console.log(`Data: ${event.data.toString()}`); + }); +}); +``` + +The callback fires once per block, retrieves the full block and its associated events, and logs each extrinsic (transaction) and event. + +:::tip Querying contract state? +Raw RPC subscriptions are a node-level tool. If your DApp needs contract state or transaction data, use the Midnight indexer's GraphQL API instead, via [`indexerPublicDataProvider`](/guides/local-to-testnet#publicdataprovider). +::: + ## Insomnia API collection The Insomnia collection provides pre-configured requests for interacting with Midnight RPC endpoints. diff --git a/docs/tokens/overview.mdx b/docs/tokens/overview.mdx index 8102a857c..aaf12bbcd 100644 --- a/docs/tokens/overview.mdx +++ b/docs/tokens/overview.mdx @@ -40,7 +40,7 @@ To build and move your own tokens end to end, follow the two-part tutorial: [Cre Every transaction on Midnight requires DUST. On a fresh wallet, DUST generation depends on how you register your NIGHT. Today, most NIGHT reaches Midnight through the cross-chain path (cNIGHT on Cardano), and the registration must finalize on Cardano and then reach a Midnight node, a process that takes about 12 hours. On a local network, DUST generates in about 5 minutes. This delay goes away once native mNIGHT launches. To get started: -- [Get faucet tokens](/guides/acquire-tokens) (tNIGHT and tDUST on Preprod) +- [Fund your wallet](/guides/acquire-tokens) (tNIGHT and tDUST on Preprod) - [Generate DUST programmatically](/guides/generating-dust-programmatically) (using the wallet SDK) ## Further reading diff --git a/docs/troubleshoot/decode-1010-transaction-rejection-errors.mdx b/docs/troubleshoot/decode-1010-transaction-rejection-errors.mdx index 6ec64e0f6..6c8695215 100644 --- a/docs/troubleshoot/decode-1010-transaction-rejection-errors.mdx +++ b/docs/troubleshoot/decode-1010-transaction-rejection-errors.mdx @@ -375,7 +375,7 @@ Either the replay counter is stale or the intent's TTL window has elapsed. Re-fe
115 InvalidProof / 235 Zswap.Malformed.InvalidProof — a zero-knowledge proof failed verification -Confirm that the proof server is running a version compatible with the node and SDK. See [run a proof server](../guides/run-proof-server.mdx) and the [release compatibility matrix](../relnotes/support-matrix.mdx). +Confirm that the proof server is running a version compatible with the node and SDK. See [run a proof server](../guides/development-environment.mdx#run-the-proof-server) and the [release compatibility matrix](../relnotes/support-matrix.mdx).
diff --git a/docs/tutorials/bboard/bboard-cli-implementation.mdx b/docs/tutorials/bboard/bboard-cli-implementation.mdx index 7bdbcd521..7c7bfcd38 100644 --- a/docs/tutorials/bboard/bboard-cli-implementation.mdx +++ b/docs/tutorials/bboard/bboard-cli-implementation.mdx @@ -65,7 +65,7 @@ Each launcher script targets a different network: - `standalone`: Connects to a local Midnight network running on your machine using the genesis-mint wallet seed :::info -For more information on setting up a local Midnight network, see the [Midnight local network](../../../guides/midnight-local-network/) documentation. +For more information on setting up a local Midnight network, see the [Midnight local network](/guides/local-to-testnet#local-network) documentation. ::: ## Configure TypeScript @@ -1074,7 +1074,7 @@ await run(config, testEnvironment, logger); ``` :::info -The `standalone` launcher connects to a local Midnight network running on your machine. For more information, see [Midnight local network](../../../guides/midnight-local-network/). +The `standalone` launcher connects to a local Midnight network running on your machine. For more information, see [Midnight local network](/guides/local-to-testnet#local-network). ::: diff --git a/docs/tutorials/zk-loan/attestation-api.mdx b/docs/tutorials/zk-loan/attestation-api.mdx index 9d3c6c7d1..e2ef48980 100644 --- a/docs/tutorials/zk-loan/attestation-api.mdx +++ b/docs/tutorials/zk-loan/attestation-api.mdx @@ -489,10 +489,10 @@ This creates a two-sided privacy guarantee: ## Set up Docker for the proof server :::note -If you are testing with [Midnight Local Dev](/guides/midnight-local-network) instead of Preprod, then skip this step. The local dev environment already includes a proof server on port `6300`. +If you are testing with [Midnight Local Dev](/guides/local-to-testnet#local-network) instead of Preprod, then skip this step. The local dev environment already includes a proof server on port `6300`. ::: -The [proof server](/guides/run-proof-server) generates ZK proofs for every transaction that interacts with the smart contract. For Preprod, you run it locally via Docker while the blockchain node and indexer are remote (hosted by Midnight Network). +The [proof server](/guides/development-environment#run-the-proof-server) generates ZK proofs for every transaction that interacts with the smart contract. For Preprod, you run it locally via Docker while the blockchain node and indexer are remote (hosted by Midnight Network). Create `zkloan-credit-scorer-cli/proof-server.yml`: diff --git a/docs/tutorials/zk-loan/cli.mdx b/docs/tutorials/zk-loan/cli.mdx index 84f6a2b50..fb0e18aa4 100644 --- a/docs/tutorials/zk-loan/cli.mdx +++ b/docs/tutorials/zk-loan/cli.mdx @@ -110,7 +110,7 @@ Key details: * The indexer provides two connections: HTTP for queries and WebSocket for real-time subscription to ledger state changes. -* `LocalDevConfig` connects to [Midnight Local Dev](/guides/midnight-local-network), a standalone Docker-based development environment that runs the Midnight node, indexer, and proof server locally. It uses the `undeployed` network ID, and all services run on `localhost`. +* `LocalDevConfig` connects to [Midnight Local Dev](/guides/local-to-testnet#local-network), a standalone Docker-based development environment that runs the Midnight node, indexer, and proof server locally. It uses the `undeployed` network ID, and all services run on `localhost`. ### Type definitions @@ -1735,7 +1735,7 @@ Edit `zkloan-credit-scorer-cli/.env` and set `MIDNIGHT_STORAGE_PASSWORD` to a va ## Run the CLI -The final part brings everything together by testing the complete flow using [Midnight Local Dev](/guides/midnight-local-network), a standalone Docker environment that runs the Midnight node, indexer, and proof server locally. Its `npm start` wizard can transfer NIGHT to any address you give it; the ZKLoan CLI then registers that NIGHT for DUST automatically, so the wallet is ready to submit transactions. +The final part brings everything together by testing the complete flow using [Midnight Local Dev](/guides/local-to-testnet#local-network), a standalone Docker environment that runs the Midnight node, indexer, and proof server locally. Its `npm start` wizard can transfer NIGHT to any address you give it; the ZKLoan CLI then registers that NIGHT for DUST automatically, so the wallet is ready to submit transactions. You need three terminal windows. diff --git a/docusaurus.config.js b/docusaurus.config.js index 715a2351b..90e8cf51d 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -194,6 +194,10 @@ const config = { from: '/category/tutorials', to: '/tutorials', }, + { + from: '/category/guides', + to: '/guides', + }, { from: '/category/contracts', to: '/examples/contracts', diff --git a/packages/tests/pipelines-urls.json b/packages/tests/pipelines-urls.json index cba2cecc8..e5d01c8bd 100644 --- a/packages/tests/pipelines-urls.json +++ b/packages/tests/pipelines-urls.json @@ -10,15 +10,13 @@ "/getting-started/quickstart", "/getting-started/hello-world", + "/guides", + "/guides/development-environment", "/guides/acquire-tokens", - "/guides/deploy-mn-app", - "/guides/compact-javascript-runtime", - "/guides/run-proof-server", + "/guides/local-to-testnet", + "/guides/work-with-compiled-contract", + "/guides/connect-wallet-frontend", "/guides/making-decision-on-contract-updatability", - "/guides/nextjs-wallet-connect", - "/guides/react-wallet-connect", - "/how-to/bun-runtime-midnight", - "/guides/windows-compact-setup", "/concepts", "/concepts/ledgers", diff --git a/sdks/community/wallets/integration.mdx b/sdks/community/wallets/integration.mdx index 7cc41358d..80402508e 100644 --- a/sdks/community/wallets/integration.mdx +++ b/sdks/community/wallets/integration.mdx @@ -34,7 +34,7 @@ type InitialAPI = { }; ``` -Support both discovery paths. Wallets such as Lace and 1AM inject under a fixed, friendly key (`window.midnight.mnLace`, `window.midnight['1am']`). The v4 specification instead installs each wallet under its own key and exposes a stable `rdns` field. Discover wallets by scanning `Object.values(window.midnight)` and matching on `rdns` or `name`. Before using a wallet, check its `apiVersion` against the range your DApp supports. The spec notes event-based discovery, like Ethereum's EIP-6963, as a possible future addition, but the current mechanism is the shared `window.midnight` object. ([React wallet-connect guide](https://docs.midnight.network/guides/react-wallet-connect)) +Support both discovery paths. Wallets such as Lace and 1AM inject under a fixed, friendly key (`window.midnight.mnLace`, `window.midnight['1am']`). The v4 specification instead installs each wallet under its own key and exposes a stable `rdns` field. Discover wallets by scanning `Object.values(window.midnight)` and matching on `rdns` or `name`. Before using a wallet, check its `apiVersion` against the range your DApp supports. The spec notes event-based discovery, like Ethereum's EIP-6963, as a possible future addition, but the current mechanism is the shared `window.midnight` object. ([React wallet-connect guide](https://docs.midnight.network/guides/connect-wallet-frontend)) ## Connect and the ConnectedAPI surface @@ -134,4 +134,4 @@ Key files in the repo, under `frontend-vite-react/src/modules/midnight/`: - `wallet-widget/ui/midnightWallet.tsx`: connect modal - `counter-sdk/`: wiring a connected wallet into a Compact call -([starter template](https://github.com/eddalabs/midnight-starter-template), [React guide](https://docs.midnight.network/guides/react-wallet-connect)) +([starter template](https://github.com/eddalabs/midnight-starter-template), [React guide](https://docs.midnight.network/guides/connect-wallet-frontend)) diff --git a/sdks/community/wallets/reference.mdx b/sdks/community/wallets/reference.mdx index 5bf00bcf8..0a47c1c43 100644 --- a/sdks/community/wallets/reference.mdx +++ b/sdks/community/wallets/reference.mdx @@ -173,7 +173,7 @@ Wallet-relevant terms. - [DApp Connector API reference](https://docs.midnight.network/api-reference/dapp-connector) - [Connector repo and specification](https://github.com/midnightntwrk/midnight-dapp-connector-api) - [`@midnight-ntwrk/dapp-connector-api` on npm (v4.0.1)](https://www.npmjs.com/package/@midnight-ntwrk/dapp-connector-api) -- [React wallet-connect guide](https://docs.midnight.network/guides/react-wallet-connect) +- [React wallet-connect guide](https://docs.midnight.network/guides/connect-wallet-frontend) - [CAIP-372 draft](https://github.com/ChainAgnostic/CAIPs/pull/372/files) - [Midnight ecosystem catalog](https://midnight.network/ecosystem-catalog) ([institutional-custody tag](https://midnight.network/ecosystem-catalog?tag=institutional-custody)) - [Reference DApp (Lace and 1AM): Edda Labs midnight-starter-template](https://github.com/eddalabs/midnight-starter-template) @@ -196,7 +196,7 @@ Wallet-relevant terms. - [Edda Labs marketplace](https://github.com/eddalabs/edda-marketplace) **Lace** -- [Lace Midnight page](https://www.lace.io/midnight); [Lace wallet guide](https://docs.midnight.network/guides/lace-wallet) +- [Lace Midnight page](https://www.lace.io/midnight); [Lace wallet guide](https://docs.midnight.network/getting-started/installation) - [Lace GitHub repo and issue tracker](https://github.com/input-output-hk/lace) - [Forum: Lace doesn't implement getProvingProvider](https://forum.midnight.network/t/lace-wallet-doesnt-implement-getprovingprovider-expected-behavior-or-version-gap/1213) - [Forum: Unable to get wallet address from Lace](https://forum.midnight.network/t/unable-to-get-wallet-address-from-lace-wallet/922) diff --git a/sdks/community/wallets/wallets-overview.mdx b/sdks/community/wallets/wallets-overview.mdx index 3aa3dab1d..c94f19d88 100644 --- a/sdks/community/wallets/wallets-overview.mdx +++ b/sdks/community/wallets/wallets-overview.mdx @@ -106,7 +106,7 @@ The profiled wallets fall into three categories: **Best for:** standard DApps whose users can run a local proof server. **Status:** live, with a partial `ConnectedAPI` (no `getProvingProvider` or `signData` yet). Open source. -- **Overview:** Input Output's multi-chain wallet (Cardano, Bitcoin, Midnight). For Midnight it is a Chrome and Edge extension, not the Lace mobile app. ([lace.io/midnight](https://www.lace.io/midnight), [docs](https://docs.midnight.network/guides/lace-wallet), [GitHub](https://github.com/input-output-hk/lace)) +- **Overview:** Input Output's multi-chain wallet (Cardano, Bitcoin, Midnight). For Midnight it is a Chrome and Edge extension, not the Lace mobile app. ([lace.io/midnight](https://www.lace.io/midnight), [docs](https://docs.midnight.network/getting-started/installation), [GitHub](https://github.com/input-output-hk/lace)) - **Supports:** - NIGHT management. - DUST generation through the Dust Generation DApp (about 12 hours to initialize; see [Fees and DUST](./integration.mdx#fees-and-dust)). diff --git a/sdks/official/midnight-js.mdx b/sdks/official/midnight-js.mdx index 294c87461..7ac512106 100644 --- a/sdks/official/midnight-js.mdx +++ b/sdks/official/midnight-js.mdx @@ -24,7 +24,7 @@ This guide provides a comprehensive overview of the Midnight.js SDK, including i Before using Midnight.js, ensure that you have: - [Node.js](https://nodejs.org/) version 22.x or higher installed. -- [Docker](https://www.docker.com/) installed and running (required for the [proof server](../../guides/run-proof-server)). +- [Docker](https://www.docker.com/) installed and running (required for the [proof server](../../guides/development-environment#run-the-proof-server)). ## Packages @@ -159,7 +159,7 @@ export const CONFIG = { :::note The undeployed network is a local network that is not deployed to the blockchain. It is used for testing and development purposes. -For more information, see the [Midnight local network](../../guides/midnight-local-network) guide. +For more information, see the [Midnight local network](../../guides/local-to-testnet#local-network) guide. ::: diff --git a/sdks/troubleshoot/index.mdx b/sdks/troubleshoot/index.mdx index ffc51e099..07fd7d2b3 100644 --- a/sdks/troubleshoot/index.mdx +++ b/sdks/troubleshoot/index.mdx @@ -144,7 +144,7 @@ The tagged-error variant exposes `tokenType: string` and `amount: bigint`. The c **What to check**: -- Confirm the [proof server](/guides/run-proof-server) is running and accessible. +- Confirm the [proof server](/guides/development-environment#run-the-proof-server) is running and accessible. - Verify you are loading the correct circuit keys. - Check the wrapped cause for the underlying provider error. diff --git a/src/theme/Navbar/Logo/index.tsx b/src/theme/Navbar/Logo/index.tsx index f41f6c858..cf2517759 100644 --- a/src/theme/Navbar/Logo/index.tsx +++ b/src/theme/Navbar/Logo/index.tsx @@ -13,8 +13,7 @@ const COMPACT_ROUTE_PATTERNS: RegExp[] = [ new RegExp(`^\\/${VERSION_PREFIX}api-reference\\/compact-runtime(?:\\/|$)`), // Guides that should use the Compact logo. - new RegExp(`^\\/${VERSION_PREFIX}guides\\/compact-javascript-runtime(?:\\/|$)`), - new RegExp(`^\\/${VERSION_PREFIX}guides\\/use-compact-javascript-implementation(?:\\/|$)`), + new RegExp(`^\\/${VERSION_PREFIX}guides\\/work-with-compiled-contract(?:\\/|$)`), // Compact category pages at root and versioned paths. new RegExp( `^\\/${VERSION_PREFIX}category\\/(?:reference|compilation-and-tooling|data-types|standard-library)(?:\\/|$)` diff --git a/vercel.json b/vercel.json index 02fe7735e..729a7506c 100644 --- a/vercel.json +++ b/vercel.json @@ -20,7 +20,7 @@ }, { "source": "/getting-started/deploy-mn-app", - "destination": "/guides/deploy-mn-app", + "destination": "/guides/local-to-testnet", "permanent": true }, { @@ -91,7 +91,7 @@ }, { "source": "/develop/tutorial/using/proof-server", - "destination": "/guides/run-proof-server", + "destination": "/guides/development-environment", "permanent": true }, { @@ -156,9 +156,120 @@ }, { "source": "/develop/how-to", - "destination": "/category/guides", + "destination": "/guides", "permanent": true }, + { + "source": "/guides/windows-compact-setup", + "destination": "/guides/development-environment", + "permanent": true + }, + { + "source": "/how-to/bun-runtime-midnight", + "destination": "/guides/development-environment", + "permanent": true + }, + { + "source": "/guides/install-bun-runtime-midnight", + "destination": "/guides/development-environment", + "permanent": true + }, + { + "source": "/guides/midnight-local-network", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/guides/configure-providers", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/guides/deploy-mn-app", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/guides/compact-javascript-runtime", + "destination": "/guides/work-with-compiled-contract", + "permanent": true + }, + { + "source": "/guides/use-compact-javascript-implementation", + "destination": "/guides/work-with-compiled-contract", + "permanent": true + }, + { + "source": "/guides/react-wallet-connect", + "destination": "/guides/connect-wallet-frontend", + "permanent": true + }, + { + "source": "/guides/nextjs-wallet-connect", + "destination": "/guides/connect-wallet-frontend", + "permanent": true + }, + { + "source": "/guides/query-the-blockchain", + "destination": "/nodes/node-endpoints", + "permanent": true + }, + { + "source": "/category/guides", + "destination": "/guides", + "permanent": true + }, + { + "source": "/develop/how-to/windows-compact-setup", + "destination": "/guides/development-environment", + "permanent": true + }, + { + "source": "/develop/how-to/bun-runtime-midnight", + "destination": "/guides/development-environment", + "permanent": true + }, + { + "source": "/develop/how-to/midnight-local-network", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/develop/how-to/configure-providers", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/develop/how-to/deploy-mn-app", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/develop/how-to/compact-javascript-runtime", + "destination": "/guides/work-with-compiled-contract", + "permanent": true + }, + { + "source": "/develop/how-to/use-compact-javascript-implementation", + "destination": "/guides/work-with-compiled-contract", + "permanent": true + }, + { + "source": "/develop/how-to/react-wallet-connect", + "destination": "/guides/connect-wallet-frontend", + "permanent": true + }, + { + "source": "/develop/how-to/nextjs-wallet-connect", + "destination": "/guides/connect-wallet-frontend", + "permanent": true + }, + { + "source": "/develop/how-to/query-the-blockchain", + "destination": "/nodes/node-endpoints", + "permanent": true + }, + { "source": "/develop/how-to/:path*", "destination": "/guides/:path*", @@ -166,9 +277,60 @@ }, { "source": "/develop/guides", - "destination": "/category/guides", + "destination": "/guides", + "permanent": true + }, + { + "source": "/develop/guides/windows-compact-setup", + "destination": "/guides/development-environment", + "permanent": true + }, + { + "source": "/develop/guides/bun-runtime-midnight", + "destination": "/guides/development-environment", + "permanent": true + }, + { + "source": "/develop/guides/midnight-local-network", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/develop/guides/configure-providers", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/develop/guides/deploy-mn-app", + "destination": "/guides/local-to-testnet", "permanent": true }, + { + "source": "/develop/guides/compact-javascript-runtime", + "destination": "/guides/work-with-compiled-contract", + "permanent": true + }, + { + "source": "/develop/guides/use-compact-javascript-implementation", + "destination": "/guides/work-with-compiled-contract", + "permanent": true + }, + { + "source": "/develop/guides/react-wallet-connect", + "destination": "/guides/connect-wallet-frontend", + "permanent": true + }, + { + "source": "/develop/guides/nextjs-wallet-connect", + "destination": "/guides/connect-wallet-frontend", + "permanent": true + }, + { + "source": "/develop/guides/query-the-blockchain", + "destination": "/nodes/node-endpoints", + "permanent": true + }, + { "source": "/develop/guides/:path*", "destination": "/guides/:path*", @@ -176,17 +338,17 @@ }, { "source": "/how-to/react-wallet-connect", - "destination": "/guides/react-wallet-connect", + "destination": "/guides/connect-wallet-frontend", "permanent": true }, { "source": "/how-to/nextjs-wallet-connect", - "destination": "/guides/nextjs-wallet-connect", + "destination": "/guides/connect-wallet-frontend", "permanent": true }, { "source": "/guides/bun-runtime-midnight", - "destination": "/how-to/bun-runtime-midnight", + "destination": "/guides/development-environment", "permanent": true }, { @@ -317,7 +479,7 @@ }, { "source": "/blog/query-the-blockchain", - "destination": "/guides/query-the-blockchain", + "destination": "/nodes/node-endpoints", "permanent": true }, { @@ -438,9 +600,60 @@ }, { "source": "/next/how-to", - "destination": "/category/guides", + "destination": "/guides", "permanent": true }, + { + "source": "/next/how-to/windows-compact-setup", + "destination": "/guides/development-environment", + "permanent": true + }, + { + "source": "/next/how-to/bun-runtime-midnight", + "destination": "/guides/development-environment", + "permanent": true + }, + { + "source": "/next/how-to/midnight-local-network", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/next/how-to/configure-providers", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/next/how-to/deploy-mn-app", + "destination": "/guides/local-to-testnet", + "permanent": true + }, + { + "source": "/next/how-to/compact-javascript-runtime", + "destination": "/guides/work-with-compiled-contract", + "permanent": true + }, + { + "source": "/next/how-to/use-compact-javascript-implementation", + "destination": "/guides/work-with-compiled-contract", + "permanent": true + }, + { + "source": "/next/how-to/react-wallet-connect", + "destination": "/guides/connect-wallet-frontend", + "permanent": true + }, + { + "source": "/next/how-to/nextjs-wallet-connect", + "destination": "/guides/connect-wallet-frontend", + "permanent": true + }, + { + "source": "/next/how-to/query-the-blockchain", + "destination": "/nodes/node-endpoints", + "permanent": true + }, + { "source": "/next/how-to/:path*", "destination": "/guides/:path*", @@ -713,12 +926,12 @@ }, { "source": "/guides/testing", - "destination": "/category/guides", + "destination": "/guides", "permanent": true }, { "source": "/guides/deployment", - "destination": "/guides/deploy-mn-app", + "destination": "/guides/local-to-testnet", "permanent": true }, {