Skip to main content

Example integration

미리 작성된 스마트 컨트랙트와 API, 바로 쓸 수 있는 코드 스니펫으로 Midnight Network 개발을 빠르게 시작하세요.

Try the project

로컬에서 전체 설정을 테스트하려면 아래 단계를 따르세요:

Repository: https://github.com/MeshJS/midnight-setup

Clone the repository

HTTPS 또는 SSH로 저장소를 복제하세요.

  • using https:
git clone https://github.com/MeshJS/midnight-setup.git
  • using ssh:
git clone git@github.com:MeshJS/midnight-setup.git

복제한 디렉터리로 이동하세요.

cd midnight-setup

Install dependencies

워크스페이스 의존성을 설치하세요.

yarn install

Set environment variable

UI 앱에 사용할 네트워크 ID를 설정하세요.

cd packages/ui && echo 'VITE_NETWORK_ID="TestNet"' > .env

Build all packages

모노레포 전체를 빌드하세요.

cd ../../ && yarn build:all

Download fetch parameters

로컬 실행에 필요한 ZK 파라미터를 다운로드하세요.

cd packages/cli && ./fetch-zk-params.sh

Start testnet with Docker

Docker Compose로 로컬 테스트넷을 시작하세요.

docker-compose -f testnet.yml up -d

Run the frontend

UI 앱 개발 서버를 시작하세요.

cd ../ui && yarn start

http://localhost:8080에서 애플리케이션에 접속할 수 있습니다.

Provider setup

컨트랙트 API를 사용하기 전에 프로바이더를 설정하세요.

import { FetchZkConfigProvider } from "@midnight-ntwrk/midnight-js-fetch-zk-config-provider";
import { httpClientProofProvider } from "@midnight-ntwrk/midnight-js-http-client-proof-provider";
import { indexerPublicDataProvider } from "@midnight-ntwrk/midnight-js-indexer-public-data-provider";
import { levelPrivateStateProvider } from "@midnight-ntwrk/midnight-js-level-private-state-provider";
import type { MidnightSetupContractProviders } from "@meshsdk/midnight-setup";

export async function setupProviders(): Promise<MidnightSetupContractProviders> {
const wallet = window.midnight?.mnLace;
if (!wallet) {
throw new Error("Please install Lace Beta Wallet for Midnight Network");
}

const walletAPI = await wallet.enable();
const walletState = await walletAPI.state();
const uris = await wallet.serviceUriConfig();

return {
privateStateProvider: levelPrivateStateProvider({
privateStateStoreName: "my-dapp-state",
}),
zkConfigProvider: new FetchZkConfigProvider(
window.location.origin,
fetch.bind(window),
),
proofProvider: httpClientProofProvider(uris.proverServerUri),
publicDataProvider: indexerPublicDataProvider(
uris.indexerUri,
uris.indexerWsUri,
),
walletProvider: {
coinPublicKey: walletState.coinPublicKey,
encryptionPublicKey: walletState.encryptionPublicKey,
balanceTx: (tx, newCoins) => {
return walletAPI.balanceAndProveTransaction(tx, newCoins);
},
},
midnightProvider: {
submitTx: (tx) => {
return walletAPI.submitTransaction(tx);
},
},
};
}

Core operations

SDK의 핵심 컨트랙트 워크플로 예제입니다.

Deploy a contract

새 컨트랙트를 배포하고 주소를 확인합니다.

import { MidnightSetupAPI } from "@meshsdk/midnight-setup";
import { setupProviders } from "./providers";

async function deployContract() {
const providers = await setupProviders();
const contractInstance = new MyContract({});
const api = await MidnightSetupAPI.deployContract(providers, contractInstance);

console.log("Contract deployed at:", api.deployedContractAddress);
return api;
}

Join existing contract

주소로 이미 배포된 컨트랙트에 연결합니다.

async function joinContract(contractAddress: string) {
const providers = await setupProviders();
const contractInstance = new MyContract({});
const api = await MidnightSetupAPI.joinContract(providers, contractInstance, contractAddress);

return api;
}

Read contract state

컨트랙트 상태와 원장 상태를 조회합니다.

// Get contract state
const contractState = await api.getContractState();
console.log("Contract data:", contractState.data);

// Get ledger state
const ledgerState = await api.getLedgerState();
console.log("Message:", ledgerState.ledgerState?.message);

React integration

아래 훅과 컴포넌트 패턴으로 SDK를 React 앱에 연결하세요.

Custom hook

배포와 참여 로직을 재사용 가능한 React 훅으로 감쌉니다.

import { useState, useCallback } from 'react';
import { MidnightSetupAPI } from '@meshsdk/midnight-setup';
import { setupProviders } from './providers';

export const useMidnightContract = () => {
const [api, setApi] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);

const deployContract = useCallback(async (contractInstance) => {
setIsLoading(true);
setError(null);

try {
const providers = await setupProviders();
const newApi = await MidnightSetupAPI.deployContract(providers, contractInstance);
setApi(newApi);
return newApi;
} catch (err) {
setError(err.message);
throw err;
} finally {
setIsLoading(false);
}
}, []);

const joinContract = useCallback(async (contractInstance, address) => {
setIsLoading(true);
setError(null);

try {
const providers = await setupProviders();
const newApi = await MidnightSetupAPI.joinContract(providers, contractInstance, address);
setApi(newApi);
return newApi;
} catch (err) {
setError(err.message);
throw err;
} finally {
setIsLoading(false);
}
}, []);

return {
api,
deployContract,
joinContract,
isLoading,
error
};
};

React component

위 훅을 사용한 간단한 컨트랙트 관리 UI 예제입니다.

import React, { useState } from 'react';
import { useMidnightContract } from './hooks/useMidnightContract';

function ContractManager() {
const {
api,
deployContract,
joinContract,
isLoading,
error
} = useMidnightContract();

const [contractAddress, setContractAddress] = useState('');

const handleDeploy = async () => {
try {
const contractInstance = new MyContract({});
const newApi = await deployContract(contractInstance);
console.log('Deployed:', newApi.deployedContractAddress);
} catch (err) {
console.error('Deploy failed:', err);
}
};

const handleJoin = async () => {
try {
const contractInstance = new MyContract({});
await joinContract(contractInstance, contractAddress);
console.log('Joined contract:', contractAddress);
} catch (err) {
console.error('Join failed:', err);
}
};

return (
<div className="contract-manager">
<h2>Contract Manager</h2>

{error && (
<div className="error">Error: {error}</div>
)}

<div className="actions">
<button onClick={handleDeploy} disabled={isLoading}>
{isLoading ? 'Deploying...' : 'Deploy Contract'}
</button>

<div className="join-section">
<input
type="text"
placeholder="Contract Address"
value={contractAddress}
onChange={(e) => setContractAddress(e.target.value)}
/>
<button onClick={handleJoin} disabled={isLoading}>
Join Contract
</button>
</div>
</div>
</div>
);
}

Error handling

아래 패턴으로 SDK 오류를 처리하고 UI를 보호하세요.

Common error patterns

알려진 SDK 오류를 사용자 친화적 메시지로 변환합니다.

const handleMidnightError = (error: Error) => {
if (error.message.includes('Please install Lace Beta Wallet')) {
return 'Please install Lace Beta Wallet for Midnight Network';
}

if (error.message.includes('Insufficient funds')) {
return 'Insufficient funds for transaction';
}

if (error.message.includes('Contract not found')) {
return 'Contract address not found or invalid';
}

return 'An unexpected error occurred';
};

Error boundary

렌더링 오류를 잡아서 폴백 UI를 표시합니다.

import React from 'react';

export class MidnightErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

static getDerivedStateFromError(error) {
return { hasError: true };
}

componentDidCatch(error, errorInfo) {
console.error('Midnight Error:', error, errorInfo);
}

render() {
if (this.state.hasError) {
return (
<div className="error-fallback">
<h2>Something went wrong with Midnight Network</h2>
<button onClick={() => window.location.reload()}>
Reload Page
</button>
</div>
);
}

return this.props.children;
}
}