Skip to main content
For the complete documentation index, see llms.txt

Networks and environments

이 가이드로 Midnight이 운영하는 네트워크를 이해하고, 지금 하려는 작업에 맞는 네트워크를 고르고, DApp을 그 네트워크에 연결하고, 지갑에 자금을 넣고, 준비가 되면 mainnet으로 옮기세요. 네트워크 전반을 설명하고, 도구를 설정할 때 쓸 정확한 endpoint와 ID를 제공하며, 각 작업을 하나씩 짚은 뒤 마지막에 실제로 동작했는지 확인하는 테스트로 마무리합니다. 아무것도 없는 상태에서 시작한다면 먼저 quickstart로 프로젝트를 스캐폴딩하고, 네트워크를 고르거나 전환해야 할 때 이 문서로 돌아오세요.

Prerequisites

이 가이드의 모든 절차에 공통으로 적용됩니다:

  • Node.js 22 버전 이상 설치.
  • 로컬 네트워크와 proof server를 위한 Docker 설치 및 실행.
  • 검증 테스트를 위한 Vitest@midnight-ntwrk/midnight-js-network-id를 테스트 워크스페이스에 설치.

The Midnight networks

Midnight은 하나의 프로덕션 네트워크를 운영하고, 개발용으로 세 개의 환경을 유지합니다. 모든 환경이 동일한 스택을 실행하므로, DApp은 코드가 아니라 설정만 바꿔 환경 사이를 오갑니다.

전체 구성은 네 개의 네트워크로 이루어집니다:

  • **undeployed**는 로컬 개발 네트워크입니다. Midnight node, indexer, proof server가 여러분의 머신에서 Docker로 실행됩니다. genesis 지갑에 미리 자금이 들어 있어, 시작한 지 몇 분 안에 배포할 수 있습니다. Running a local network를 참고하세요.
  • **preview**는 초기 개발과 실험을 위한 public 테스트 네트워크로, 코어 엔지니어링 팀이 운영합니다.
  • **preprod**는 mainnet 배포 전 최종 검증을 위한 public 테스트 네트워크입니다. 테스트 네트워크 중 mainnet을 가장 가깝게 추종합니다.
  • **mainnet**은 프로덕션 네트워크입니다. mainnet의 토큰은 실제 가치를 지니며 faucet이 없습니다.

각 public 네트워크는 RPC로 자신을 식별합니다. system_chain 메서드는 Midnight Preview, Midnight Preprod, Midnight Mainnet 중 하나를 반환합니다. 모든 네트워크는 DApp이 통신하는 동일한 세 가지 서비스를 노출합니다. node(HTTPS·WebSocket 상의 JSON-RPC), indexer(HTTP·WebSocket 상의 GraphQL), 그리고 proof server입니다. node와 indexer는 네트워크마다 다르지만, proof server는 여러분의 private 데이터를 다루기 때문에 어떤 네트워크를 대상으로 하든 로컬 6300 포트에서 실행됩니다. 모든 endpoint는 Environment reference에 정리되어 있습니다.

The testnet-02 name is retired

예전 문서나 도구는 testnet-02라는 네트워크를 참조하기도 합니다. 이 네트워크는 폐기되었고 그 endpoint는 더 이상 연결되지 않습니다. 대신 previewpreprod를 사용하세요.

Network selection at a glance

주어진 작업에 어떤 네트워크를 대상으로 할지 정리한 표입니다. 무언가를 설정하기 전에 먼저 참고하세요. 각 선택에 따른 설정과 자금 조달의 결과를 함께 표시했습니다.

작업네트워크자금 조달위험에 노출되는 가치
컨트랙트 반복 개발, 테스트 실행, CIundeployedgenesis 지갑에 미리 자금이 있어 faucet 불필요없음
개발 초기에 공유 public 인프라를 대상으로 테스트previewPreview faucet에서 무료 tNIGHT, 요청량 제한없음
프로덕션 출시 전 최종 검증preprodPreprod faucet에서 무료 tNIGHT, 요청량 제한없음
프로덕션 운영mainnet실제 NIGHT, 등록 후 DUST 생성실제

속도를 위해 undeployed에서 시작하고, 공유 인프라나 지속되는 체인이 필요하면 previewpreprod로 옮기고, 출시 전에 preprod에서 검증하고, mainnet은 신중한 최종 단계로 다루세요. 각 선택의 비용은 Funding and transaction cost에서 설명합니다.

Environment reference

각 환경의 endpoint, network ID, 자금 조달 출처입니다. 지갑, indexer, 도구를 이 값에 맞춰 설정하세요. 다른 페이지들은 이 값을 다시 적지 않고 이곳으로 링크합니다.

midnight-local-dev로 실행하는 로컬 개발 네트워크입니다. 모든 서비스가 여러분의 머신에서 Docker로 실행됩니다. 설정은 Running a local network에서 다룹니다.

ServiceValue
Network IDundeployed
Node RPChttp://localhost:9944
Indexer (GraphQL)http://localhost:8088/api/v4/graphql
Indexer (WebSocket)ws://localhost:8088/api/v4/graphql/ws
Proof serverhttp://localhost:6300
Faucet없음. genesis 지갑에 미리 자금이 들어 있고, 자금 조달 메뉴가 계정당 50,000 tNIGHT을 전송합니다.
Address prefixesmn_addr_undeployed, mn_shield-addr_undeployed, mn_dust_undeployed
Block explorers없음

주소는 Bech32m으로 인코딩되며, prefix가 주소 유형과 네트워크를 나타냅니다. mainnet은 접미사 없는 prefix를 쓰고(예: mn_addr), 나머지 네트워크는 모두 이름을 덧붙입니다(예: mn_addr_preprod). 지갑 viewing key도 mn_shield-esk prefix로 같은 규칙을 따릅니다.

Midnight은 개발과 테스트를 위해 public node와 indexer endpoint를 제공합니다. 프로덕션 DApp의 경우, 자체 node를 운영하거나 전용 인프라 제공업체를 사용하는 방안을 고려하세요.

Running a local network

midnight-local-dev로 Midnight 스택 전체, 즉 node, indexer, proof server를 여러분의 머신에서 Docker로 실행하세요. 이 도구는 미리 자금이 들어간 genesis 지갑을 초기화하고 자금 조달 메뉴를 제공하므로, faucet 없이 위험 부담 없이 몇 분 안에 배포하고 트랜잭션을 보낼 수 있습니다.

Procedure

  1. 저장소를 클론하고 의존성을 설치합니다:

    git clone https://github.com/midnightntwrk/midnight-local-dev.git
    cd midnight-local-dev
    npm install
  2. 네트워크를 시작합니다:

    npm start

    이 명령은 Docker 이미지를 받아오고(버전은 standalone.yml에 고정되어 있음), health check와 함께 node, indexer, proof server를 시작하고, 미리 채굴된 NIGHT을 보유한 genesis master 지갑을 초기화하고, 수수료를 낼 수 있도록 그 지갑을 DUST용으로 등록한 뒤, 자금 조달 메뉴를 제공합니다:

    Choose an option:
    [1] Fund accounts from config file (NIGHT + DUST registration)
    [2] Fund accounts by public key (NIGHT transfer only)
    [3] Display wallets
    [4] Exit
  3. 개발에 사용할 지갑에 자금을 넣습니다. 1번은 계정이 담긴 JSON 파일을 읽어(accounts.example.jsonaccounts.json으로 복사하고 24단어 니모닉을 추가) 각 계정에 tNIGHT을 전송하고 DUST 생성을 위해 등록합니다. 2번은 붙여넣은 Bech32m 주소로 tNIGHT을 전송하며, 수신자가 직접 DUST용으로 등록합니다. 어느 쪽이든 각 계정은 50,000 tNIGHT을 받고, 작업당 최대 10개 계정까지 처리합니다.

  4. 컨테이너만 필요하다면 지갑 도구를 건너뛰고 Docker Compose를 직접 사용하세요. 이 방식에서는 genesis 자금 조달과 DUST 등록을 직접 처리합니다:

    docker compose -f standalone.yml up -d # start
    docker compose -f standalone.yml ps # status
    docker compose -f standalone.yml logs -f # logs
    docker compose -f standalone.yml down # stop

Verification

로컬 endpoint가 응답합니다. node는 dev 체인을 보고하며 health check를 제공하고, indexer는 블록을 제공하고, proof server는 연결을 받아들입니다.

local-network.test.ts
import { describe, it, expect } from 'vitest';

describe('local network', () => {
it('node is healthy', async () => {
const res = await fetch('http://localhost:9944/health').then((r) => r.json());
expect(res.isSyncing).toBe(false);
});

it('indexer serves blocks', async () => {
const res = await fetch('http://localhost:8088/api/v4/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: '{ block { height } }' }),
}).then((r) => r.json());
expect(res.data.block.height).toBeGreaterThan(0);
});

it('proof server reports ok', async () => {
const res = await fetch('http://localhost:6300/health').then((r) => r.json());
expect(res.status).toBe('ok');
});
});
✓ local-network.test.ts > local network > node is healthy
✓ local-network.test.ts > local network > indexer serves blocks
✓ local-network.test.ts > local network > proof server reports ok

Test Files 1 passed (1)
Tests 3 passed (3)

Local network troubleshooting

로컬 스택에서 마주치기 쉬운 실패 유형과 그 해결책입니다.

증상해결
Bind for 0.0.0.0:9944 failed: port is already allocated이전 실행이 아직 포트를 점유하고 있습니다. docker compose -f standalone.yml down을 실행하거나, lsof -i :9944로 점유 중인 프로세스를 찾으세요.
indexer가 첫 시작에서 block number 1 not found로 종료됨새 체인에서 발생하는 시작 시점 경합입니다. indexer가 node가 아직 생성하지 않은 블록을 요청한 것입니다. docker start midnight-indexer로 다시 시작하면 블록이 생긴 뒤 정상적으로 물립니다.
Operation failed: Expected undeployed address, got Preprod address지갑이 잘못된 네트워크에 있습니다. Lace의 Settings에서 Undeployed 네트워크로 전환한 뒤 그 unshielded 주소를 사용하세요.
컨테이너가 시작되지 않음Docker가 실행 중인지 확인한 뒤 docker compose -f standalone.yml pullup을 실행하고, 실패한 서비스의 로그를 docker compose -f standalone.yml logs -f로 확인하세요.
시작 후 지갑 sync가 느림indexer가 node를 따라잡는 중입니다. curl http://localhost:9944/health로 node가 블록을 생성하는지 확인하고 indexer 로그를 지켜보세요.

Connecting a DApp to a network

network ID를 설정하고 그에 맞는 endpoint를 연결해 DApp을 원하는 네트워크로 향하게 하세요. 흔한 함정은 서로 다른 네트워크의 값을 섞는 것입니다. 예를 들어 preprod network ID에 preview indexer URL을 쓰는 경우입니다. ID와 endpoint를 한곳에 함께 두어 서로 어긋나지 않게 하세요.

Procedure

  1. Network selection at a glance를 참고해 대상 네트워크를 고릅니다.

  2. provider를 초기화하기 전에 network ID를 설정합니다. Midnight.js는 주소를 정규화하고 트랜잭션을 구성할 때(즉 deployContractcallTx 경로) 이 값을 읽으므로, 어떤 컨트랙트 작업보다 먼저 설정해야 합니다:

    import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id';

    setNetworkId('preprod');

    /** Supported network IDs: 'mainnet', 'preview', 'preprod', 'undeployed' */

    기본값은 없습니다. setNetworkId를 호출하기 전까지 getNetworkId()Network ID has not been configured를 던집니다. 이 호출은 문자열을 있는 그대로 저장하므로, 네트워크 이름을 잘못 쓰더라도 여기서 실패하지 않고 나중에 그 ID를 소비하는 컴포넌트에서 드러납니다.

  3. Environment reference의 값을 사용해 network ID와 그 endpoint를 하나의 설정 객체에 함께 두세요. proof server URL은 모든 네트워크에서 로컬로 유지된다는 점에 유의하세요:

    const NETWORKS = {
    undeployed: {
    node: 'http://localhost:9944',
    indexer: 'http://localhost:8088/api/v4/graphql',
    indexerWS: 'ws://localhost:8088/api/v4/graphql/ws',
    proofServer: 'http://localhost:6300',
    },
    preprod: {
    node: 'https://rpc.preprod.midnight.network',
    indexer: 'https://indexer.preprod.midnight.network/api/v4/graphql',
    indexerWS: 'wss://indexer.preprod.midnight.network/api/v4/graphql/ws',
    proofServer: 'http://localhost:6300',
    },
    } as const;

    const network = NETWORKS['preprod'];
  4. endpoint를 provider에 전달합니다. private state, ZK 설정, 지갑 provider를 포함한 전체 provider 객체는 Configuring providers for a contract에서 다룹니다:

    import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';

    const publicDataProvider = indexerPublicDataProvider(network.indexer, network.indexerWS);
  5. create-mn-app으로 스캐폴딩한 프로젝트(hello-world 템플릿)에서는 대신 setup 스크립트로 네트워크를 선택합니다. 이 선택은 전환하기 전까지 유지됩니다:

    npm run setup -- --network preview # runs on preview and makes it active
    npm run network preprod # switch the active network later

    스캐폴드의 네트워크 스크립트는 undeployed, preview, preprod를 받습니다. mainnet은 스캐폴드 대상이 아닙니다. 위에서 보인 대로 provider로 직접 연결하세요.

Verification

각 network ID가 SDK를 통해 왕복하고, 설정한 endpoint가 예상한 체인 정체성과 현재 블록 높이로 응답합니다.

networks.test.ts
import { describe, it, expect } from 'vitest';
import { setNetworkId, getNetworkId } from '@midnight-ntwrk/midnight-js-network-id';

const rpc = (url: string, method: string) =>
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', method, params: [], id: 1 }),
}).then((r) => r.json());

const indexerBlock = (url: string) =>
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: '{ block { height } }' }),
}).then((r) => r.json());

const networks = [
{
id: 'preview',
chain: 'Midnight Preview',
node: 'https://rpc.preview.midnight.network',
indexer: 'https://indexer.preview.midnight.network/api/v4/graphql',
},
{
id: 'preprod',
chain: 'Midnight Preprod',
node: 'https://rpc.preprod.midnight.network',
indexer: 'https://indexer.preprod.midnight.network/api/v4/graphql',
},
{
id: 'mainnet',
chain: 'Midnight Mainnet',
node: 'https://rpc.mainnet.midnight.network',
indexer: 'https://indexer.mainnet.midnight.network/api/v4/graphql',
},
];

describe.each(networks)('$id', ({ id, chain, node, indexer }) => {
it('sets the network ID', () => {
setNetworkId(id);
expect(getNetworkId()).toBe(id);
});

it('reaches the node RPC', async () => {
const res = await rpc(node, 'system_chain');
expect(res.result).toBe(chain);
});

it('reaches the indexer', async () => {
const res = await indexerBlock(indexer);
expect(res.data.block.height).toBeGreaterThan(0);
});
});
✓ networks.test.ts > 'preview' > sets the network ID
✓ networks.test.ts > 'preview' > reaches the node RPC
✓ networks.test.ts > 'preview' > reaches the indexer
✓ networks.test.ts > 'preprod' > sets the network ID
✓ networks.test.ts > 'preprod' > reaches the node RPC
✓ networks.test.ts > 'preprod' > reaches the indexer
✓ networks.test.ts > 'mainnet' > sets the network ID
✓ networks.test.ts > 'mainnet' > reaches the node RPC
✓ networks.test.ts > 'mainnet' > reaches the indexer

Test Files 1 passed (1)
Tests 9 passed (9)

undeployed 네트워크는 로컬 컨테이너가 실행되는 동안에만 존재하므로 이 표에 없습니다. local network를 띄운 상태라면 동일한 검사가 http://localhost:9944http://localhost:8088/api/v4/graphql을 대상으로도 통과합니다.

Funding and transaction cost

Midnight의 모든 트랜잭션은 DUST를 소비하며, DUST가 어디서 오느냐가 네트워크 사이의 가장 실질적인 차이입니다. 이 두 토큰 모델을 한 번 이해해 두면 이후 각 네트워크에서 혼란에 빠지는 시간을 아낄 수 있습니다.

NIGHT은 네이티브 유틸리티 토큰이며, 이를 보유하는 것이 DUST에 대한 자격이 됩니다. DUST는 shielded, 전송 불가 리소스로, 수수료가 이것으로 지불됩니다. 등록된 NIGHT은 시간이 지나면서 NIGHT당 약 5 DUST의 캡까지 DUST를 생성하고 약 일주일에 걸쳐 다시 채워지므로, 자금이 든 지갑은 트랜잭션 처리 능력을 영구히 소진하는 것이 아니라 다시 회복합니다. Tokens on Midnight이 이 모델을 소개하고, DUST architecture가 생성·감쇠와 프로토콜 파라미터를 다룹니다.

로컬 네트워크에서는 genesis 지갑에 미리 자금이 있고 이미 DUST용으로 등록되어 있으며, 자금 조달 메뉴가 여러분의 테스트 지갑으로 tNIGHT을 전송합니다. DUST는 약 5분이면 생성됩니다. 필요가 없으므로 faucet도 없습니다. Running a local network를 참고하세요.

**previewpreprod**에서는 네트워크의 faucet에서 무료 tNIGHT을 요청하고(Preprod faucet은 요청당 1,000 tNIGHT을 보내며, 두 faucet 모두 요청량이 제한됨) 지갑에서 tDUST 생성을 위해 등록하세요. Funding a wallet 가이드가 faucet, Lace의 Generate tDUST 흐름, 그리고 스크립트로 처리하는 wallet SDK 경로를 안내합니다. 테스트 토큰은 실제 가치가 없습니다.

**mainnet**에는 faucet이 없습니다. 현재 대부분의 NIGHT은 Cardano에 cNIGHT으로 보유되며, DUST 생성은 cross-chain으로 이루어집니다. Cardano reward 주소를 Midnight DUST public key와 함께 등록하면(cNgD DApp이 이를 처리) 보유한 cNIGHT이 Midnight에서 DUST를 생성합니다. 이 등록은 Cardano에서 확정된 뒤 Midnight node에 도달해야 하며 약 12시간이 걸리므로, 프로덕션 지갑은 출시일보다 훨씬 앞서 자금을 넣어 두세요.

Preparing a DApp for mainnet

preprod에서 동작하는 DApp을 프로덕션 네트워크로 옮깁니다. 방식 자체는 다른 네트워크 전환과 똑같은 설정 변경입니다. mainnet이 다른 점은 자금 조달이 cross-chain이라 느리고, 실수가 실제 가치를 대가로 치르며, public 테스트 인프라의 보장이 적용되지 않는다는 것입니다.

Prerequisites

Procedure

  1. 먼저 preprod에서 전체 배포·상호작용 흐름을 검증합니다. mainnet에 가장 가까운 네트워크이므로, 여기서 실패하는 것은 프로덕션에서도 실패합니다.

  2. 프로덕션 지갑에 자금을 넣습니다. cNgD DApp을 통해 cNIGHT을 DUST 생성용으로 등록하고, Funding and transaction cost에서 설명한 대로 등록이 반영되기까지 약 12시간을 기다리세요. 트랜잭션을 시도하기 전에 지갑에 DUST 잔액이 표시되는지 확인하세요.

  3. 설정을 mainnet으로 향하게 합니다. network ID를 설정하고 endpoint를 교체하면 됩니다. DApp의 다른 부분은 바뀌지 않습니다:

    import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id';

    setNetworkId('mainnet');

    const network = {
    node: 'https://rpc.mainnet.midnight.network',
    indexer: 'https://indexer.mainnet.midnight.network/api/v4/graphql',
    indexerWS: 'wss://indexer.mainnet.midnight.network/api/v4/graphql/ws',
    proofServer: 'http://localhost:6300',
    };

    create-mn-app 스캐폴드는 mainnet 대상을 제공하지 않으므로, Configuring providers for a contract에서처럼 provider를 직접 연결하세요.

  4. 인프라를 결정합니다. public endpoint는 개발과 테스트용으로 제공됩니다. 프로덕션에서는 자체 node와 indexer를 운영하거나 전용 인프라 제공업체를 사용하세요.

  5. 무언가를 공지하기 전에 Mainnet readiness checklist를 처리하세요.

Verification

mainnet endpoint가 프로덕션 체인 정체성과 현재 블록 높이로 응답합니다. 이 가이드의 연결 테스트를 mainnet으로 필터링해 실행하세요:

npx vitest run networks.test.ts -t mainnet
↓ networks.test.ts > 'preview' > sets the network ID
↓ networks.test.ts > 'preview' > reaches the node RPC
↓ networks.test.ts > 'preview' > reaches the indexer
↓ networks.test.ts > 'preprod' > sets the network ID
↓ networks.test.ts > 'preprod' > reaches the node RPC
↓ networks.test.ts > 'preprod' > reaches the indexer
✓ networks.test.ts > 'mainnet' > sets the network ID
✓ networks.test.ts > 'mainnet' > reaches the node RPC
✓ networks.test.ts > 'mainnet' > reaches the indexer

Test Files 1 passed (1)
Tests 3 passed | 6 skipped (9)

Mainnet readiness checklist

프로덕션 출시 전에 이 목록을 처리하세요. 각 항목은 그것을 설명하는 페이지로 링크됩니다.

  • 전체 흐름이 preprod에서 검증되었다. mainnet에 가장 가까운 네트워크에서 배포·상호작용·상태 관찰을 처음부터 끝까지 수행합니다.
  • 보안 체크리스트가 완료되었다. 컨트랙트와 그 주위 DApp에 대해 pre-deployment security checklist를 처리합니다.
  • 업데이트 가능성 결정이 내려졌다. 컨트랙트가 실제 가치를 보유하기 전에 업그레이드 가능 여부와 방식을 결정합니다. Contract updatability and the maintenance authority를 참고하세요.
  • 프로덕션 지갑이 DUST를 생성한다. cNIGHT이 등록되었고, 약 12시간의 등록 지연이 지났으며, 지갑에 DUST 잔액이 표시됩니다. Funding and transaction cost를 참고하세요.
  • 설정의 모든 endpoint가 mainnet endpoint다. previewpreprod URL이 하나도 남아 있지 않습니다. Environment reference를 참고하세요.
  • 인프라 결정이 내려졌다. 자체 node와 indexer를 운영하거나 전용 제공업체를 확보했습니다. public endpoint는 개발과 테스트용입니다.
  • 키 관리가 정리되었다. 컨트랙트와 자금을 통제하는 키에 소유자, 백업, 교체 경로가 있습니다. Security and best practices를 참고하세요.
  • 프로덕션에서 DApp을 관찰할 수 있다. 배포를 확인하고 활동을 지켜보는 데 어떤 block explorer와 indexer 쿼리를 쓸지 알고 있습니다.

Additional resources