@effectstream/midnight-contracts 0.103.0 → 0.103.2

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/README.md CHANGED
@@ -123,6 +123,21 @@ The circuit list is enumerated automatically from the contract's compiled `keys/
123
123
  - `deployMidnightContractPhased(...)`: the phased deploy routine `deployMidnightContract` delegates to when `phasedVerifierKeys` is set. Exported for advanced callers that already have built providers and a wallet.
124
124
  - `DeployConfig`, `NetworkUrls`: input types.
125
125
 
126
+ Wallet and dust helpers, exported from the package root:
127
+
128
+ - `buildWalletFacade(...)`: builds the wallet facade the deploy and batcher paths run on.
129
+ - `registerNightForDust(...)`, `getInitialDustState(...)`: register NIGHT to generate dust and read the starting state.
130
+ - `waitForDustFunds(wallet, opts)` / `waitForDustFundsWithRetry(...)`: block until a wallet has spendable dust — the step that makes a freshly funded wallet usable.
131
+ - `saveDustState(...)` / `loadDustState(...)`: persist and restore dust state across restarts, avoiding a re-sync.
132
+ - `resolveFacadeDustBalance(...)`: current dust balance for a facade.
133
+
134
+ Other subpaths:
135
+
136
+ - `@effectstream/midnight-contracts/wallet-info` - wallet inspection plus the dust-state persistence helpers above, and `resolveWalletSyncTimeoutMs()`.
137
+ - `@effectstream/midnight-contracts/midnight-env` - `midnightNetworkConfig` (the resolved `{ id, indexer, indexerWS, node, proofServer }` endpoints, env-overridable), `MidnightNetworkConfig`, and `isExternalProofServerConfigured`.
138
+ - `@effectstream/midnight-contracts/ledger-from-tx-state` - `midnightLedgerFromTxStateHex(...)` and the `MidnightLedgerFn` / `MidnightContractStateDeserializer` types, for decoding contract ledger state from a serialized transaction state.
139
+ - `@effectstream/midnight-contracts/types` - shared types.
140
+
126
141
  ## Examples
127
142
 
128
143
  End-to-end usage in templates:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effectstream/midnight-contracts",
3
- "version": "0.103.0",
3
+ "version": "0.103.2",
4
4
  "license": "(MIT OR Apache-2.0)",
5
5
  "homepage": "https://effectstream.github.io/docs/",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  "./types": "./src/types.ts"
22
22
  },
23
23
  "dependencies": {
24
- "@effectstream/utils": "0.103.0",
24
+ "@effectstream/utils": "0.103.2",
25
25
  "@scure/bip39": "^2.0.1",
26
26
  "dotenv": "^16.4.7",
27
27
  "rxjs": "^7.8.2",
@@ -0,0 +1,73 @@
1
+ // Dust-state persistence — the ONLY disk I/O behind the `./wallet-info`
2
+ // subpath, split into its own module so it stays out of browser bundles.
3
+ //
4
+ // `get-wallet-info.ts` is reachable from browsers (MidnightLocal.connectFromSeed
5
+ // imports it to build the wallet facade), and previously carried these
6
+ // functions along with their `node:fs` import. Bundlers shim `node:fs` to an
7
+ // empty/null module, which once crashed the whole entry at import time and,
8
+ // after that was patched, still shipped dead fs/path polyfills to the client.
9
+ // Here the functions are reached only via a lazy `await import()` inside
10
+ // `waitForDustFundsWithRetry` — a node-side flow — so `node:fs`/`node:path`
11
+ // never enter the browser's static graph at all.
12
+ import * as path from "node:path";
13
+ import * as fs from "node:fs";
14
+
15
+ const log = console;
16
+
17
+ /**
18
+ * Build the dust state file path from network + seed.
19
+ * Uses first 16 hex chars of the seed as a stable identifier — the seed
20
+ * deterministically maps to a dust address, so this is a unique key per wallet
21
+ * that is available before building the facade.
22
+ */
23
+ export function getDustStatePath(baseDir: string, networkId: string, seed: string): string {
24
+ const seedKey = seed.slice(0, 16);
25
+ return path.join(baseDir, `${networkId}-${seedKey}.json`);
26
+ }
27
+
28
+ function isUndeployedNetwork(networkId: string): boolean {
29
+ return networkId.toLowerCase() === "undeployed";
30
+ }
31
+
32
+ /**
33
+ * Save serialized dust wallet state to disk.
34
+ * No-ops for "undeployed" networks (chain resets make cached state invalid).
35
+ * @param seed - Wallet seed hex string (used to derive stable file path)
36
+ */
37
+ export function saveDustState(
38
+ baseDir: string,
39
+ networkId: string,
40
+ seed: string,
41
+ serializedState: string,
42
+ ): string | null {
43
+ if (isUndeployedNetwork(networkId)) return null;
44
+ const filePath = getDustStatePath(baseDir, networkId, seed);
45
+ try {
46
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
47
+ fs.writeFileSync(filePath, serializedState, "utf-8");
48
+ log.info(`Dust state saved to ${filePath}`);
49
+ return filePath;
50
+ } catch (e) {
51
+ log.warn(`Failed to save dust state to ${filePath}: ${e instanceof Error ? e.message : String(e)}`);
52
+ return null;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Load previously saved dust wallet state from disk.
58
+ * Always returns null for "undeployed" networks.
59
+ * @param seed - Wallet seed hex string (used to derive stable file path)
60
+ */
61
+ export function loadDustState(
62
+ baseDir: string,
63
+ networkId: string,
64
+ seed: string,
65
+ ): string | null {
66
+ if (isUndeployedNetwork(networkId)) return null;
67
+ const filePath = getDustStatePath(baseDir, networkId, seed);
68
+ try {
69
+ return fs.readFileSync(filePath, "utf-8");
70
+ } catch {
71
+ return null;
72
+ }
73
+ }
@@ -38,77 +38,24 @@ import {
38
38
  TransactionHistoryStorage,
39
39
  } from "@midnightntwrk/wallet-sdk-abstractions";
40
40
  import { MidnightBech32m } from "@midnightntwrk/wallet-sdk-address-format";
41
- import * as path from "node:path";
42
- import { mkdirSync, writeFileSync, readFileSync } from "node:fs";
41
+ // No node:fs / node:path here — this module is reachable from BROWSER bundles
42
+ // (MidnightLocal.connectFromSeed imports `./wallet-info` to build the wallet
43
+ // facade), and browser bundlers shim those to empty/null modules. All disk I/O
44
+ // (dust-state persistence) lives in dust-state.ts, loaded lazily inside
45
+ // waitForDustFundsWithRetry so it never enters the browser's static graph.
43
46
  import { CONSTANTS } from "./constants.ts";
44
47
  import type { NetworkUrls, WalletResult } from "./types.ts";
45
48
  import { midnightNetworkConfig } from "./midnight-env.ts";
46
49
  import { getEnv, args as getArgs, exit, isNotFoundError } from "@effectstream/utils/runtime";
47
50
 
48
51
  // ============================================================================
49
- // Dust State Persistence
52
+ // Dust State Persistence — moved to dust-state.ts (node-only; disk I/O).
53
+ // Re-exported from mod.ts for node consumers; NOT re-exported here, since a
54
+ // static re-export would pull node:fs right back into the browser graph.
50
55
  // ============================================================================
51
56
 
52
57
  const DEFAULT_DUST_STATE_DIR = "dust-state";
53
58
 
54
- /**
55
- * Build the dust state file path from network + seed.
56
- * Uses first 16 hex chars of the seed as a stable identifier — the seed
57
- * deterministically maps to a dust address, so this is a unique key per wallet
58
- * that is available before building the facade.
59
- */
60
- function getDustStatePath(baseDir: string, networkId: string, seed: string): string {
61
- const seedKey = seed.slice(0, 16);
62
- return path.join(baseDir, `${networkId}-${seedKey}.json`);
63
- }
64
-
65
- function isUndeployedNetwork(networkId: string): boolean {
66
- return networkId.toLowerCase() === "undeployed";
67
- }
68
-
69
- /**
70
- * Save serialized dust wallet state to disk.
71
- * No-ops for "undeployed" networks (chain resets make cached state invalid).
72
- * @param seed - Wallet seed hex string (used to derive stable file path)
73
- */
74
- export function saveDustState(
75
- baseDir: string,
76
- networkId: string,
77
- seed: string,
78
- serializedState: string,
79
- ): string | null {
80
- if (isUndeployedNetwork(networkId)) return null;
81
- const filePath = getDustStatePath(baseDir, networkId, seed);
82
- try {
83
- mkdirSync(path.dirname(filePath), { recursive: true });
84
- writeFileSync(filePath, serializedState, "utf-8");
85
- log.info(`Dust state saved to ${filePath}`);
86
- return filePath;
87
- } catch (e) {
88
- log.warn(`Failed to save dust state to ${filePath}: ${e instanceof Error ? e.message : String(e)}`);
89
- return null;
90
- }
91
- }
92
-
93
- /**
94
- * Load previously saved dust wallet state from disk.
95
- * Always returns null for "undeployed" networks.
96
- * @param seed - Wallet seed hex string (used to derive stable file path)
97
- */
98
- export function loadDustState(
99
- baseDir: string,
100
- networkId: string,
101
- seed: string,
102
- ): string | null {
103
- if (isUndeployedNetwork(networkId)) return null;
104
- const filePath = getDustStatePath(baseDir, networkId, seed);
105
- try {
106
- return readFileSync(filePath, "utf-8");
107
- } catch {
108
- return null;
109
- }
110
- }
111
-
112
59
  // ============================================================================
113
60
  // Key Derivation
114
61
  // ============================================================================
@@ -554,6 +501,11 @@ export async function waitForDustFundsWithRetry(
554
501
 
555
502
  const networkIdStr = String(networkId);
556
503
 
504
+ // Lazy import keeps node:fs out of the browser's static graph — this retry
505
+ // flow is node-side (long-running dust sync with disk caching), while the
506
+ // module as a whole is bundled for browsers via `./wallet-info`.
507
+ const { loadDustState, saveDustState, getDustStatePath } = await import("./dust-state.ts");
508
+
557
509
  // Pre-load cached state from disk before building any wallet.
558
510
  // Uses seed-based path so we don't need the dust address yet.
559
511
  const cachedState: string | null = loadDustState(dustStateDir, networkIdStr, seed);
package/src/mod.ts CHANGED
@@ -18,9 +18,12 @@ export {
18
18
  getInitialDustState,
19
19
  waitForDustFunds,
20
20
  waitForDustFundsWithRetry,
21
- saveDustState,
22
- loadDustState,
23
21
  } from "./get-wallet-info.ts";
22
+ // Disk-backed dust persistence lives in its own node-only module so the
23
+ // browser-reachable ./wallet-info subpath carries no node:fs. This barrel is
24
+ // node-only anyway (deploy.ts imports node:fs/promises), so a static
25
+ // re-export here is fine and keeps the public API unchanged.
26
+ export { saveDustState, loadDustState } from "./dust-state.ts";
24
27
  export type {
25
28
  WalletSyncMode,
26
29
  DustSyncWithRetryOptions,