Skip to main content

Hello world contract와 상호작용하기

이 튜토리얼에서는 Midnight Preprod 네트워크에 배포된 hello world contract와 상호작용하는 CLI를 구축합니다. contract circuit 호출, public state 읽기, DUST 잔액 관리 방법을 다룹니다.

Prerequisites

시작하기 전에 다음을 준비하세요:

  • deployment.json에 contract address가 저장된 배포 완료 hello world contract. hello world contract 배포하기 튜토리얼을 먼저 완료해야 합니다.
  • 64자리 wallet seed
  • npm install로 모든 의존성이 설치된 상태
  • proof server 실행 중. 설정 방법은 proof server 가이드를 참조하세요.
  • 배포 과정에서 생성한 src/utils.ts 파일

hello world contract 배포하기 튜토리얼을 완료했다면 프로젝트 구조가 다음과 같아야 합니다:

my-midnight-contract/
├── contracts/
│ └── managed/
│ └── hello-world/
│ ├── compiler/
│ ├── contract/
│ ├── keys/
│ └── zkir/
│ └── hello-world.compact
├── src/
│ ├── deploy.ts
│ └── utils.ts
├── deployment.json
├── package.json
└── tsconfig.json
1

Add CLI script to package.json

CLI 애플리케이션 실행을 위한 script를 추가합니다.

package.jsonscripts 섹션을 다음과 같이 업데이트하세요:

{
"scripts": {
"compile": "compact compile contracts/hello-world.compact contracts/managed/hello-world",
"build": "tsc",
"deploy": "tsx src/deploy.ts",
"cli": "tsx src/cli.ts"
}
}

CLI 애플리케이션을 실행하는 cli script가 추가됩니다.

2

Create the CLI application file

컨트랙트 작업을 위한 대화형 인터페이스를 제공하는 CLI 파일을 작성합니다.

src/cli.ts를 생성하고 다음 코드를 추가하세요:

import { createInterface } from 'node:readline/promises';
import { stdin, stdout } from 'node:process';
import * as fs from 'node:fs';
import * as Rx from 'rxjs';

// Midnight SDK import
import { findDeployedContract } from '@midnight-ntwrk/midnight-js-contracts';

// utils.ts 파일의 공유 유틸리티
import {
createWallet,
createProviders,
compiledContract,
HelloWorld
} from './utils.js';

각 import의 역할:

  • readline: 대화형 CLI 생성
  • fs: 배포 설정 파일 읽기
  • Rx (RxJS): 지갑 동기화를 위한 반응형 상태 관리
  • findDeployedContract: 기존 배포된 컨트랙트에 연결
  • 공유 유틸리티: 배포 시 사용한 지갑 및 provider 설정 재사용
3

Implement contract connection logic

cli.ts에 배포 정보 로드, 지갑 생성, 배포된 컨트랙트 연결을 담당하는 메인 함수를 추가하세요.

// ─── 메인 CLI 스크립트 ───────────────────────────────────────────────────────────

async function main() {
console.log('\n╔══════════════════════════════════════════════════════════╗');
console.log('║ Hello World Contract CLI (Preprod) ║');
console.log('╚══════════════════════════════════════════════════════════╝\n');

// deployment.json 확인
if (!fs.existsSync('deployment.json')) {
console.error('No deployment.json found! Run `npm run deploy` first.\n');
process.exit(1);
}

const deployment = JSON.parse(fs.readFileSync('deployment.json', 'utf-8'));
console.log(` Contract: ${deployment.contractAddress}\n`);

const rl = createInterface({ input: stdin, output: stdout });

try {
// wallet seed(지갑 시드) 입력 받기
const seed = await rl.question(' Enter your wallet seed: ');

console.log('\n Connecting to Midnight Preprod...');
const walletCtx = await createWallet(seed.trim());

console.log(' Syncing wallet...');
await Rx.firstValueFrom(
walletCtx.wallet.state().pipe(
Rx.throttleTime(5000),
Rx.filter((s) => s.isSynced)
)
);

console.log(' Setting up providers...');
const providers = await createProviders(walletCtx);

console.log(' Joining contract...');
const contract = await findDeployedContract(providers, {
contractAddress: deployment.contractAddress,
compiledContract,
privateStateId: 'helloWorldState',
initialPrivateState: {},
});

console.log(' Connected!\n');

이 코드가 수행하는 작업:

  • deployment.json에 컨트랙트 정보가 있는지 확인
  • wallet seed를 입력받아 지갑 접근 복원
  • utils.ts의 공유 유틸리티로 지갑 인스턴스 생성
  • 지갑을 Preprod와 동기화하여 최신 블록체인 상태 확보
  • 컨트랙트 상호작용에 필요한 provider 설정
  • findDeployedContract로 배포된 컨트랙트에 연결. 이 함수는:
    • 지정된 주소에 컨트랙트 존재 여부 확인
    • 컨트랙트의 현재 상태 로드
    • 로컬 private state 저장소 설정
    • circuit 호출이 준비된 컨트랙트 인스턴스 반환
4

Build the interactive menu

사용자에게 옵션을 표시하고 선택을 처리하는 메인 메뉴 루프를 만듭니다.

cli.ts의 메인 함수에 이어서 추가하세요:

    // 메인 메뉴 루프
let running = true;
while (running) {
const dust = (
await Rx.firstValueFrom(
walletCtx.wallet.state().pipe(Rx.filter((s) => s.isSynced))
)
).dust.walletBalance(new Date());

console.log('─────────────────────────────────────────────────────────────────');
console.log(` DUST: ${dust.toLocaleString()}`);
console.log('─────────────────────────────────────────────────────────────────');
const choice = await rl.question(
' [1] Store a message\n [2] Read current message\n [3] Exit\n > '
);

switch (choice.trim()) {
case '1':
try {
const message = await rl.question('\n Enter message: ');
console.log(' Storing message (this may take 20-30 seconds)...\n');
const tx = await contract.callTx.storeMessage(message);
console.log(` ✅ Message stored!`);
console.log(` Transaction: ${tx.public.txId}`);
console.log(` Block: ${tx.public.blockHeight}\n`);
} catch (e) {
console.error(
` ❌ Error: ${e instanceof Error ? e.message : e}\n`
);
}
break;

case '2':
try {
console.log('\n Reading message from blockchain...');
const state = await providers.publicDataProvider.queryContractState(
deployment.contractAddress
);
if (state) {
const ledgerState = HelloWorld.ledger(state.data);
console.log(
` Current message: "${ledgerState.message || '(empty)'}"\n`
);
} else {
console.log(' No message found.\n');
}
} catch (e) {
console.error(
` ❌ Error: ${e instanceof Error ? e.message : e}\n`
);
}
break;

case '3':
running = false;
break;
}
}

await walletCtx.wallet.stop();
console.log('\n Goodbye!\n');
} finally {
rl.close();
}
}

main().catch(console.error);

CLI 애플리케이션의 메인 진입점입니다. 배포 정보를 로드하고, 지갑을 생성하고, 컨트랙트에 연결한 뒤 메인 메뉴 루프에서 사용자 입력을 처리합니다.

5

Run the CLI application

이제 배포된 컨트랙트와 상호작용할 CLI를 실행할 수 있습니다.

다음 명령어로 CLI를 시작하세요:

npm run cli

CLI가 다음 순서로 진행됩니다:

  1. 배포 시 사용한 wallet seed를 입력합니다.
  2. 지갑이 Preprod와 동기화될 때까지 대기합니다 (보통 수 초 소요).
  3. 메뉴에 표시되는 DUST 잔액을 확인합니다.
  4. 원하는 작업을 선택합니다.

Store message example session

╔══════════════════════════════════════════════════════════╗
║ Hello World Contract CLI (Preprod) ║
╚══════════════════════════════════════════════════════════╝

Contract: 0x1234567890abcdef...

Enter your wallet seed: ********************************

Connecting to Midnight Preprod...
Syncing wallet...
Setting up providers...
Joining contract...
Connected!

─────────────────────────────────────────────────────────────────
DUST: 1,500,000
─────────────────────────────────────────────────────────────────
[1] Store a message
[2] Read current message
[3] Exit
> 1

Enter message: Welcome to the Midnight Network!
Storing message (this may take 20-30 seconds)...

✅ Message stored!
Transaction: 0xabcd1234...
Block: 123456

Read message example session

─────────────────────────────────────────────────────────────────
DUST: 1,450,000
─────────────────────────────────────────────────────────────────
[1] Store a message
[2] Read current message
[3] Exit
> 2

Reading message from blockchain...
Current message: "Welcome to the Midnight Network!"

Troubleshooting

자주 발생하는 문제와 해결 방법입니다.

Contract not found error

"No deployment.json found" 메시지가 표시되면:

  • npm run deploy가 성공적으로 완료되었는지 확인하세요.
  • 프로젝트 루트에 deployment.json이 있는지 확인하세요.
  • 올바른 디렉토리에서 CLI를 실행하고 있는지 확인하세요.

Insufficient DUST

DUST 부족으로 메시지 저장에 실패하면:

  • 메뉴에서 DUST 잔액을 확인하세요.
  • 최근 자금을 입금했다면 DUST 생성까지 잠시 기다리세요.
  • tNight token이 있는지 확인하세요. DUST는 tNight token에서 생성됩니다.
  • Preprod faucet에서 추가 tNight token을 받으세요.

Invalid seed error

wallet seed가 거부되면:

  • 올바른 64자리 16진수 seed인지 확인하세요.
  • 오타나 불필요한 공백이 없는지 확인하세요.
  • 컨트랙트 배포 시 사용한 동일한 seed인지 확인하세요.

Next steps

CLI로 배포된 컨트랙트와 상호작용할 수 있게 되었습니다. 다음으로 DApp용 웹 인터페이스를 구축해 보세요. 컨트랙트의 GUI를 만들려면 React wallet connector 가이드를 참조하세요.

지갑 메서드 및 컨트랙트 상호작용의 전체 API 문서는 DApp Connector API 레퍼런스에서 확인할 수 있습니다.