@effectstream/midnight-contracts 0.200.5 → 0.300.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effectstream/midnight-contracts",
3
- "version": "0.200.5",
3
+ "version": "0.300.0",
4
4
  "license": "(MIT OR Apache-2.0)",
5
5
  "homepage": "https://effectstream.github.io/docs/",
6
6
  "repository": {
@@ -26,35 +26,34 @@
26
26
  "./types": "./src/types.ts"
27
27
  },
28
28
  "dependencies": {
29
- "@effectstream/utils": "0.200.5",
30
- "@midnight-ntwrk/compact-js": "2.5.5-rc.7",
31
- "@midnight-ntwrk/compact-runtime": "0.18.0-rc.1",
32
- "@midnight-ntwrk/midnight-js-contracts": "5.0.0-beta.6",
33
- "@midnight-ntwrk/midnight-js-http-client-proof-provider": "5.0.0-beta.6",
34
- "@midnight-ntwrk/midnight-js-indexer-public-data-provider": "5.0.0-beta.6",
35
- "@midnight-ntwrk/midnight-js-level-private-state-provider": "5.0.0-beta.6",
36
- "@midnight-ntwrk/midnight-js-network-id": "5.0.0-beta.6",
37
- "@midnight-ntwrk/midnight-js-node-zk-config-provider": "5.0.0-beta.6",
38
- "@midnight-ntwrk/midnight-js-protocol": "5.0.0-beta.6",
39
- "@midnight-ntwrk/midnight-js-types": "5.0.0-beta.6",
40
- "@midnight-ntwrk/midnight-js-utils": "5.0.0-beta.6",
29
+ "@effectstream/utils": "0.300.0",
30
+ "@midnight-ntwrk/compact-js": "3.0.0",
31
+ "@midnight-ntwrk/compact-runtime": "0.21.0",
32
+ "@midnight-ntwrk/midnight-js-contracts": "5.0.0",
33
+ "@midnight-ntwrk/midnight-js-http-client-proof-provider": "5.0.0",
34
+ "@midnight-ntwrk/midnight-js-indexer-public-data-provider": "5.0.0",
35
+ "@midnight-ntwrk/midnight-js-level-private-state-provider": "5.0.0",
36
+ "@midnight-ntwrk/midnight-js-node-zk-config-provider": "5.0.0",
37
+ "@midnight-ntwrk/midnight-js-protocol": "5.0.0",
38
+ "@midnight-ntwrk/midnight-js-types": "5.0.0",
39
+ "@midnight-ntwrk/midnight-js-utils": "5.0.0",
41
40
  "@midnight-ntwrk/platform-js": "3.0.0",
42
- "@midnightntwrk/ledger-v9": "1.0.0-rc.3",
43
- "@midnightntwrk/onchain-runtime-v4": "4.0.0-rc.3",
44
- "@midnightntwrk/wallet-sdk": "2.0.0-beta.2",
45
- "@midnightntwrk/wallet-sdk-abstractions": "3.0.0-beta.0",
46
- "@midnightntwrk/wallet-sdk-address-format": "4.0.0-beta.2",
47
- "@midnightntwrk/wallet-sdk-capabilities": "4.0.0-beta.2",
48
- "@midnightntwrk/wallet-sdk-dust-wallet": "5.0.0-beta.2",
49
- "@midnightntwrk/wallet-sdk-facade": "5.0.0-beta.2",
50
- "@midnightntwrk/wallet-sdk-hd": "3.1.0-beta.1",
51
- "@midnightntwrk/wallet-sdk-indexer-client": "1.3.0-beta.1",
52
- "@midnightntwrk/wallet-sdk-node-client": "2.0.0-beta.2",
53
- "@midnightntwrk/wallet-sdk-prover-client": "2.0.0-beta.2",
54
- "@midnightntwrk/wallet-sdk-runtime": "1.0.6-beta.0",
55
- "@midnightntwrk/wallet-sdk-shielded": "4.0.0-beta.2",
56
- "@midnightntwrk/wallet-sdk-unshielded-wallet": "4.0.0-beta.2",
57
- "@midnightntwrk/wallet-sdk-utilities": "1.2.0",
41
+ "@midnightntwrk/ledger-v9": "1.0.0",
42
+ "@midnightntwrk/onchain-runtime-v4": "4.0.0",
43
+ "@midnightntwrk/wallet-sdk": "2.0.0",
44
+ "@midnightntwrk/wallet-sdk-abstractions": "3.0.0",
45
+ "@midnightntwrk/wallet-sdk-address-format": "4.0.0",
46
+ "@midnightntwrk/wallet-sdk-capabilities": "4.0.0",
47
+ "@midnightntwrk/wallet-sdk-dust-wallet": "5.0.0",
48
+ "@midnightntwrk/wallet-sdk-facade": "5.0.0",
49
+ "@midnightntwrk/wallet-sdk-hd": "4.0.0",
50
+ "@midnightntwrk/wallet-sdk-indexer-client": "2.0.0",
51
+ "@midnightntwrk/wallet-sdk-node-client": "2.0.0",
52
+ "@midnightntwrk/wallet-sdk-prover-client": "2.0.0",
53
+ "@midnightntwrk/wallet-sdk-runtime": "2.0.0",
54
+ "@midnightntwrk/wallet-sdk-shielded": "4.0.0",
55
+ "@midnightntwrk/wallet-sdk-unshielded-wallet": "4.0.0",
56
+ "@midnightntwrk/wallet-sdk-utilities": "1.3.0",
58
57
  "@scure/bip39": "^2.0.1",
59
58
  "bn.js": "5.2.3",
60
59
  "dotenv": "^16.4.7",
@@ -20,16 +20,22 @@
20
20
 
21
21
  import { readFileSync, writeFileSync, rmSync, readdirSync } from "node:fs";
22
22
  import * as path from "node:path";
23
- import { getNetworkId } from "@midnight-ntwrk/midnight-js-network-id";
24
- import { ContractExecutable } from "@midnight-ntwrk/compact-js";
23
+ import {
24
+ CompiledContract,
25
+ ContractExecutable,
26
+ type Contract,
27
+ } from "@midnight-ntwrk/compact-js";
25
28
  import {
26
29
  asContractAddress,
27
30
  exitResultOrError,
28
- makeContractExecutableRuntime,
31
+ makeLedger9ContractExecutableRuntime,
29
32
  SucceedEntirely,
30
33
  type MidnightProviders,
31
34
  } from "@midnight-ntwrk/midnight-js-types";
32
- import { submitInsertVerifierKeyTx } from "@midnight-ntwrk/midnight-js-contracts";
35
+ import {
36
+ submitInsertVerifierKeyTx,
37
+ type Ledger9ContractProviders,
38
+ } from "@midnight-ntwrk/midnight-js-contracts";
33
39
  import {
34
40
  ContractDeploy,
35
41
  ContractState,
@@ -40,6 +46,7 @@ import {
40
46
 
41
47
  import { CONSTANTS } from "./constants.ts";
42
48
  import type { DeployConfig, WalletResult } from "./types.ts";
49
+ import { adoptInWallet } from "./wallet-transaction.ts";
43
50
 
44
51
  const log = console;
45
52
 
@@ -110,7 +117,7 @@ function saveState(stateFilePath: string, state: PhasedDeploymentState): void {
110
117
  export async function deployMidnightContractPhased(
111
118
  providers: MidnightProviders,
112
119
  // deno-lint-ignore no-explicit-any
113
- compiledContract: any,
120
+ compiledContract: CompiledContract.CompiledContract<Contract.Any, any>,
114
121
  config: DeployConfig,
115
122
  // deno-lint-ignore no-explicit-any
116
123
  deployArgs: any[] | undefined,
@@ -137,7 +144,7 @@ export async function deployMidnightContractPhased(
137
144
  const coinPublicKey = providers.walletProvider.getCoinPublicKey().toString();
138
145
 
139
146
  const contractExec = ContractExecutable.make(compiledContract);
140
- const contractRuntime = makeContractExecutableRuntime(
147
+ const contractRuntime = makeLedger9ContractExecutableRuntime(
141
148
  providers.zkConfigProvider,
142
149
  { coinPublicKey, signingKey },
143
150
  );
@@ -170,7 +177,7 @@ export async function deployMidnightContractPhased(
170
177
  contractAddress = contractDeploy.address;
171
178
 
172
179
  const unprovenTx = Transaction.fromParts(
173
- getNetworkId(),
180
+ providers.config.networkId,
174
181
  undefined,
175
182
  undefined,
176
183
  Intent.new(createTtl()).addDeploy(contractDeploy),
@@ -178,11 +185,7 @@ export async function deployMidnightContractPhased(
178
185
  log.info(`Deploying empty contract at address: ${contractAddress}`);
179
186
 
180
187
  const recipe = await walletResult.wallet.balanceUnprovenTransaction(
181
- unprovenTx as any,
182
- {
183
- shieldedSecretKeys: walletResult.walletZswapSecretKeys as any,
184
- dustSecretKey: walletResult.walletDustSecretKey as any,
185
- },
188
+ await adoptInWallet(walletResult.wallet, "Unproven", unprovenTx),
186
189
  { ttl: createTtl() },
187
190
  );
188
191
  const signedRecipe = await walletResult.wallet.signRecipe(
@@ -205,12 +208,10 @@ export async function deployMidnightContractPhased(
205
208
  state.contractAddress = contractAddress;
206
209
  saveState(stateFilePath, state);
207
210
 
208
- (providers.privateStateProvider as any).setContractAddress?.(contractAddress);
209
211
  if (config.privateStateId) {
210
- await providers.privateStateProvider.set(
211
- config.privateStateId as any,
212
- privateState,
213
- );
212
+ await providers.privateStateProvider
213
+ .forContract(contractAddress)
214
+ .set(config.privateStateId, privateState);
214
215
  }
215
216
  await providers.privateStateProvider.setSigningKey(
216
217
  contractAddress,
@@ -239,15 +240,15 @@ export async function deployMidnightContractPhased(
239
240
  for (let attempt = 1; ; attempt++) {
240
241
  try {
241
242
  const result = await submitInsertVerifierKeyTx(
242
- providers,
243
+ providers as Ledger9ContractProviders,
243
244
  compiledContract,
244
245
  asContractAddress(contractAddress),
245
- circuitId as any,
246
+ circuitId,
246
247
  verifierKey,
247
248
  );
248
- if (result.status !== SucceedEntirely) {
249
+ if (result.public.status !== SucceedEntirely) {
249
250
  throw new Error(
250
- `Insert verifier key for ${circuitId} returned status ${result.status}`,
251
+ `Insert verifier key for ${circuitId} returned status ${result.public.status}`,
251
252
  );
252
253
  }
253
254
  break;
package/src/deploy.ts CHANGED
@@ -3,15 +3,17 @@
3
3
  // Single WASM instance for ContractState / ContractMaintenanceAuthority.
4
4
  import "@midnightntwrk/onchain-runtime-v4";
5
5
 
6
- import { setNetworkId } from "@midnight-ntwrk/midnight-js-network-id";
7
6
  import { Buffer } from "node:buffer";
8
7
  import { statSync, readdirSync } from "node:fs";
9
8
  import { writeFile } from "node:fs/promises";
10
9
  import { getEnv, cwd } from "@effectstream/utils/runtime";
11
- import { deployContract } from "@midnight-ntwrk/midnight-js-contracts";
10
+ import {
11
+ deployContract,
12
+ type Ledger9ContractProviders,
13
+ type Ledger9DeployContractOptionsWithPrivateState,
14
+ } from "@midnight-ntwrk/midnight-js-contracts";
12
15
  import type { PrivateStateId } from "@midnight-ntwrk/midnight-js-types";
13
- import { CompiledContract, type Witnesses, type Contract } from "@midnight-ntwrk/compact-js";
14
- import type { SigningKey } from "@midnightntwrk/ledger-v9";
16
+ import { CompiledContract, type Contract } from "@midnight-ntwrk/compact-js";
15
17
  import type { NetworkId } from "@midnightntwrk/wallet-sdk-abstractions";
16
18
  import * as path from "node:path";
17
19
 
@@ -20,6 +22,7 @@ const log = console;
20
22
  import { findContractDirectoryForDeploy } from "./read-contract.ts";
21
23
  import { mnemonicToSeed } from "./mnemonicToSeed.ts";
22
24
  import type { NetworkUrls, DeployConfig, WalletResult } from "./types.ts";
25
+ export type { DeployConfig } from "./types.ts";
23
26
  import { buildWalletAndWaitForFunds, extractInitialOwnerFromWallet } from "./build-wallet.ts";
24
27
  import { configureMidnightNodeProviders } from "./providers.ts";
25
28
  import { deployMidnightContractPhased } from "./deploy-phased.ts";
@@ -140,8 +143,6 @@ export async function deployMidnightContract(
140
143
  `Preflight resolved endpoints -> indexerHttp=${resolvedNetworkUrls.indexer}, indexerWs=${resolvedNetworkUrls.indexerWS}, node=${resolvedNetworkUrls.node}, proofServer=${resolvedNetworkUrls.proofServer}, networkId=${resolvedNetworkId}`,
141
144
  );
142
145
 
143
- setNetworkId(resolvedNetworkId);
144
-
145
146
  let walletResult: WalletResult | null = opts?.walletResult ?? null;
146
147
  let providers: Awaited<ReturnType<typeof configureMidnightNodeProviders>> | null = null;
147
148
 
@@ -211,6 +212,7 @@ export async function deployMidnightContract(
211
212
  deployPrivateStateStoreName,
212
213
  zkConfigPath,
213
214
  unshieldedKeystore,
215
+ resolvedNetworkId,
214
216
  );
215
217
  log.info("Providers configured.");
216
218
 
@@ -228,7 +230,7 @@ export async function deployMidnightContract(
228
230
  };
229
231
 
230
232
  // First, create the compiled contract
231
- const MyCompiledContract = CompiledContract.make(
233
+ const MyCompiledContract = CompiledContract.make<Contract.Any>(
232
234
  resolvedConfig.contractName,
233
235
  resolvedConfig.contractClass,
234
236
  ).pipe(
@@ -252,28 +254,23 @@ export async function deployMidnightContract(
252
254
  );
253
255
  log.info("Contract deployed (phased).");
254
256
  } else {
255
- const deployOptions: {
256
- compiledContract: CompiledContract.CompiledContract<
257
- Contract<undefined, Witnesses<undefined>>,
258
- undefined,
259
- never
260
- >;
261
- privateStateId: PrivateStateId;
262
- initialPrivateState: Contract.PrivateState<any>;
263
- signingKey?: SigningKey;
264
- args: Contract.InitializeParameters<any>;
265
- } = {
266
- compiledContract: MyCompiledContract as any,
257
+ // The contract class is only known at run time, so the options are
258
+ // typed against `Contract.Any`. `privateStateId` and
259
+ // `initialPrivateState` are always given together (both defaulted
260
+ // above), which is the pairing the with-private-state overload requires.
261
+ const deployOptions = {
262
+ compiledContract: MyCompiledContract,
267
263
  privateStateId: resolvedConfig.privateStateId as PrivateStateId,
268
- initialPrivateState:
269
- resolvedConfig.initialPrivateState as Contract.PrivateState<any>,
264
+ initialPrivateState: resolvedConfig.initialPrivateState,
270
265
  args: (deployArgs && deployArgs.length > 0
271
266
  ? deployArgs
272
- : []) as Contract.InitializeParameters<any>,
273
- signingKey: undefined,
274
- };
267
+ : []) as Contract.InitializeParameters<Contract.Any>,
268
+ } satisfies Ledger9DeployContractOptionsWithPrivateState<Contract.Any>;
275
269
 
276
- const deployedContract = await deployContract(providers, deployOptions);
270
+ const deployedContract = await deployContract(
271
+ providers as Ledger9ContractProviders<Contract.Any>,
272
+ deployOptions,
273
+ );
277
274
  log.info("Contract deployed.");
278
275
  contractAddress = deployedContract.deployTxData.public.contractAddress;
279
276
  }
@@ -11,7 +11,6 @@
11
11
  import dotenv from "dotenv";
12
12
  import { parseArgs } from "node:util";
13
13
  import { nativeToken } from "@midnightntwrk/ledger-v9";
14
- import { setNetworkId } from "@midnight-ntwrk/midnight-js-network-id";
15
14
  import type { NetworkId } from "@midnightntwrk/wallet-sdk-abstractions";
16
15
  import * as Rx from "rxjs";
17
16
  import type { NetworkUrls } from "./types.ts";
@@ -96,7 +95,6 @@ async function main() {
96
95
  log.info(`Indexer: ${indexer}`);
97
96
  log.info(`Indexer WS: ${indexerWS}`);
98
97
  log.info(`Node: ${node}`);
99
- setNetworkId(networkId);
100
98
 
101
99
  try {
102
100
  log.info("Building wallet...");
@@ -19,7 +19,6 @@ import {
19
19
  } from "@midnightntwrk/ledger-v9";
20
20
 
21
21
  const log = console;
22
- import { setNetworkId } from "@midnight-ntwrk/midnight-js-network-id";
23
22
  import { Buffer } from "node:buffer";
24
23
  import * as Rx from "rxjs";
25
24
  import { HDWallet, Roles } from "@midnightntwrk/wallet-sdk-hd";
@@ -988,7 +987,6 @@ export async function buildWalletFacade(
988
987
  dustSerializedState?: string | null,
989
988
  options?: { stopAuxWalletsImmediately?: boolean },
990
989
  ): Promise<WalletResult> {
991
- setNetworkId(networkId);
992
990
  const shieldedSeed = deriveSeedForRole(seed, Roles.Zswap);
993
991
  const dustSeed = deriveSeedForRole(seed, Roles.Dust);
994
992
  const unshieldedSeed = deriveSeedForRole(seed, Roles.NightExternal);
@@ -1013,7 +1011,13 @@ export async function buildWalletFacade(
1013
1011
  buildDustWallet(walletConfig, dustSeed, dustSerializedState),
1014
1012
  });
1015
1013
 
1016
- await wallet.start(zswapSecretKeys, dustSecretKey);
1014
+ // wallet-sdk 2 starts every wallet from seeds: each ledger version derives
1015
+ // its own keys from them, so the same identity holds across the v9 boundary.
1016
+ await wallet.start({
1017
+ shielded: shieldedSeed,
1018
+ unshielded: unshieldedSeed,
1019
+ dust: dustSeed,
1020
+ });
1017
1021
 
1018
1022
  const stopAuxNow =
1019
1023
  syncMode === 'dust-only' && (options?.stopAuxWalletsImmediately ?? true);
@@ -23,7 +23,6 @@ export type NetworkConfig = {
23
23
  indexerWS: string;
24
24
  node: string;
25
25
  proofServer: string;
26
- faucetUrl?: string;
27
26
  networkId: NetworkId.NetworkId;
28
27
  genesisWalletSeed: string;
29
28
  }
@@ -48,26 +47,15 @@ const deployedNetworkConfig = (networkId: NetworkId.NetworkId): NetworkConfig =>
48
47
  genesisWalletSeed: '',
49
48
  });
50
49
 
51
- const stagenetNetworkConfig = Object.freeze({
52
- indexer: "https://indexer.stagenet.shielded.tools/api/v4/graphql",
53
- indexerWS: "wss://indexer.stagenet.shielded.tools/api/v4/graphql/ws",
54
- node: "wss://rpc.stagenet.shielded.tools",
55
- proofServer: "http://127.0.0.1:6300",
56
- faucetUrl: "https://faucet.stagenet.shielded.tools/api/drips",
57
- networkId: "stagenet" as NetworkId.NetworkId,
58
- genesisWalletSeed: '',
59
- }) satisfies NetworkConfig;
60
-
61
50
  /**
62
51
  * Resolve defaults for every wallet-SDK network ID. `undeployed` uses loopback
63
- * services, Stagenet uses its explicit Shielded Tools profile, and every other
64
- * deployed or future ID follows the hosted Midnight endpoint convention.
52
+ * services; every deployed or future ID (mainnet, preview, preprod, ...)
53
+ * follows the hosted Midnight endpoint convention.
65
54
  */
66
55
  export const defaultMidnightNetworkConfig = (
67
56
  networkId: NetworkId.NetworkId,
68
57
  ): NetworkConfig => {
69
58
  if (networkId === "undeployed") return undeployedNetworkConfig;
70
- if (networkId === "stagenet") return stagenetNetworkConfig;
71
59
  return deployedNetworkConfig(networkId);
72
60
  };
73
61
 
@@ -90,7 +78,6 @@ export type MidnightNetworkConfig = {
90
78
  indexerWS: string;
91
79
  node: string;
92
80
  proofServer: string;
93
- faucetUrl?: string;
94
81
  walletSeed: string;
95
82
  };
96
83
 
@@ -100,7 +87,6 @@ export const midnightNetworkConfig: MidnightNetworkConfig = {
100
87
  indexerWS: env("MIDNIGHT_INDEXER_WS", selectedNetworkConfig.indexerWS),
101
88
  node: env("MIDNIGHT_NODE_HTTP", selectedNetworkConfig.node),
102
89
  proofServer: env(["MIDNIGHT_PROOF_SERVER_URL", "MIDNIGHT_PROOF_SERVER"], selectedNetworkConfig.proofServer),
103
- faucetUrl: selectedNetworkConfig.faucetUrl,
104
90
  walletSeed,
105
91
  };
106
92
 
package/src/mod.ts CHANGED
@@ -39,4 +39,11 @@ export {
39
39
  } from "./build-wallet.ts";
40
40
  export { readMidnightContract } from "./read-contract.ts";
41
41
  export { configureMidnightNodeProviders } from "./providers.ts";
42
+ export {
43
+ activeProtocolVersion,
44
+ adoptInWallet,
45
+ finalizedFromWallet,
46
+ unwrapInWallet,
47
+ } from "./wallet-transaction.ts";
48
+ export type { WalletStage } from "./wallet-transaction.ts";
42
49
  export { midnightNetworkConfig } from "./midnight-env.ts";
package/src/providers.ts CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { Buffer } from "node:buffer";
6
6
  import * as path from "node:path";
7
- import type { ZswapSecretKeys, DustSecretKey, CoinPublicKey, EncPublicKey, FinalizedTransaction, TransactionId } from "@midnightntwrk/ledger-v9";
7
+ import type { ZswapSecretKeys, DustSecretKey } from "@midnightntwrk/ledger-v9";
8
8
  import { httpClientProofProvider } from "@midnight-ntwrk/midnight-js-http-client-proof-provider";
9
9
  import { indexerPublicDataProvider } from "@midnight-ntwrk/midnight-js-indexer-public-data-provider";
10
10
  import { levelPrivateStateProvider } from "@midnight-ntwrk/midnight-js-level-private-state-provider";
@@ -12,120 +12,124 @@ import {
12
12
  NodeZkConfigProvider,
13
13
  nodeZkConfigRegistry,
14
14
  } from "@midnight-ntwrk/midnight-js-node-zk-config-provider";
15
- import type { WalletProvider, MidnightProvider, MidnightProviders, UnboundTransaction } from "@midnight-ntwrk/midnight-js-types";
15
+ import {
16
+ createMidnightProviderFromHandlers,
17
+ createWalletProviderFromHandlers,
18
+ type MidnightProvider,
19
+ type MidnightProviders,
20
+ type NetworkId,
21
+ type WalletProvider,
22
+ } from "@midnight-ntwrk/midnight-js-types";
16
23
  import type { WalletFacade } from "@midnightntwrk/wallet-sdk-facade";
17
24
  import type { NetworkUrls } from "./types.ts";
18
25
  import type { UnshieldedKeystore } from "@midnightntwrk/wallet-sdk-unshielded-wallet";
19
26
  import { CONSTANTS } from "./constants.ts";
20
- // import { Contract } from "@midnight-ntwrk/compact-js";
21
27
  import { getEnv } from "@effectstream/utils/runtime";
28
+ import { adoptInWallet, finalizedFromWallet } from "./wallet-transaction.ts";
22
29
 
23
30
  /**
24
31
  * Create a TTL date for transactions
25
32
  */
26
33
  function createTtl(): Date {
27
- return new Date(Date.now() + CONSTANTS.TTL_DURATION_MS);
28
- }
34
+ return new Date(Date.now() + CONSTANTS.TTL_DURATION_MS);
35
+ }
36
+
37
+ /**
38
+ * Wallet and midnight providers backed by a wallet-sdk 2 `WalletFacade`.
39
+ *
40
+ * Ledger-v9 only: the v8 entries are `undefined`, so a v8 handle is refused by
41
+ * the providers with `ProviderEraUnsupportedError`. The facade balances with
42
+ * the key material it was started from, so no secret keys are passed here.
43
+ */
44
+ function createWalletAndMidnightProviders(
45
+ wallet: WalletFacade,
46
+ zswapSecretKeys: ZswapSecretKeys,
47
+ unshieldedKeystore: UnshieldedKeystore,
48
+ ): { walletProvider: WalletProvider; midnightProvider: MidnightProvider } {
49
+ const walletProvider = createWalletProviderFromHandlers({
50
+ v8: undefined,
51
+ v9: async (tx, ttl) => {
52
+ const recipe = await wallet.balanceUnboundTransaction(
53
+ await adoptInWallet(wallet, "Unbound", tx),
54
+ { ttl: ttl ?? createTtl() },
55
+ );
56
+ const signedRecipe = await wallet.signRecipe(
57
+ recipe,
58
+ (payload) => unshieldedKeystore.signDataAsync(payload),
59
+ );
60
+ return finalizedFromWallet(wallet, await wallet.finalizeRecipe(signedRecipe));
61
+ },
62
+ getCoinPublicKey: () => zswapSecretKeys.coinPublicKey,
63
+ getEncryptionPublicKey: () => zswapSecretKeys.encryptionPublicKey,
64
+ });
65
+ const midnightProvider = createMidnightProviderFromHandlers({
66
+ v8: undefined,
67
+ v9: async (tx) =>
68
+ wallet.submitTransaction(await adoptInWallet(wallet, "Finalized", tx)),
69
+ });
70
+ return { walletProvider, midnightProvider };
71
+ }
29
72
 
30
73
  /**
31
- * Create wallet and midnight provider adapter for WalletFacade
74
+ * Configure all providers needed for contract deployment and circuit calls.
32
75
  *
33
- * Implements the WalletProvider and MidnightProvider interfaces
34
- * as defined in @midnight-ntwrk/midnight-js-types v5.
76
+ * `walletZswapSecretKeys`, `dustSecretKey` and `walletDustSecretKey` are not
77
+ * read: wallet-sdk 2 balances with the keys the facade was started from.
78
+ * `networkId` becomes `providers.config.networkId`, the network every
79
+ * transaction built with these providers targets.
35
80
  */
36
- function createWalletAndMidnightProvider(
37
- wallet: WalletFacade,
38
- zswapSecretKeys: ZswapSecretKeys,
39
- walletZswapSecretKeys: ZswapSecretKeys,
40
- dustSecretKey: DustSecretKey,
41
- walletDustSecretKey: DustSecretKey,
42
- unshieldedKeystore: UnshieldedKeystore
43
- ): WalletProvider & MidnightProvider {
44
- return {
45
- getCoinPublicKey(): CoinPublicKey {
46
- console.log("✅ Getting coin public key", zswapSecretKeys.coinPublicKey);
47
- return zswapSecretKeys.coinPublicKey;
48
- },
49
- getEncryptionPublicKey(): EncPublicKey {
50
- console.log("✅ Getting encryption public key", zswapSecretKeys.encryptionPublicKey);
51
- return zswapSecretKeys.encryptionPublicKey;
52
- },
53
- async balanceTx(
54
- tx: UnboundTransaction,
55
- ttl?: Date
56
- ): Promise<FinalizedTransaction> {
57
- console.log("✅ Balancing transaction", tx);
58
- const unboundTransactionRecipe = await wallet.balanceUnboundTransaction(tx, {
59
- shieldedSecretKeys: zswapSecretKeys,
60
- dustSecretKey: dustSecretKey,
61
- }, { ttl: ttl ?? createTtl() } );
62
- const signedRecipe = await wallet.signRecipe(
63
- unboundTransactionRecipe,
64
- (payload) => unshieldedKeystore.signDataAsync(payload),
65
- );
66
- return wallet.finalizeRecipe(signedRecipe);
67
- },
68
- submitTx(tx: FinalizedTransaction): Promise<TransactionId> {
69
- console.log("✅ Submitting transaction", tx);
70
- return wallet.submitTransaction(tx);
71
- },
72
- };
73
- }
74
-
75
- /**
76
- * Configure all providers needed for contract deployment
77
- */
78
- export async function configureMidnightNodeProviders(
79
- wallet: WalletFacade,
80
- zswapSecretKeys: ZswapSecretKeys,
81
- walletZswapSecretKeys: ZswapSecretKeys,
82
- dustSecretKey: DustSecretKey,
83
- walletDustSecretKey: DustSecretKey,
84
- networkUrls: Required<Omit<NetworkUrls, "id">>,
85
- privateStateStoreName: string,
86
- zkConfigPath: string,
87
- unshieldedKeystore: UnshieldedKeystore
88
- ): Promise<MidnightProviders> {
89
- const signingKeyStoreName = `${privateStateStoreName}-signing-keys`;
90
- const walletAndMidnightProvider = createWalletAndMidnightProvider(
91
- wallet,
92
- zswapSecretKeys,
93
- walletZswapSecretKeys,
94
- dustSecretKey,
95
- walletDustSecretKey,
96
- unshieldedKeystore
97
- );
98
- const zkConfigProvider = new NodeZkConfigProvider(zkConfigPath, {
99
- verify: "require",
100
- });
101
- // Midnight.js 5 resolves proof artifacts through a registry so transactions
102
- // with cross-contract calls can select bundles by verifier key. Search from
103
- // the managed-artifact parent to include sibling compiled contracts.
104
- const zkConfigRegistry = await nodeZkConfigRegistry(path.dirname(zkConfigPath));
105
- return {
106
- // For deployment, we use full private state config because we may need to verify
107
- // the deployed contract state. For batcher/transaction submission use cases,
108
- // a minimal config with just walletProvider is sufficient and much faster:
109
- // levelPrivateStateProvider({ walletProvider })
110
- // Omitting privateStateStoreName/midnightDbName avoids historical private state sync.
111
- privateStateProvider: levelPrivateStateProvider({
112
- midnightDbName: "midnight-level-db-deploy", // Use separate DB for deployment to avoid lock conflicts
113
- privateStateStoreName,
114
- signingKeyStoreName,
115
- // walletProvider: walletAndMidnightProvider, // Use wallet's encryption key for private state
116
- privateStoragePasswordProvider: async () => getEnv("MIDNIGHT_STORAGE_PASSWORD") ?? "YourPasswordMy1!",
117
- accountId: Buffer.from(zswapSecretKeys.coinPublicKey).toString('hex'),
118
- }), // Type assertion: runtime supports walletProvider even though types don't reflect it yet
119
- publicDataProvider: indexerPublicDataProvider(
120
- networkUrls.indexer,
121
- networkUrls.indexerWS
122
- ),
123
- zkConfigProvider,
124
- proofProvider: httpClientProofProvider(
125
- networkUrls.proofServer,
126
- zkConfigRegistry,
127
- ),
128
- walletProvider: walletAndMidnightProvider,
129
- midnightProvider: walletAndMidnightProvider,
130
- };
131
- }
81
+ export async function configureMidnightNodeProviders(
82
+ wallet: WalletFacade,
83
+ zswapSecretKeys: ZswapSecretKeys,
84
+ _walletZswapSecretKeys: ZswapSecretKeys,
85
+ _dustSecretKey: DustSecretKey,
86
+ _walletDustSecretKey: DustSecretKey,
87
+ networkUrls: Required<Omit<NetworkUrls, "id">>,
88
+ privateStateStoreName: string,
89
+ zkConfigPath: string,
90
+ unshieldedKeystore: UnshieldedKeystore,
91
+ networkId: NetworkId,
92
+ ): Promise<MidnightProviders> {
93
+ const signingKeyStoreName = `${privateStateStoreName}-signing-keys`;
94
+ const { walletProvider, midnightProvider } = createWalletAndMidnightProviders(
95
+ wallet,
96
+ zswapSecretKeys,
97
+ unshieldedKeystore,
98
+ );
99
+ const zkConfigProvider = new NodeZkConfigProvider(zkConfigPath, {
100
+ verify: "require",
101
+ });
102
+ // Midnight.js 5 resolves proof artifacts through a registry so transactions
103
+ // with cross-contract calls can select bundles by verifier key. Search from
104
+ // the managed-artifact parent to include sibling compiled contracts.
105
+ const zkConfigRegistry = await nodeZkConfigRegistry(path.dirname(zkConfigPath));
106
+ return {
107
+ // For deployment, we use full private state config because we may need to verify
108
+ // the deployed contract state. For batcher/transaction submission use cases,
109
+ // a minimal config with just walletProvider is sufficient and much faster:
110
+ // levelPrivateStateProvider({ walletProvider })
111
+ // Omitting privateStateStoreName/midnightDbName avoids historical private state sync.
112
+ privateStateProvider: levelPrivateStateProvider({
113
+ midnightDbName: "midnight-level-db-deploy", // Use separate DB for deployment to avoid lock conflicts
114
+ privateStateStoreName,
115
+ signingKeyStoreName,
116
+ privateStoragePasswordProvider: async () => getEnv("MIDNIGHT_STORAGE_PASSWORD") ?? "YourPasswordMy1!",
117
+ accountId: Buffer.from(zswapSecretKeys.coinPublicKey).toString("hex"),
118
+ }),
119
+ publicDataProvider: indexerPublicDataProvider(
120
+ networkUrls.indexer,
121
+ networkUrls.indexerWS,
122
+ ),
123
+ zkConfigProvider,
124
+ proofProvider: httpClientProofProvider(
125
+ networkUrls.proofServer,
126
+ zkConfigRegistry,
127
+ ),
128
+ walletProvider,
129
+ midnightProvider,
130
+ config: {
131
+ networkId,
132
+ ttlSeconds: CONSTANTS.TTL_DURATION_MS / 1000,
133
+ },
134
+ };
135
+ }
@@ -0,0 +1,107 @@
1
+ // ============================================================================
2
+ // Wallet transaction handles (ledger-v9)
3
+ // ============================================================================
4
+ //
5
+ // midnight-js 5 hands providers era-tagged transaction handles, and wallet-sdk
6
+ // 2 takes and returns sealed `WalletTransaction` handles. This module is the
7
+ // bridge between the two, following the midnight-js 5.0.0 testkit
8
+ // (`testkit-js/src/wallet/wallet-transaction.ts`), ledger-v9 only.
9
+
10
+ import {
11
+ DefaultForkSchedule,
12
+ ProtocolVersion,
13
+ WalletTransaction,
14
+ type WalletFacade,
15
+ } from "@midnightntwrk/wallet-sdk";
16
+ // The wallet SDK's own copy of ledger-v9: whatever is handed to the SDK must be
17
+ // decoded with the copy the SDK runs, or its classes refuse each other.
18
+ import * as WalletLedger9 from "@midnightntwrk/wallet-sdk/ledger/v9";
19
+ import * as Rx from "rxjs";
20
+
21
+ type VersionedWallet = Pick<WalletFacade, "state">;
22
+
23
+ /** The stages a transaction is handed to the wallet at. */
24
+ export type WalletStage = "Unproven" | "Unbound" | "Finalized";
25
+
26
+ type Serializable = { readonly serialize: () => Uint8Array };
27
+
28
+ const walletDecoders: Readonly<Record<WalletStage, (bytes: Uint8Array) => Serializable>> = {
29
+ Unproven: (bytes) =>
30
+ WalletLedger9.Transaction.deserialize("signature", "pre-proof", "pre-binding", bytes),
31
+ Unbound: (bytes) =>
32
+ WalletLedger9.Transaction.deserialize("signature", "proof", "pre-binding", bytes),
33
+ Finalized: (bytes) =>
34
+ WalletLedger9.Transaction.deserialize("signature", "proof", "binding", bytes),
35
+ };
36
+
37
+ /** The protocol version the wallet facade is acting at right now. */
38
+ export async function activeProtocolVersion(
39
+ wallet: VersionedWallet,
40
+ ): Promise<ProtocolVersion.ProtocolVersion> {
41
+ return (await Rx.firstValueFrom(wallet.state())).activeProtocolVersion;
42
+ }
43
+
44
+ /**
45
+ * The epoch the chain is in right now, which is the only one the wallet will
46
+ * unwrap into. Uses the facade's default fork schedule because the facade is
47
+ * configured without an explicit `forks` entry.
48
+ */
49
+ async function activeEpoch(
50
+ wallet: VersionedWallet,
51
+ ): Promise<ProtocolVersion.ProtocolVersion.Range> {
52
+ return ProtocolVersion.epochOf(
53
+ await activeProtocolVersion(wallet),
54
+ DefaultForkSchedule.v9,
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Hands a ledger-v9 transaction to the wallet SDK: decodes its bytes with the
60
+ * wallet SDK's ledger copy and seals them at the chain's active protocol
61
+ * version.
62
+ */
63
+ export async function adoptInWallet<S extends WalletStage>(
64
+ wallet: VersionedWallet,
65
+ stage: S,
66
+ tx: { serialize(): Uint8Array },
67
+ ): Promise<WalletTransaction<S>> {
68
+ return WalletTransaction.adopt(
69
+ stage,
70
+ walletDecoders[stage](tx.serialize()),
71
+ await activeProtocolVersion(wallet),
72
+ );
73
+ }
74
+
75
+ /**
76
+ * Reads the transaction a wallet SDK handle carries, refusing a handle sealed
77
+ * for another epoch than the one the chain is in. The result is an object of
78
+ * the wallet SDK's ledger-v9 copy.
79
+ */
80
+ export async function unwrapInWallet<T extends Serializable = WalletLedger9.Transaction<
81
+ WalletLedger9.Signaturish,
82
+ WalletLedger9.Proofish,
83
+ WalletLedger9.Bindingish
84
+ >>(
85
+ wallet: VersionedWallet,
86
+ handle: WalletTransaction,
87
+ ): Promise<T> {
88
+ const unwrapped = WalletTransaction.unwrapWithin<T>(
89
+ handle,
90
+ await activeEpoch(wallet),
91
+ );
92
+ if (unwrapped._tag === "Left") {
93
+ throw unwrapped.left;
94
+ }
95
+ return unwrapped.right;
96
+ }
97
+
98
+ /**
99
+ * Reads the bytes of a finalized wallet SDK handle, refusing a handle sealed
100
+ * for another epoch than the one the chain is in.
101
+ */
102
+ export async function finalizedFromWallet(
103
+ wallet: VersionedWallet,
104
+ handle: WalletTransaction<"Finalized">,
105
+ ): Promise<Uint8Array> {
106
+ return (await unwrapInWallet(wallet, handle)).serialize();
107
+ }
@@ -82,10 +82,6 @@ try {
82
82
  amount: fundingAmount,
83
83
  }],
84
84
  }],
85
- {
86
- shieldedSecretKeys: source.zswapSecretKeys,
87
- dustSecretKey: source.dustSecretKey,
88
- },
89
85
  { ttl: new Date(Date.now() + 30 * 60 * 1_000) },
90
86
  );
91
87
  if (fundingRecipe.type !== "UNPROVEN_TRANSACTION") {
@@ -194,8 +194,8 @@ describe("wallet-v2 migration", () => {
194
194
  });
195
195
 
196
196
  describe("network defaults", () => {
197
- test("public config type accepts the complete pre-faucet object shape", () => {
198
- const preFaucetConfig: MidnightNetworkConfig = {
197
+ test("public config type accepts the complete object shape", () => {
198
+ const config: MidnightNetworkConfig = {
199
199
  id: "undeployed",
200
200
  indexer: "http://127.0.0.1:8088/api/v4/graphql",
201
201
  indexerWS: "ws://127.0.0.1:8088/api/v4/graphql/ws",
@@ -204,19 +204,14 @@ describe("network defaults", () => {
204
204
  walletSeed: "legacy-wallet-seed",
205
205
  };
206
206
 
207
- expect(Object.hasOwn(preFaucetConfig, "faucetUrl")).toBe(false);
208
- });
209
-
210
- test("stagenet uses the complete explicit Shielded Tools profile", () => {
211
- expect(defaultMidnightNetworkConfig("stagenet")).toEqual({
212
- indexer: "https://indexer.stagenet.shielded.tools/api/v4/graphql",
213
- indexerWS: "wss://indexer.stagenet.shielded.tools/api/v4/graphql/ws",
214
- node: "wss://rpc.stagenet.shielded.tools",
215
- proofServer: "http://127.0.0.1:6300",
216
- faucetUrl: "https://faucet.stagenet.shielded.tools/api/drips",
217
- networkId: "stagenet",
218
- genesisWalletSeed: "",
219
- });
207
+ expect(Object.keys(config).sort()).toEqual([
208
+ "id",
209
+ "indexer",
210
+ "indexerWS",
211
+ "node",
212
+ "proofServer",
213
+ "walletSeed",
214
+ ]);
220
215
  });
221
216
 
222
217
  test("undeployed retains the complete loopback v4 profile", () => {
@@ -230,8 +225,8 @@ describe("network defaults", () => {
230
225
  });
231
226
  });
232
227
 
233
- for (const networkId of ["preview", "preprod"] as const) {
234
- test(`preserves node-1.x network ID ${networkId} without remapping`, () => {
228
+ for (const networkId of ["mainnet", "preview", "preprod"] as const) {
229
+ test(`hosted network ID ${networkId} follows the Midnight endpoint convention`, () => {
235
230
  expect(defaultMidnightNetworkConfig(networkId)).toEqual({
236
231
  indexer: `https://indexer.${networkId}.midnight.network/api/v4/graphql`,
237
232
  indexerWS: `wss://indexer.${networkId}.midnight.network/api/v4/graphql/ws`,
@@ -254,7 +249,7 @@ describe("network defaults", () => {
254
249
  });
255
250
  });
256
251
 
257
- test("selected stagenet defaults expose inert faucet metadata without fetch", async () => {
252
+ test("selected mainnet defaults resolve without any network call", async () => {
258
253
  const moduleUrl = new URL("../src/midnight-env.ts", import.meta.url).href;
259
254
  const probe = Bun.spawn([
260
255
  process.execPath,
@@ -271,7 +266,7 @@ describe("network defaults", () => {
271
266
  ], {
272
267
  env: {
273
268
  ...process.env,
274
- MIDNIGHT_NETWORK_ID: "stagenet",
269
+ MIDNIGHT_NETWORK_ID: "mainnet",
275
270
  MIDNIGHT_INDEXER_HTTP: "",
276
271
  MIDNIGHT_INDEXER_WS: "",
277
272
  MIDNIGHT_NODE_HTTP: "",
@@ -294,19 +289,18 @@ describe("network defaults", () => {
294
289
  expect(exitCode).toBe(0);
295
290
  expect(JSON.parse(stdout)).toEqual({
296
291
  midnightNetworkConfig: {
297
- id: "stagenet",
298
- indexer: "https://indexer.stagenet.shielded.tools/api/v4/graphql",
299
- indexerWS: "wss://indexer.stagenet.shielded.tools/api/v4/graphql/ws",
300
- node: "wss://rpc.stagenet.shielded.tools",
292
+ id: "mainnet",
293
+ indexer: "https://indexer.mainnet.midnight.network/api/v4/graphql",
294
+ indexerWS: "wss://indexer.mainnet.midnight.network/api/v4/graphql/ws",
295
+ node: "https://rpc.mainnet.midnight.network",
301
296
  proofServer: "http://127.0.0.1:6300",
302
- faucetUrl: "https://faucet.stagenet.shielded.tools/api/drips",
303
297
  walletSeed: "",
304
298
  },
305
299
  networkCalls: 0,
306
300
  });
307
301
  });
308
302
 
309
- test("selected stagenet environment overrides win while faucet stays inert", async () => {
303
+ test("selected network environment overrides win without any network call", async () => {
310
304
  const moduleUrl = new URL("../src/midnight-env.ts", import.meta.url).href;
311
305
  const probe = Bun.spawn([
312
306
  process.execPath,
@@ -323,7 +317,7 @@ describe("network defaults", () => {
323
317
  ], {
324
318
  env: {
325
319
  ...process.env,
326
- MIDNIGHT_NETWORK_ID: "stagenet",
320
+ MIDNIGHT_NETWORK_ID: "mainnet",
327
321
  MIDNIGHT_INDEXER_HTTP: "https://override.example/indexer",
328
322
  MIDNIGHT_INDEXER_WS: "wss://override.example/indexer/ws",
329
323
  MIDNIGHT_NODE_HTTP: "wss://override.example/node",
@@ -353,7 +347,7 @@ describe("network defaults", () => {
353
347
  proofServer: midnightNetworkConfig.proofServer,
354
348
  walletSeed: midnightNetworkConfig.walletSeed,
355
349
  }).toEqual({
356
- id: "stagenet",
350
+ id: "mainnet",
357
351
  indexer: "https://override.example/indexer",
358
352
  indexerWS: "wss://override.example/indexer/ws",
359
353
  node: "wss://override.example/node",