mcpField
E
example-hello-world

example-hello-world

github.com/kali-decoder开发18121 次安装1 天前更新
代码仓库

关于 example-hello-world

为 Midnight Network 开发者提供生成完整、可运行 Hello World DApp 的技能,涵盖 Compact 合约、TypeScript、测试和本地环境配置。内容遵循官方参考实现,并提示按兼容矩阵选择版本。

主要功能

  1. 01生成完整 DApp 项目文件
  2. 02提供 Compact 合约与 TypeScript 代码
  3. 03包含测试和 Docker 配置
  4. 04说明合约编译与本地运行
  5. 05提示检查版本兼容矩阵

适用场景

  1. 01搭建 Midnight Hello World 示例
  2. 02配置本地开发网与测试
  3. 03将模板改造为自定义合约

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/kali-decoder/midnight-skills --skill example-hello-world

安装后在 Claude Code 中输入/example-hello-world确认已加载。

详细介绍

示例 Hello World DApp 技能

此技能可生成完整且可运行的 Midnight DApp,涵盖项目中的每个文件:Compact 合约、TypeScript 工具、测试脚本、Docker 配置和软件包设置。所有代码均与官方 midnightntwrk/example-hello-world 参考实现一致。

主要参考资料:

  • github.com/midnightntwrk/example-hello-world — 官方参考仓库
  • docs.midnight.network/getting-started/hello-world — 官方 Hello World 指南
  • docs.midnight.network/relnotes/support-matrix — 权威版本兼容性矩阵

以下软件包版本是截至 2026 年 5 月的快照。 固定版本前,请从支持矩阵中确认当前经过测试的版本组合,另请参阅 references/volatile.md。切勿安装 @latest。

关键架构说明:

  • 使用 @midnight-ntwrk/testkit-js 的 FluentWalletBuilder 创建钱包
  • 使用 @midnight-ntwrk/midnight-js-contracts 的 deployContract 和 submitCallTx
  • Compact pragma 为 >= 0.23
  • 本地 devnet 的索引器 API 路径使用 /api/v4/graphql
  • MidnightWalletProvider 类封装 WalletFacade,以实现 MidnightProvider 和 WalletProvider 接口
  • syncWallet 函数会在钱包同步期间提供详细的进度日志
  • 测试使用静态种子(ALICE_SEED),以实现可复现的测试
  • privateStateStoreName 使用 Date.now() 实现测试隔离
  • 使用 ESM 的 vitest 需要 NODE_OPTIONS='--experimental-vm-modules'

1) 项目结构

hello-world-local/
├── contracts/
│   ├── hello-world.compact          # Compact smart contract (you create this)
│   ├── index.ts                     # Barrel file for compiled contract exports (create manually)
│   └── managed/
│       └── hello-world/             # Compiler output (auto-generated, do not edit)
│           ├── compiler/
│           ├── contract/
│           ├── keys/
│           └── zkir/
├── src/
│   ├── config.ts                    # Network configuration
│   ├── wallet.ts                    # MidnightWalletProvider + syncWallet
│   ├── providers.ts                 # HelloWorldProviders builder
│   └── test/
│       └── hw.test.ts               # vitest tests (deploy + storeMessage)
├── compose.yml                      # Docker: full local stack
├── package.json
├── tsconfig.json
└── vitest.config.ts

contracts/managed/ 目录由 Compact 编译器创建 — 请勿手动创建或编辑其中的文件。


2) 先决条件

# Node.js 22+ required
node --version

# Docker required (local stack)
docker --version

# Install Compact compiler
curl --proto '=https' --tlsv1.2 -sSf \
  https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh
source $HOME/.local/bin/env
compact --version

3) package.json

{
  "name": "hello-world-local",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "engines": {
    "node": ">=22.0.0"
  },
  "scripts": {
    "compile": "compact compile contracts/hello-world.compact managed/hello-world",
    "test": "NODE_OPTIONS='--experimental-vm-modules' vitest run",
    "test:local": "MIDNIGHT_NETWORK=local yarn test",
    "env:up": "docker compose up -d --wait",
    "env:down": "docker compose down",
    "validate": "yarn env:up && yarn test:local; yarn env:down"
  },
  "dependencies": {
    "@midnight-ntwrk/compact-runtime": "0.16.0",
    "@midnight-ntwrk/ledger-v8": "8.0.3",
    "@midnight-ntwrk/midnight-js-contracts": "4.0.4",
    "@midnight-ntwrk/midnight-js-http-client-proof-provider": "4.0.4",
    "@midnight-ntwrk/midnight-js-indexer-public-data-provider": "4.0.4",
    "@midnight-ntwrk/midnight-js-level-private-state-provider": "4.0.4",
    "@midnight-ntwrk/midnight-js-network-id": "4.0.4",
    "@midnight-ntwrk/midnight-js-node-zk-config-provider": "4.0.4",
    "@midnight-ntwrk/midnight-js-types": "4.0.4",
    "@midnight-ntwrk/midnight-js-utils": "4.0.4",
    "@midnight-ntwrk/testkit-js": "4.0.4",
    "@midnight-ntwrk/wallet-sdk-facade": "3.0.0",
    "axios": "^1.15.0",
    "pino": "^9.0.0",
    "pino-pretty": "^13.0.0",
    "rxjs": "^7.8.2",
    "testcontainers": "^11.13.0",
    "ws": "^8.14.2"
  },
  "devDependencies": {
    "@midnight-ntwrk/midnight-js-compact": "4.0.4",
    "@types/node": "^22.0.0",
    "@types/ws": "^8.5.9",
    "typescript": "^5.7.0",
    "vitest": "^3.0.0"
  },
  "resolutions": {
    "@midnight-ntwrk/ledger-v8": "8.0.3",
    "@midnight-ntwrk/midnight-js-network-id": "4.0.4",
    "@midnight-ntwrk/compact-runtime": "0.16.0"
  },
  "packageManager": "[email protected]"
}

4) tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ES2022",
    "moduleResolution": "bundler",
    "lib": ["ES2022"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "verbatimModuleSyntax": true,
    "noEmit": true,
    "outDir": "dist",
    "rootDir": "."
  },
  "include": ["src/**/*.ts", "contract/**/*.ts"]
}

⚠️ "contract/**/*.ts" 使用单数形式 — 这与仓库完全一致,并涵盖编译器的输出路径。使用复数形式的 "contracts/**/*.ts" 会导致 TypeScript 无提示地无法解析编译后的合约类型。


5) vitest.config.ts

import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'node',
    globals: true,
    testTimeout: 10 * 60_000,
    hookTimeout: 15 * 60_000,
    include: ['src/**/*.test.ts'],
    reporters: ['default'],
    sequence: { concurrent: false },
  },
});

6) contracts/hello-world.compact

请手动创建此文件 — managed/ 中的编译器输出由此文件生成。

pragma language_version >= 0.23;

export ledger message: Opaque<"string">;

export circuit storeMessage(newMessage: Opaque<"string">): [] {
  message = disclose(newMessage);
}

请从 contracts/ 目录进行编译:

cd contracts
compact compile hello-world.compact managed/hello-world
cd ..

预期输出:

Compiling 1 circuits:
  circuit "storeMessage" (k=6, rows=26)

这会生成 contracts/managed/hello-world/contract/index.js(以及密钥、zkir 和编译器目录)。运行测试前请务必先进行编译。


7) contracts/index.ts(barrel 文件 — 手动创建)

请手动创建此文件 — 它会从编译后的合约输出中导入内容。必须先运行 compact compile,生成已编译的合约模块,导入才能成功解析。

import { CompiledContract } from '@midnight-ntwrk/compact-runtime';
import { fileURLToPath } from 'node:url';
import path from 'node:path';

export {
  Contract,
  ledger,
  pureCircuits,
  type Ledger,
  type ImpureCircuits,
  type PureCircuits,
} from './managed/hello-world/contract/index.js';
import { Contract } from './managed/hello-world/contract/index.js';

const currentDir = path.dirname(fileURLToPath(import.meta.url));
export const zkConfigPath = path.resolve(currentDir, 'managed', 'hello-world');

export const CompiledHelloWorldContract = CompiledContract.make(
  'HelloWorldContract',
  Contract,
).pipe(
  CompiledContract.withVacantWitnesses,
  CompiledContract.withCompiledFileAssets(zkConfigPath),
);

8) src/config.ts

export type NetworkConfig = {
  networkId: string;
  indexer: string;
  indexerWS: string;
  node: string;
  nodeWS: string;
  proofServer: string;
  faucet: string;
};

export const LOCAL_CONFIG: NetworkConfig = {
  networkId: 'undeployed',
  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',
  nodeWS: 'ws://127.0.0.1:9944',
  proofServer: 'http://127.0.0.1:6300',
  faucet: '',
};

export function getConfig(): NetworkConfig {
  const network = process.env['MIDNIGHT_NETWORK'] ?? 'local';
  if (network !== 'local') {
    throw new Error(
      `Unknown network: ${network}. This harness only supports 'local'.`,
    );
  }
  return LOCAL_CONFIG;
}

9) src/wallet.ts

import {
  type CoinPublicKey,
  DustSecretKey,
  type EncPublicKey,
  type FinalizedTransaction,
  LedgerParameters,
  ZswapSecretKeys,
} from '@midnight-ntwrk/ledger-v8';
import {
  type MidnightProvider,
  type UnboundTransaction,
  type WalletProvider,
} from '@midnight-ntwrk/midnight-js-types';
import { ttlOneHour } from '@midnight-ntwrk/midnight-js-utils';
import { type WalletFacade, type FacadeState } from '@midnight-ntwrk/wallet-sdk-facade';
import {
  type DustWalletOptions,
  type EnvironmentConfiguration,
  FluentWalletBuilder,
} from '@midnight-ntwrk/testkit-js';
import * as Rx from 'rxjs';
import type { Logger } from 'pino';

export class MidnightWalletProvider implements MidnightProvider, WalletProvider {
  readonly wallet: WalletFacade;

  private constructor(
    private readonly logger: Logger,
    wallet: WalletFacade,
    private readonly zswapSecretKeys: ZswapSecretKeys,
    private readonly dustSecretKey: DustSecretKey,
  ) {
    this.wallet = wallet;
  }

  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);
  }

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

  async start(): Promise<void> {
    this.logger.info('Starting wallet...');
    await this.wallet.start(this.zswapSecretKeys, this.dustSecretKey);
  }

  async stop(): Promise<void> {
    return this.wallet.stop();
  }

  static async build(
    logger: Logger,
    env: EnvironmentConfiguration,
    seed: string,
  ): Promise<MidnightWalletProvider> {
    const dustOptions: DustWalletOptions = {
      ledgerParams: LedgerParameters.initialParameters(),
      additionalFeeOverhead: 1_000n,
      feeBlocksMargin: 5,
    };

    const builder = FluentWalletBuilder.forEnvironment(env)
      .withDustOptions(dustOptions);

    // buildWithoutStarting() returns { wallet, seeds } — seeds contains
    // the derived key material. The cast reflects the documented testkit-js
    // pattern; verify against your installed version if you see type errors.
    const buildResult = await builder.withSeed(seed).buildWithoutStarting();
    const { wallet, seeds } = buildResult as {
      wallet: WalletFacade;
      seeds: {
        masterSeed: string;
        shielded: Uint8Array;
        dust: Uint8Array;
      };
    };

    logger.info(`Wallet built from seed: ${seeds.masterSeed.slice(0, 8)}...`);

    return new MidnightWalletProvider(
      logger,
      wallet,
      ZswapSecretKeys.fromSeed(seeds.shielded),
      DustSecretKey.fromSeed(seeds.dust),
    );
  }
}

function isProgressStrictlyComplete(progress: unknown): boolean {
  if (!progress || typeof progress !== 'object') {
    return false;
  }
  const candidate = progress as { isStrictlyComplete?: unknown };
  if (typeof candidate.isStrictlyComplete !== 'function') {
    return false;
  }
  return (candidate.isStrictlyComplete as () => boolean)();
}

export async function syncWallet(
  logger: Logger,
  wallet: WalletFacade,
  timeout = 300_000,
): Promise<FacadeState> {
  logger.info('Syncing wallet...');
  let emissionCount = 0;
  return Rx.firstValueFrom(
    wallet.state().pipe(
      Rx.tap((state: FacadeState) => {
        emissionCount++;
        const shielded = isProgressStrictlyComplete(state.shielded.state.progress);
        const unshielded = isProgressStrictlyComplete(state.unshielded.progress);
        const dust = isProgressStrictlyComplete(state.dust.state.progress);
        logger.info(
          `Wallet sync [${emissionCount}]: shielded=${shielded}, unshielded=${unshielded}, dust=${dust}`,
        );
      }),
      Rx.filter(
        (state: FacadeState) =>
          isProgressStrictlyComplete(state.shielded.state.progress) &&
          isProgressStrictlyComplete(state.dust.state.progress) &&
          isProgressStrictlyComplete(state.unshielded.progress),
      ),
      Rx.tap(() => logger.info(`Wallet sync complete after ${emissionCount} emissions`)),
      Rx.timeout({
        each: timeout,
        with: () =>
          Rx.throwError(
            () => new Error(`Wallet sync timeout after ${timeout}ms (${emissionCount} emissions received)`),
          ),
      }),
      Rx.catchError((err) => {
        logger.error(`Wallet sync error: ${err}`);
        return Rx.throwError(() => err);
      }),
    ),
  );
}

10) src/providers.ts

import { type MidnightProviders } from '@midnight-ntwrk/midnight-js-types';
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';
import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider';
import { type MidnightWalletProvider } from './wallet.js';
import { type NetworkConfig } from './config.js';

export type HelloWorldCircuits = 'storeMessage';

export type HelloWorldProviders = MidnightProviders<any>;

export function buildProviders(
  wallet: MidnightWalletProvider,
  zkConfigPath: string,
  config: NetworkConfig,
): HelloWorldProviders {
  const zkConfigProvider = new NodeZkConfigProvider<HelloWorldCircuits>(zkConfigPath);

  return {
    privateStateProvider: levelPrivateStateProvider({
      privateStateStoreName: `hello-world-${Date.now()}`,
      walletProvider: wallet,
      privateStoragePasswordProvider: () => 'xK9#mQ2$pL8@nR5!vW3*',
      accountId: `test-account-${Date.now()}`,
    }),
    publicDataProvider: indexerPublicDataProvider(
      config.indexer,
      config.indexerWS,
    ),
    zkConfigProvider,
    proofProvider: httpClientProofProvider(
      config.proofServer,
      zkConfigProvider,
    ),
    walletProvider: wallet,
    midnightProvider: wallet,
  };
}

11) src/test/hw.test.ts

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { WebSocket } from 'ws';
import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id';
import {
  deployContract,
  submitCallTx,
} from '@midnight-ntwrk/midnight-js-contracts';
import type { ContractAddress } from '@midnight-ntwrk/compact-runtime';
import pino from 'pino';

import { getConfig } from '../config.js';
import { MidnightWalletProvider, syncWallet } from '../wallet.js';
import { buildProviders, type HelloWorldProviders } from '../providers.js';
import {
  CompiledHelloWorldContract,
  ledger,
  zkConfigPath,
} from '../../contracts/index.js';
import type { EnvironmentConfiguration } from '@midnight-ntwrk/testkit-js';

// Required for GraphQL subscriptions in Node.js
// @ts-expect-error WebSocket global assignment for apollo
globalThis.WebSocket = WebSocket;

process.on('unhandledRejection', (reason, promise) => {
  console.error('UNHANDLED REJECTION:', reason);
  console.error('Promise:', promise);
});

process.on('uncaughtException', (err) => {
  console.error('UNCAUGHT EXCEPTION:', err);
});

const ALICE_SEED =
  '0000000000000000000000000000000000000000000000000000000000000001';
const ALICE_PRIVATE_STATE_ID = 'AlicePrivateHWState';

const logger = pino({
  level: process.env['LOG_LEVEL'] ?? 'info',
  transport: { target: 'pino-pretty' },
});

describe('Hello World Contract', () => {
  let aliceWallet: MidnightWalletProvider;
  let aliceProviders: HelloWorldProviders;
  let contractAddress: ContractAddress;

  const config = getConfig();

  async function queryLedger(providers: HelloWorldProviders) {
    const state =
      await providers.publicDataProvider.queryContractState(contractAddress);
    expect(state).not.toBeNull();
    return ledger(state!.data);
  }

  beforeAll(async () => {
    setNetworkId(config.networkId);

    const envConfig: EnvironmentConfiguration = {
      walletNetworkId: config.networkId,
      networkId: config.networkId,
      indexer: config.indexer,
      indexerWS: config.indexerWS,
      node: config.node,
      nodeWS: config.nodeWS,
      faucet: config.faucet,
      proofServer: config.proofServer,
    };

    aliceWallet = await MidnightWalletProvider.build(logger, envConfig, ALICE_SEED);
    await aliceWallet.start();
    await syncWallet(logger, aliceWallet.wallet, 600_000);

    aliceProviders = buildProviders(aliceWallet, zkConfigPath, config);
    logger.info('Providers initialized. Ready to test!');
  });

  afterAll(async () => {
    if (aliceWallet) {
      logger.info('Stopping Alice wallet...');
      await aliceWallet.stop();
    }
  });

  it('Deploys the contract', async () => {
    logger.info('Creating private state...');

    const deployed: any = await (deployContract as any)(aliceProviders, {
      compiledContract: CompiledHelloWorldContract,
      privateStateId: ALICE_PRIVATE_STATE_ID,
      initialPrivateState: {},
      args: [],
    });

    logger.info('Setting the contract address...');
    contractAddress = deployed.deployTxData.public.contractAddress;
    logger.info(`Contract deployed at: ${contractAddress}`);
    expect(contractAddress).toBeDefined();
    expect(contractAddress.length).toBeGreaterThan(0);

    const state = await queryLedger(aliceProviders);
    expect(state.message).toEqual('');
  });

  it('Stores Hello World!', async () => {
    const message = 'Hello World!';

    await (submitCallTx as any)(aliceProviders, {
      compiledContract: CompiledHelloWorldContract,
      contractAddress,
      privateStateId: ALICE_PRIVATE_STATE_ID,
      circuitId: 'storeMessage',
      args: [message],
    });

    const state = await queryLedger(aliceProviders);
    expect(state.message).toEqual(message);
  });
});

12) compose.yml

services:
  proof-server:
    image: 'midnightntwrk/proof-server:8.0.3'
    command: ['midnight-proof-server', '-v']
    ports:
      - '127.0.0.1:6300:6300'
    environment:
      RUST_BACKTRACE: 'full'
    healthcheck:
      test: ['CMD-SHELL', 'echo > /dev/tcp/127.0.0.1/6300']
      interval: 10s
      timeout: 5s
      retries: 20
      start_period: 10s

  indexer:
    image: 'midnightntwrk/indexer-standalone:4.0.0'
    ports:
      - '127.0.0.1:8088:8088'
    environment:
      RUST_LOG: 'indexer=info,chain_indexer=info,indexer_api=info,wallet_indexer=info,indexer_common=info,fastrace_opentelemetry=off,info'
      APP__INFRA__NODE__URL: 'ws://node:9944'
      APP__APPLICATION__NETWORK_ID: 'undeployed'
      APP__INFRA__STORAGE__PASSWORD: 'indexer'
      APP__INFRA__PUB_SUB__PASSWORD: 'indexer'
      APP__INFRA__LEDGER_STATE_STORAGE__PASSWORD: 'indexer'
      APP__INFRA__SECRET: '303132333435363738393031323334353637383930313233343536373839303132'
    healthcheck:
      test: ['CMD-SHELL', 'cat /var/run/indexer-standalone/running']
      interval: 10s
      timeout: 5s
      retries: 20
      start_period: 10s
    depends_on:
      node:
        condition: service_healthy

  node:
    image: 'midnightntwrk/midnight-node:0.22.3'
    ports:
      - '127.0.0.1:9944:9944'
    healthcheck:
      test: ['CMD', 'curl', '-f', 'http://localhost:9944/health']
      interval: 2s
      timeout: 5s
      retries: 20
      start_period: 5s
    environment:
      CFG_PRESET: 'dev'
      SIDECHAIN_BLOCK_BENEFICIARY: '04bcf7ad3be7a5c790460be82a713af570f22e0f801f6659ab8e84a52be6969e'

13) 首次运行 — 本地 Devnet

# 1. Install deps
yarn install

# 2. Compile contract (must run from contracts/ directory)
cd contracts
compact compile hello-world.compact managed/hello-world
cd ..

# 3. Start local stack
yarn env:up
# wait ~30 seconds for all services to become healthy

# 4. Run tests (new terminal)
yarn test:local

预期输出:

[12:46:12.694] INFO (22064): Wallet sync complete after 23 emissions
[12:46:12.703] INFO (22064): Providers initialized. Ready to test
[12:46:12.707] INFO (22064): Creating private state...
[12:46:32.347] INFO (22064): Setting the contract address...
[12:46:32.347] INFO (22064): Contract deployed at: bba6579743ae23b44301d4a9f8df30dbd5244d63a59d8fbc2c9fc7ea521a04f8
 ✓ src/test/hw.test.ts (2 tests) 39112ms
   ✓ Hello World Contract > Deploys the contract  19649ms
   ✓ Hello World Contract > Stores Hello World!   18184ms

14) 故障排查

错误原因修复方法
compact: command not found未设置 PATHsource $HOME/.local/bin/env
Cannot find module '../../contracts/...'尚未编译合约先在 contracts/ 目录中运行 compact compile
ETARGET No matching version found版本不在经过测试的集合中根据支持矩阵确定当前版本 — 参见 references/volatile.md。不要安装 @latest。
ECONNREFUSED 127.0.0.1:6300证明服务未运行yarn env:up
测试中出现 NODE_OPTIONS 错误缺少实验性标志确保在测试脚本中设置了 NODE_OPTIONS='--experimental-vm-modules'
Wallet sync timeoutDocker 服务未正常运行确认 yarn env:up 已完成 — 所有 3 个容器都必须处于正常状态
v4/graphql 返回 404索引器 URL 已过时本地 devnet 使用 /api/v4/graphql — 在 config.ts 中修正
FluentWalletBuilder is not a function导入的 testkit-js 不正确从 @midnight-ntwrk/testkit-js 导入
levelPrivateStateProvider 选项错误缺少必需字段必须传入 privateStateStoreName、walletProvider、privateStoragePasswordProvider(强密码,至少 16 个字符,且不能包含连续字符模式)以及 accountId
TS:合约的 Cannot find moduletsconfig.json 的 include 不正确必须使用 "contract/**/*.ts"(单数),而不是 "contracts/**/*.ts"
找不到 submitCallTx导入错误从 @midnight-ntwrk/midnight-js-contracts 导入
重新编译后旧合约地址失效验证密钥已更改重新部署 — 合约源码更改时,编译后的密钥也会改变

15) 改造此模板

  1. 将 contracts/hello-world.compact 替换为你的合约
  2. 编译:cd contracts && compact compile your-contract.compact managed/your-contract
  3. 更新 contracts/index.ts:
    • 将导入路径改为 ./managed/your-contract/contract/index.js
    • 将 zkConfigPath 更新为 managed/your-contract
    • 更新 CompiledContract.make() 名称和 Contract 引用
  4. 在 src/providers.ts 中:
    • 将 HelloWorldCircuits 类型更新为你的电路名称
    • 更新 privateStateStoreName 前缀,避免 LevelDB 冲突
  5. 在 src/test/hw.test.ts 中:
    • 将 submitCallTx 参数替换为你的电路调用
    • 将 ledger() 替换为合约的 ledger() 函数
    • 更新 ALICE_PRIVATE_STATE_ID 和 initialPrivateState 的结构
  6. 如果目标网络不是本地网络,则更新 src/config.ts

评论

支持 Markdown