mcpField
M
midnight-js

midnight-js

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

关于 midnight-js

介绍如何使用 Midnight.js TypeScript SDK 在 Midnight Network 上构建 DApp 的参考技能,涵盖钱包与 Provider 配置、合约部署和调用、网络设置及浏览器和 Node.js 集成测试。

主要功能

  1. 01Midnight.js 包与 Provider 指南
  2. 02网络及端点配置
  3. 03浏览器和无头钱包设置
  4. 04部署、调用和读取合约
  5. 05私有状态 Provider 选项
  6. 06Testkit 集成测试

适用场景

  1. 01搭建 Midnight.js DApp 项目
  2. 02配置浏览器或 Node.js 钱包
  3. 03部署并交互调用合约
  4. 04运行 Midnight 集成测试

安装

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

$npx skills add https://github.com/kali-decoder/midnight-skills --skill midnight-js

安装后在 Claude Code 中输入/midnight-js确认已加载。

详细介绍

Midnight.js 技能

Midnight.js 是用于在 Midnight Network 上构建 DApp 的 TypeScript SDK,类似于 Ethereum 的 Web3.js。官方 SDK 页面即将推出;本技能基于官方 Counter CLI 教程和 1AM 入门模板。

主要参考资料:

  • docs.midnight.network/tutorials/counter/counter-cli — 官方完整实现
  • github.com/midnightntwrk/example-counter — 参考仓库(Node.js / 无头钱包)
  • github.com/webisoftSoftware/1AM-starter-template — 浏览器钱包变体

1) 软件包一览

软件包用途
@midnight-ntwrk/midnight-js-contractsdeployContract、findDeployedContract、submitCallTx
@midnight-ntwrk/midnight-js-typesMidnightProviders、WalletProvider、MidnightProvider、createProofProvider
@midnight-ntwrk/midnight-js-indexer-public-data-providerindexerPublicDataProvider(GraphQL 索引器客户端)
@midnight-ntwrk/midnight-js-fetch-zk-config-providerFetchZkConfigProvider(浏览器 / CDN)
@midnight-ntwrk/midnight-js-node-zk-config-providerNodeZkConfigProvider(Node.js / 文件系统)
@midnight-ntwrk/midnight-js-http-client-proof-providerhttpClientProofProvider(可自托管的证明服务)
@midnight-ntwrk/midnight-js-level-private-state-providerlevelPrivateStateProvider(持久化 LevelDB 私有状态)
@midnight-ntwrk/midnight-js-network-idsetNetworkId、getNetworkId
@midnight-ntwrk/compact-runtimeContractState(索引器反序列化)
@midnight-ntwrk/ledger-v8Transaction、LedgerParameters、ZswapSecretKeys、DustSecretKey
@midnight-ntwrk/compact-jsCompiledContract、ImpureCircuitId
@midnight-ntwrk/wallet-sdk-facadeWalletFacade(统一钱包)
@midnight-ntwrk/wallet-sdk-hdHDWallet、generateRandomSeed、Roles
@midnight-ntwrk/wallet-sdk-shieldedShieldedWallet
@midnight-ntwrk/wallet-sdk-unshielded-walletUnshieldedWallet、createKeystore、PublicKey
@midnight-ntwrk/wallet-sdk-dust-walletDustWallet
@midnight-ntwrk/wallet-sdk-address-formatMidnightBech32m、ShieldedAddress 等

2) 网络配置

在执行任何 SDK 操作之前,务必调用 setNetworkId。 它会设置一个所有库都会自动使用的全局变量。

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

// Call once at startup, in the config constructor or before building providers
setNetworkId('preprod'); // 'preprod' | 'preview' | 'mainnet' | 'undeployed'

网络端点:

网络索引器 HTTP索引器 WSRPC
preprodhttps://indexer.preprod.midnight.network/api/v4/graphqlwss://indexer.preprod.midnight.network/api/v4/graphql/wshttps://rpc.preprod.midnight.network
previewhttps://indexer.preview.midnight.network/api/v4/graphqlwss://indexer.preview.midnight.network/api/v4/graphql/wswss://rpc.preview.midnight.network
mainnethttps://indexer.mainnet.midnight.network/api/v4/graphqlwss://indexer.mainnet.midnight.network/api/v4/graphql/wshttps://rpc.mainnet.midnight.network
undeployed(本地)http://localhost:8088/api/v4/graphqlws://localhost:8088/api/v4/graphql/wsws://localhost:9944

3) 编译合约设置

在模块加载时预先编译一次合约,而不是在每次部署 / 调用时编译。

import { CompiledContract } from '@midnight-ntwrk/compact-js';
import { Contract, witnesses } from './managed/counter'; // generated by compact compiler

// Node.js: load ZK assets from filesystem
import { NodeZkConfigProvider } from '@midnight-ntwrk/midnight-js-node-zk-config-provider';
const zkConfigPath = path.resolve('contract/src/managed/counter');

const compiledContract = CompiledContract.make('counter', Contract).pipe(
  CompiledContract.withVacantWitnesses,
  CompiledContract.withCompiledFileAssets(zkConfigPath),
);

// Browser / CDN: load ZK assets via fetch
import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider';
const compiledContract = CompiledContract.make('YourContract', Contract).pipe(
  CompiledContract.withVacantWitnesses,
  CompiledContract.withCompiledFileAssets('/zk/your-contract'),
);

4) 类型定义(重要模式)

为合约的提供程序定义类型别名——这是 Midnight.js 的惯用模式:

import type { MidnightProviders } from '@midnight-ntwrk/midnight-js-types';
import type { DeployedContract, FoundContract } from '@midnight-ntwrk/midnight-js-contracts';
import type { ImpureCircuitId } from '@midnight-ntwrk/compact-js';
import { Counter, type CounterPrivateState } from './managed/counter';

// Extract circuit IDs from the contract type
export type CounterCircuits = ImpureCircuitId<Counter.Contract<CounterPrivateState>>;

// Private state key (string literal type)
export const CounterPrivateStateId = 'counterPrivateState' as const;
export type CounterPrivateStateId = typeof CounterPrivateStateId;

// Full providers type for this contract
export type CounterProviders = MidnightProviders<
  CounterCircuits,
  CounterPrivateStateId,
  CounterPrivateState
>;

// Contract instance types
export type CounterContract = Counter.Contract<CounterPrivateState>;
export type DeployedCounterContract = DeployedContract<CounterContract> | FoundContract<CounterContract>;

5) 钱包设置(Node.js / 无头)

HD 密钥派生
import { HDWallet, generateRandomSeed, Roles } from '@midnight-ntwrk/wallet-sdk-hd';
import * as ledger from '@midnight-ntwrk/ledger-v8';
import { Buffer } from 'buffer';

// Generate a fresh random seed (returns Uint8Array)
const seed = generateRandomSeed();
const seedHex = Buffer.from(seed).toString('hex'); // save this

// Or restore from existing hex seed
const seedHex = '...'; // from user input

const hdWallet = HDWallet.fromSeed(Buffer.from(seedHex, 'hex'));
if (hdWallet.type !== 'seedOk') throw new Error('Invalid seed');

const derivationResult = hdWallet.hdWallet
  .selectAccount(0)
  .selectRoles([Roles.Zswap, Roles.NightExternal, Roles.Dust])
  .deriveKeysAt(0);

if (derivationResult.type !== 'keysDerived') throw new Error('Key derivation failed');

hdWallet.hdWallet.clear(); // wipe secret material from memory

const keys = derivationResult.keys;
const shieldedSecretKeys = ledger.ZswapSecretKeys.fromSeed(keys[Roles.Zswap]);
const dustSecretKey = ledger.DustSecretKey.fromSeed(keys[Roles.Dust]);
构建钱包
import { ShieldedWallet } from '@midnight-ntwrk/wallet-sdk-shielded';
import { UnshieldedWallet, createKeystore, PublicKey, InMemoryTransactionHistoryStorage } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
import { DustWallet } from '@midnight-ntwrk/wallet-sdk-dust-wallet';
import { WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade';

const unshieldedKeystore = createKeystore(keys[Roles.NightExternal], getNetworkId());

const shieldedWallet = ShieldedWallet({
  networkId: getNetworkId(),
  indexerClientConnection: { indexerHttpUrl: indexer, indexerWsUrl: indexerWS },
  provingServerUrl: new URL(proofServer),
  relayURL: new URL(node.replace(/^http/, 'ws')), // convert http → ws
}).startWithSecretKeys(shieldedSecretKeys);

const unshieldedWallet = UnshieldedWallet({
  networkId: getNetworkId(),
  indexerClientConnection: { indexerHttpUrl: indexer, indexerWsUrl: indexerWS },
  txHistoryStorage: new InMemoryTransactionHistoryStorage(),
}).startWithPublicKey(PublicKey.fromKeyStore(unshieldedKeystore));

const dustWallet = DustWallet({
  networkId: getNetworkId(),
  costParameters: {
    additionalFeeOverhead: 300_000_000_000_000n,
    feeBlocksMargin: 5,
  },
  indexerClientConnection: { indexerHttpUrl: indexer, indexerWsUrl: indexerWS },
  provingServerUrl: new URL(proofServer),
  relayURL: new URL(node.replace(/^http/, 'ws')),
}).startWithSecretKey(dustSecretKey, ledger.LedgerParameters.initialParameters().dust);

const wallet = new WalletFacade(shieldedWallet, unshieldedWallet, dustWallet);
await wallet.start(shieldedSecretKeys, dustSecretKey);
钱包同步(RxJS 模式——与 WalletFacade 搭配时始终使用)
import * as Rx from 'rxjs';

// Wait until wallet is fully synced
const syncedState = await Rx.firstValueFrom(
  wallet.state().pipe(
    Rx.throttleTime(5_000),
    Rx.filter((state) => state.isSynced),
  ),
);

// Wait until wallet has non-zero unshielded balance
import { unshieldedToken } from '@midnight-ntwrk/ledger-v8';

const balance = await Rx.firstValueFrom(
  wallet.state().pipe(
    Rx.throttleTime(10_000),
    Rx.filter((s) => s.isSynced),
    Rx.map((s) => s.unshielded.balances[unshieldedToken().raw] ?? 0n),
    Rx.filter((balance) => balance > 0n),
  ),
);

Node.js WebSocket 要求:

import { WebSocket } from 'ws';
// Required for GraphQL subscriptions (wallet sync) to work in Node.js
globalThis.WebSocket = WebSocket as unknown as typeof globalThis.WebSocket;
// Put this at the very top of your entry file, before any wallet imports

6) DUST 生成(仅限预发布 / 尚未部署)

NIGHT 代币会随时间生成 DUST,但只有在 UTXO 明确注册到链上后才会开始生成。DUST 是所有交易所需的不可转让费用资源。

// 1. Wait for sync
const state = await Rx.firstValueFrom(wallet.state().pipe(Rx.filter((s) => s.isSynced)));

// 2. Check if DUST already available
if (state.dust.availableCoins.length > 0) {
  console.log('DUST already available:', state.dust.walletBalance(new Date()));
  return;
}

// 3. Register unregistered NIGHT UTXOs
const nightUtxos = state.unshielded.availableCoins.filter(
  (coin: any) => coin.meta?.registeredForDustGeneration !== true,
);

if (nightUtxos.length > 0) {
  const recipe = await wallet.registerNightUtxosForDustGeneration(
    nightUtxos,
    unshieldedKeystore.getPublicKey(),
    (payload) => unshieldedKeystore.signData(payload),
  );
  const finalized = await wallet.finalizeRecipe(recipe);
  await wallet.submitTransaction(finalized);
}

// 4. Wait for DUST balance > 0 (may take a few minutes)
await Rx.firstValueFrom(
  wallet.state().pipe(
    Rx.throttleTime(5_000),
    Rx.filter((s) => s.isSynced),
    Rx.filter((s) => s.dust.walletBalance(new Date()) > 0n),
  ),
);

DUST 故障排查:

  • 部署失败后 DUST 余额降至 0 → 重启 DApp 以释放锁定的 DUST 代币
  • pendingCoins > 0 && availableCoins === 0 → DUST 被待处理 / 失败的交易锁定
  • NIGHT 已注册但 DUST = 0 → 仍在累积,请等待几分钟

7) 提供方配置

Node.js(可自托管的证明服务)
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';

const zkConfigProvider = new NodeZkConfigProvider<CounterCircuits>(zkConfigPath);

const providers: CounterProviders = {
  privateStateProvider: levelPrivateStateProvider<CounterPrivateStateId>({
    privateStateStoreName: 'counter-private-state',  // LevelDB store name
    walletProvider: walletAndMidnightProvider,
  }),
  publicDataProvider: indexerPublicDataProvider(indexerHttp, indexerWs),
  zkConfigProvider,
  proofProvider: httpClientProofProvider(proofServerUrl, zkConfigProvider),
  walletProvider: walletAndMidnightProvider,
  midnightProvider: walletAndMidnightProvider,
};
浏览器(1AM 钱包 / CDN ZK 资源)
import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider';
import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';
import { createProofProvider } from '@midnight-ntwrk/midnight-js-types';

const config = await connectedAPI.getConfiguration();
setNetworkId(config.networkId);

const zkConfigProvider = new FetchZkConfigProvider(
  new URL(zkAssetBasePath, window.location.origin).toString(),
  window.fetch.bind(window),
);

const provingProvider = await connectedAPI.getProvingProvider(zkConfigProvider);

const providers = {
  publicDataProvider: indexerPublicDataProvider(config.indexerUri, config.indexerWsUri),
  zkConfigProvider,
  proofProvider: createProofProvider(provingProvider),  // wraps 1AM proving provider
  walletProvider: { /* see 1AM skill */ },
  midnightProvider: { /* see 1AM skill */ },
  privateStateProvider: createPrivateStateProvider(), // in-memory for browser
};

8) WalletProvider 和 MidnightProvider(Node.js 无头模式)

这是 WalletFacade 与 midnight-js 合约 API 之间的桥接:

import type { WalletProvider, MidnightProvider } from '@midnight-ntwrk/midnight-js-types';
import * as ledger from '@midnight-ntwrk/ledger-v8';

const state = await Rx.firstValueFrom(wallet.state().pipe(Rx.filter((s) => s.isSynced)));

const walletAndMidnightProvider: WalletProvider & MidnightProvider = {
  getCoinPublicKey() {
    return state.shielded.coinPublicKey.toHexString();
  },
  getEncryptionPublicKey() {
    return state.shielded.encryptionPublicKey.toHexString();
  },
  async balanceTx(tx, ttl?) {
    const recipe = await wallet.balanceUnboundTransaction(
      tx,
      { shieldedSecretKeys, dustSecretKey },
      { ttl: ttl ?? new Date(Date.now() + 30 * 60 * 1000) },
    );

    // ⚠️ KNOWN BUG WORKAROUND: wallet SDK signRecipe hardcodes 'pre-proof' but
    // proven (UnboundTransaction) intents contain 'proof' data → "Failed to clone intent"
    // Sign intents manually with the correct proof marker (see signTransactionIntents below)
    signTransactionIntents(recipe.baseTransaction, signFn, 'proof');
    if (recipe.balancingTransaction) {
      signTransactionIntents(recipe.balancingTransaction, signFn, 'pre-proof');
    }

    return wallet.finalizeRecipe(recipe);
  },
  submitTx(tx) {
    return wallet.submitTransaction(tx) as any;
  },
};
“Failed to clone intent” 错误修复
/**
 * Workaround for wallet SDK bug where signRecipe hardcodes 'pre-proof',
 * causing failures when signing proven (UnboundTransaction) intents.
 * Call with proofMarker='proof' for baseTransaction, 'pre-proof' for balancingTransaction.
 */
const signTransactionIntents = (
  tx: { intents?: Map<number, any> },
  signFn: (payload: Uint8Array) => ledger.Signature,
  proofMarker: 'proof' | 'pre-proof',
): void => {
  if (!tx.intents || tx.intents.size === 0) return;

  for (const segment of tx.intents.keys()) {
    const intent = tx.intents.get(segment);
    if (!intent) continue;

    const cloned = ledger.Intent.deserialize<
      ledger.SignatureEnabled, ledger.Proofish, ledger.PreBinding
    >('signature', proofMarker, 'pre-binding', intent.serialize());

    const signature = signFn(cloned.signatureData(segment));

    if (cloned.fallibleUnshieldedOffer) {
      const sigs = cloned.fallibleUnshieldedOffer.inputs.map(
        (_: ledger.UtxoSpend, i: number) =>
          cloned.fallibleUnshieldedOffer!.signatures.at(i) ?? signature,
      );
      cloned.fallibleUnshieldedOffer = cloned.fallibleUnshieldedOffer.addSignatures(sigs);
    }

    if (cloned.guaranteedUnshieldedOffer) {
      const sigs = cloned.guaranteedUnshieldedOffer.inputs.map(
        (_: ledger.UtxoSpend, i: number) =>
          cloned.guaranteedUnshieldedOffer!.signatures.at(i) ?? signature,
      );
      cloned.guaranteedUnshieldedOffer = cloned.guaranteedUnshieldedOffer.addSignatures(sigs);
    }

    tx.intents.set(segment, cloned);
  }
};

9) 部署和调用合约

部署
import { deployContract } from '@midnight-ntwrk/midnight-js-contracts';

const deployed = await deployContract(providers, {
  compiledContract,
  privateStateId: CounterPrivateStateId,
  initialPrivateState: { privateCounter: 0 },
});

console.log('Contract address:', deployed.deployTxData.public.contractAddress);
调用电路
// Using callTx (recommended — automatically builds, proves, balances, submits)
const result = await deployed.callTx.increment();
console.log('txId:', result.public.txId);
console.log('blockHeight:', result.public.blockHeight);
加入现有合约
import { findDeployedContract } from '@midnight-ntwrk/midnight-js-contracts';

const contract = await findDeployedContract(providers, {
  contractAddress: '09dbe05f...', // hex string
  compiledContract,
  privateStateId: CounterPrivateStateId,
  initialPrivateState: { privateCounter: 0 },
});

// Now call circuits on it
await contract.callTx.increment();
读取账本状态
import { ContractState } from '@midnight-ntwrk/compact-runtime';
import { Counter } from './managed/counter';

const contractState = await providers.publicDataProvider.queryContractState(contractAddress);

if (contractState === null) {
  console.log('Contract not found');
} else {
  // Apply generated ledger() function to deserialize typed state
  const ledgerState = Counter.ledger(contractState.data);
  console.log('Counter value:', ledgerState.round);
}

10) 私有状态提供方(内存中 — 浏览器 / 最简配置)

import type {
  PrivateStateProvider, PrivateStateId, PrivateStateExport,
  SigningKeyExport,
} from '@midnight-ntwrk/midnight-js-types';

function createPrivateStateProvider() {
  let scope = '';
  const stateStore = new Map<string, unknown>();
  const signingKeyStore = new Map<string, unknown>();
  const key = (id: string) => `${scope}:${id}`;

  return {
    setContractAddress(address: string) { scope = address; },
    async set(id: string, state: unknown) { stateStore.set(key(id), state); },
    async get(id: string) { return stateStore.get(key(id)) ?? null; },
    async remove(id: string) { stateStore.delete(key(id)); },
    async clear() { stateStore.clear(); },
    async setSigningKey(addr: string, k: unknown) { signingKeyStore.set(addr, k); },
    async getSigningKey(addr: string) { return signingKeyStore.get(addr) ?? null; },
    async removeSigningKey(addr: string) { signingKeyStore.delete(addr); },
    async clearSigningKeys() { signingKeyStore.clear(); },
    async exportPrivateStates(): Promise<PrivateStateExport> { throw new Error('Not implemented'); },
    async importPrivateStates() { throw new Error('Not implemented'); },
    async exportSigningKeys(): Promise<SigningKeyExport> { throw new Error('Not implemented'); },
    async importSigningKeys() { throw new Error('Not implemented'); },
  };
}

对于持久化的 Node.js 私有状态,请改用 levelPrivateStateProvider。


11) Testkit(集成测试)

import { getTestEnvironment } from '@midnight-ntwrk/testkit-js';
import pino from 'pino';

const logger = pino({ level: 'info' });
let testEnvironment: Awaited<ReturnType<typeof getTestEnvironment>>;

beforeAll(async () => {
  testEnvironment = getTestEnvironment(logger);
  const environmentConfig = await testEnvironment.start();
  // environmentConfig contains indexer, node, proofServer URLs
});

afterAll(async () => {
  await testEnvironment.shutdown();
});

// Get wallet providers for testing
const walletProvider = await testEnvironment.getMidnightWalletProvider();
// or multiple wallets (max 4 on local):
const [wallet1, wallet2] = await testEnvironment.startMidnightWalletProviders(2);

通过环境变量选择环境:

# Local Docker (default)
MN_TEST_ENVIRONMENT=undeployed yarn test

# Against preprod
MN_TEST_ENVIRONMENT=devnet yarn test

# Custom endpoints
MN_TEST_ENVIRONMENT=env-var-remote \
MN_TEST_NETWORK_ID=undeployed \
MN_TEST_INDEXER=http://localhost:8088/api/ \
MN_TEST_INDEXER_WS=ws://localhost:8088/ws/ \
MN_TEST_NODE=http://localhost:9944 \
yarn test

12) 证明服务

在 Node.js / 无头模式下使用时,通过 Docker 在本地运行证明服务:

# Auto-started proof server (preprod, pulls image automatically)
cd counter-cli && npm run preprod-ps

# Or manually
docker compose -f proof-server.yml up
# Wait for: "starting service... listening on: 0.0.0.0:6300"

# Direct Docker run
docker run -p 6300:6300 midnightntwrk/proof-server:latest midnight-proof-server -v

证明服务 URL:http://127.0.0.1:6300

Mac ARM(Apple Silicon)修复方法: 如果证明服务卡住,请前往 Docker Desktop → Settings → General → Virtual Machine Options → 选择 Docker VMM → 重启 Docker。


13) 常见问题

未调用 setNetworkId → SDK 深处会出现难以理解的类型不匹配错误。请在启动时立即调用,且在任何 SDK 操作之前调用。

未在 Node.js 中全局设置 WebSocket → 钱包同步的 GraphQL 订阅会静默失败。请在入口文件顶部加入 globalThis.WebSocket = WebSocket。

找不到 ZK 资源 → 请再次确认 zkConfigPath 是绝对路径,指向已编译的托管文件夹,并且其中包含 keys/ 和 zkir/ 子目录。开发前请运行 sync:assets npm 脚本。

“Failed to clone intent” → 钱包 SDK 错误,请使用 signTransactionIntents 变通方案。此问题发生在已生成证明的 UnboundTransaction 被平衡时 — SDK 将 'pre-proof' 硬编码,但 intents 中包含 'proof' 数据。

部署失败后 DUST = 0 → 已知的钱包 SDK 问题。重启 DApp 以释放锁定的 DUST 代币。部署前请使用 DUST 监视器确认状态。

领取水龙头代币后钱包余额为 0 → 请先等待同步完成。如果仍为 0,请确认使用的是未屏蔽地址(mn_addr_preprod1...),而非屏蔽地址。

relayURL 必须是 WebSocket → 将 https:// 转换为 wss://,将 http:// 转换为 ws://。钱包 SDK 要求中继使用 WebSocket URL。

每次部署都调用 CompiledContract.make → 请在模块加载时调用一次,而不是在部署函数内部调用。每次调用都加载 ZK 密钥会很慢。

在浏览器中使用 levelPrivateStateProvider → LevelDB 仅适用于 Node.js。浏览器环境请使用内存中的 createPrivateStateProvider。

重新编译后合约地址过期 → Compact 合约变更时,验证密钥也会改变。旧地址将无法通过证明验证。合约有任何变更后都请重新部署。


14) 依赖版本(快照 — 非固定版本)

这些版本号是带有时间戳的快照,可能会变化。 安装前,请从支持矩阵确认当前经过测试的版本组合 — 参见 references/volatile.md。下方列出的 包名 才是有用的信息;切勿安装 @latest。

{
  "@midnight-ntwrk/compact-runtime": "0.15.0",
  "@midnight-ntwrk/ledger-v8": "8.0.3",
  "@midnight-ntwrk/midnight-js-contracts": "4.0.2",
  "@midnight-ntwrk/midnight-js-http-client-proof-provider": "4.0.2",
  "@midnight-ntwrk/midnight-js-indexer-public-data-provider": "4.0.2",
  "@midnight-ntwrk/midnight-js-level-private-state-provider": "4.0.2",
  "@midnight-ntwrk/midnight-js-network-id": "4.0.2",
  "@midnight-ntwrk/midnight-js-node-zk-config-provider": "4.0.2",
  "@midnight-ntwrk/midnight-js-types": "4.0.2",
  "@midnight-ntwrk/midnight-js-utils": "4.0.2",
  "@midnight-ntwrk/wallet-sdk-address-format": "3.1.0",
  "@midnight-ntwrk/wallet-sdk-dust-wallet": "3.0.0",
  "@midnight-ntwrk/wallet-sdk-facade": "3.0.0",
  "@midnight-ntwrk/wallet-sdk-hd": "3.0.1",
  "@midnight-ntwrk/wallet-sdk-shielded": "2.1.0",
  "@midnight-ntwrk/wallet-sdk-unshielded-wallet": "2.1.0",
  "@midnight-ntwrk/compact-js": "2.5.0"
}

Node.js 版本:22+(必需)

评论

支持 Markdown