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

How to configure providers

provider는 Midnight.js 패키지가 Midnight Network과 상호작용할 때 사용하는 모듈식 구성 요소입니다.

이 가이드에서는 Compact 스마트 컨트랙트를 배포하거나 상호작용하기 전에 Midnight.js provider를 구성하는 방법을 다룹니다.

Prerequisites

이 가이드를 따라가려면 다음이 준비되어 있어야 합니다.

  • keys/zkir/ 디렉터리가 생성된, 컴파일된 Compact 스마트 컨트랙트. 아직 없다면 build your first contract 튜토리얼을 따라 시작하세요.
  • Node.js 22.x 이상이 설치되어 있어야 합니다. NVM으로 설치하세요.
  • Docker가 설치되어 실행 중이어야 합니다. proof server를 실행하고 영지식(ZK) 증명을 생성하는 데 필요합니다.

The MidnightProviders type

MidnightProviders@midnight-ntwrk/midnight-js-types에서 가져오는 제네릭 타입입니다. 컨트랙트에 특화된 세 가지 인자를 받습니다.

import { type MidnightProviders } from '@midnight-ntwrk/midnight-js-types';

// CircuitKeys — 컴파일된 컨트랙트의 circuit 이름들의 union
// PrivateStateId — private state 저장 키의 리터럴 타입
// PrivateState — 컨트랙트의 private state 객체 형태
type MyProviders = MidnightProviders<CircuitKeys, PrivateStateId, PrivateState>;

이러한 별칭은 common-types.ts 파일에 두는 것이 좋습니다. API 패키지와 UI 패키지가 동일한 컨트랙트 타입을 공유하는 경우 특히 유용합니다.

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<MyCircuitKeys, PrivateStateId, MyPrivateState>;

Set the network ID

provider를 초기화하기 전에 setNetworkId를 호출하세요. 모든 Midnight.js 패키지는 이 값을 읽어 올바른 네트워크를 대상으로 삼습니다.

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

setNetworkId('preprod');

/** 지원되는 network ID: 'mainnet', 'preview', 'preprod', 'undeployed' */

Node.js 환경에서는 indexer에 대한 GraphQL subscription이 동작하도록 WebSocket도 polyfill하세요.

import { WebSocket } from 'ws';

globalThis.WebSocket = WebSocket as unknown as typeof globalThis.WebSocket;

Configure the providers

각 provider는 거래 파이프라인에서 한 가지 특정 기능을 담당합니다.

  • private state 저장
  • indexer 쿼리
  • ZK proof 생성
  • 거래 잔액 조정(balancing)
  • 거래 온체인 제출

다음 섹션에서는 각 provider를 구성하는 방법을 다룹니다.

privateStateProvider

private state provider는 컨트랙트의 private state를 로컬 기기에 저장하고 가져옵니다. private state는 네트워크로 전송되지 않습니다.

@midnight-ntwrk/midnight-js-level-private-state-providerlevelPrivateStateProvider를 사용하세요. 이 함수는 private state를 AES-256-GCM으로 암호화한 LevelDB 데이터베이스에 영속화합니다.

import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider';

const privateStateProvider = levelPrivateStateProvider<PrivateStateId, MyPrivateState>({
privateStateStoreName: 'my-contract-private-state',
signingKeyStoreName: 'my-contract-private-state-signing-keys',
privateStoragePasswordProvider: () => 'your-encryption-password',
});

아래 표는 levelPrivateStateProvider 함수의 매개변수를 설명합니다.

ParameterDescription
privateStateStoreNameprivate state용 LevelDB 저장소 이름
signingKeyStoreNamesigning key용 LevelDB 저장소 이름
privateStoragePasswordProvider암호화 비밀번호를 반환하는 함수
Password security

운영 환경에서는 하드코딩된 비밀번호를 사용하지 마세요. 지갑 자격 증명이나 안전한 키 관리 시스템에서 파생하세요.

publicDataProvider

public data provider는 Midnight indexer의 GraphQL API를 통해 온체인 컨트랙트 상태를 쿼리하고 구독합니다. Node.js와 브라우저 환경 모두에서 @midnight-ntwrk/midnight-js-indexer-public-data-providerindexerPublicDataProvider를 사용하세요.

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

const publicDataProvider = indexerPublicDataProvider(
'https://indexer.preprod.midnight.network/api/v4/graphql', // HTTP 쿼리 URL
'wss://indexer.preprod.midnight.network/api/v4/graphql/ws', // WebSocket subscription URL
);

로컬 개발 시에는 다음을 사용하세요.

const publicDataProvider = indexerPublicDataProvider(
'http://localhost:8088/api/v4/graphql',
'ws://localhost:8088/api/v4/graphql/ws',
);
Local development

Midnight은 개발과 테스트 용도로 로컬 네트워크를 제공합니다. main network에 배포하기 전에 로컬 네트워크로 컨트랙트와 provider를 테스트하세요. 자세한 내용은 Midnight local network 가이드를 참고하세요.

zkConfigProvider

ZK configuration provider는 proof provider가 영지식 증명을 생성하는 데 필요한 prover key, verifier key, ZKIR artifact를 공급합니다. 적합한 구현은 ZK artifact를 어디에 저장하는지에 따라 달라집니다.

컨트랙트를 Node.js 환경에서 실행한다면 @midnight-ntwrk/midnight-js-node-zk-config-providerNodeZkConfigProvider를 사용하세요. 이 provider는 로컬 파일 시스템에서 artifact를 읽습니다. 경로는 컴파일된 컨트랙트의 keys/zkir/ 출력이 들어 있는 디렉터리를 가리켜야 합니다.

import { NodeZkConfigProvider } from '@midnight-ntwrk/midnight-js-node-zk-config-provider';

const zkConfigProvider = new NodeZkConfigProvider<'circuitA' | 'circuitB'>(
'/path/to/contract/src/managed/my-contract',
);

타입 매개변수는 컨트랙트가 노출하는 circuit 이름들의 union입니다. 보통 CircuitKeys 별칭과 동일한 타입입니다.

proofProvider

proof provider는 Midnight proof server를 호출해 아직 증명되지 않은 거래로부터 영지식 증명을 생성합니다. @midnight-ntwrk/midnight-js-http-client-proof-providerhttpClientProofProvider를 사용하세요.

이 함수는 proof server URL과 zkConfigProvider 인스턴스를 받습니다. proof server는 동일한 ZK artifact에 접근해야 하며, 이를 zkConfigProvider를 통해 가져옵니다.

import { httpClientProofProvider } from '@midnight-ntwrk/midnight-js-http-client-proof-provider';

const proofProvider = httpClientProofProvider(
'http://localhost:6300', // proof server URL
zkConfigProvider,
);
Proof server

proof server는 로컬에서 실행하는 Docker 컨테이너이거나, 호스팅된 인스턴스를 가리킬 수도 있습니다. 설정 방법은 proof server guide를 참고하세요.

walletProvider

wallet provider는 shielded token을 받는 데 필요한 public key를 노출하고 거래 데이터를 복호화합니다. 또한 수수료를 충당할 UTXO를 선택하고 잔돈 출력을 추가하여 unbound 거래의 잔액을 조정합니다.

Node.js CLI에서는 Wallet SDK facade를 사용해 WalletProvider를 구현할 수 있습니다.

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<FinalizedTransaction> {
const recipe = await this.wallet.balanceUnboundTransaction(
tx,
{ shieldedSecretKeys: this.zswapSecretKeys, dustSecretKey: this.dustSecretKey },
{ ttl },
);
return await this.wallet.finalizeRecipe(recipe);
}
}

이 클래스는 WalletFacade 인스턴스와 함께, wallet seed에서 파생된 ZswapSecretKeysDustSecretKey를 받습니다. SDK가 거래 생애 주기 동안 호출하는 세 가지 메서드를 노출합니다.

  • getCoinPublicKey: 토큰을 받는 데 쓰이는 shielded 주소를 반환합니다.
  • getEncryptionPublicKey: 들어오는 shielded 거래 데이터를 복호화하는 데 쓰이는 key를 반환합니다.
  • balanceTx: 수수료를 충당할 UTXO를 선택하고, 잔돈 출력을 추가하며, 증명 생성과 제출이 가능하도록 거래를 마무리합니다.
DUST requirements

DUST는 Midnight Network에서 거래를 구동하는 네트워크 리소스입니다. 거래 수수료를 지불하려면 지갑에 DUST가 있어야 합니다. 자세한 내용은 Generating DUST programmatically 가이드를 참고하세요.

WalletFacade가 두 기능을 모두 노출하므로, WalletProvider를 구현하는 클래스가 MidnightProvider도 구현할 수 있습니다.

import { type MidnightProvider } from '@midnight-ntwrk/midnight-js-types';
import { type FinalizedTransaction } from '@midnight-ntwrk/ledger-v8';

class MyWalletProvider implements WalletProvider, MidnightProvider {
// ...다른 메서드들...

submitTx(tx: FinalizedTransaction): Promise<string> {
return this.wallet.submitTransaction(tx);
}
}

그런 다음 하나의 인스턴스를 provider 객체에서 walletProvidermidnightProvider로 함께 전달합니다.

Assemble the providers object

각 provider를 초기화했다면, 이를 MidnightProviders 객체로 조립해 스마트 컨트랙트 API에 전달하세요.

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<MyCircuitKeys>('/path/to/contract/managed/my-contract');

const walletProvider = new MyWalletProvider(wallet, zswapSecretKeys, dustSecretKey);

const providers: MyProviders = {
privateStateProvider: levelPrivateStateProvider<PrivateStateId, MyPrivateState>({
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,
};

이 객체를 deployContract 또는 findDeployedContract에 전달하세요:

import { deployContract, findDeployedContract } from '@midnight-ntwrk/midnight-js-contracts';

// 새 컨트랙트 배포
const deployed = await deployContract(providers, {
compiledContract: CompiledMyContract,
privateStateId: myPrivateStateKey,
initialPrivateState: myInitialPrivateState,
});

// 또는 기존 컨트랙트에 연결
const deployed = await findDeployedContract(providers, {
contractAddress,
compiledContract: CompiledMyContract,
privateStateId: myPrivateStateKey,
initialPrivateState: myInitialPrivateState,
});

deployContractfindDeployedContract 함수는 MidnightProviders 객체와 컨트랙트 세부 정보를 매개변수로 받습니다.

Note

이 함수들의 사용법에 대한 자세한 내용은 Midnight.js SDK 문서를 참고하세요.

Next steps

provider를 구성했다면, 이제 Midnight Network에서 스마트 컨트랙트를 배포하고 상호작용할 준비가 되었습니다. 다음 자료에서 이 워크플로의 다음 단계를 다룹니다.