关于 midnight-js
介绍如何使用 Midnight.js TypeScript SDK 在 Midnight Network 上构建 DApp 的参考技能,涵盖钱包与 Provider 配置、合约部署和调用、网络设置及浏览器和 Node.js 集成测试。
主要功能
- 01Midnight.js 包与 Provider 指南
- 02网络及端点配置
- 03浏览器和无头钱包设置
- 04部署、调用和读取合约
- 05私有状态 Provider 选项
- 06Testkit 集成测试
适用场景
- 01搭建 Midnight.js DApp 项目
- 02配置浏览器或 Node.js 钱包
- 03部署并交互调用合约
- 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-contracts | deployContract、findDeployedContract、submitCallTx |
@midnight-ntwrk/midnight-js-types | MidnightProviders、WalletProvider、MidnightProvider、createProofProvider |
@midnight-ntwrk/midnight-js-indexer-public-data-provider | indexerPublicDataProvider(GraphQL 索引器客户端) |
@midnight-ntwrk/midnight-js-fetch-zk-config-provider | FetchZkConfigProvider(浏览器 / CDN) |
@midnight-ntwrk/midnight-js-node-zk-config-provider | NodeZkConfigProvider(Node.js / 文件系统) |
@midnight-ntwrk/midnight-js-http-client-proof-provider | httpClientProofProvider(可自托管的证明服务) |
@midnight-ntwrk/midnight-js-level-private-state-provider | levelPrivateStateProvider(持久化 LevelDB 私有状态) |
@midnight-ntwrk/midnight-js-network-id | setNetworkId、getNetworkId |
@midnight-ntwrk/compact-runtime | ContractState(索引器反序列化) |
@midnight-ntwrk/ledger-v8 | Transaction、LedgerParameters、ZswapSecretKeys、DustSecretKey |
@midnight-ntwrk/compact-js | CompiledContract、ImpureCircuitId |
@midnight-ntwrk/wallet-sdk-facade | WalletFacade(统一钱包) |
@midnight-ntwrk/wallet-sdk-hd | HDWallet、generateRandomSeed、Roles |
@midnight-ntwrk/wallet-sdk-shielded | ShieldedWallet |
@midnight-ntwrk/wallet-sdk-unshielded-wallet | UnshieldedWallet、createKeystore、PublicKey |
@midnight-ntwrk/wallet-sdk-dust-wallet | DustWallet |
@midnight-ntwrk/wallet-sdk-address-format | MidnightBech32m、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 | 索引器 WS | RPC |
|---|---|---|---|
preprod | https://indexer.preprod.midnight.network/api/v4/graphql | wss://indexer.preprod.midnight.network/api/v4/graphql/ws | https://rpc.preprod.midnight.network |
preview | https://indexer.preview.midnight.network/api/v4/graphql | wss://indexer.preview.midnight.network/api/v4/graphql/ws | wss://rpc.preview.midnight.network |
mainnet | https://indexer.mainnet.midnight.network/api/v4/graphql | wss://indexer.mainnet.midnight.network/api/v4/graphql/ws | https://rpc.mainnet.midnight.network |
undeployed(本地) | http://localhost:8088/api/v4/graphql | ws://localhost:8088/api/v4/graphql/ws | ws://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+(必需)