@effectstream/midnight-contracts 0.200.2 → 0.200.5

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.2",
3
+ "version": "0.200.5",
4
4
  "license": "(MIT OR Apache-2.0)",
5
5
  "homepage": "https://effectstream.github.io/docs/",
6
6
  "repository": {
@@ -26,7 +26,7 @@
26
26
  "./types": "./src/types.ts"
27
27
  },
28
28
  "dependencies": {
29
- "@effectstream/utils": "0.200.2",
29
+ "@effectstream/utils": "0.200.5",
30
30
  "@midnight-ntwrk/compact-js": "2.5.5-rc.7",
31
31
  "@midnight-ntwrk/compact-runtime": "0.18.0-rc.1",
32
32
  "@midnight-ntwrk/midnight-js-contracts": "5.0.0-beta.6",
@@ -23,6 +23,7 @@ export type NetworkConfig = {
23
23
  indexerWS: string;
24
24
  node: string;
25
25
  proofServer: string;
26
+ faucetUrl?: string;
26
27
  networkId: NetworkId.NetworkId;
27
28
  genesisWalletSeed: string;
28
29
  }
@@ -47,17 +48,28 @@ const deployedNetworkConfig = (networkId: NetworkId.NetworkId): NetworkConfig =>
47
48
  genesisWalletSeed: '',
48
49
  });
49
50
 
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
+
50
61
  /**
51
- * Resolve defaults for every wallet-SDK network ID. Only `undeployed` uses
52
- * loopback services; every deployed or future network ID follows the hosted
53
- * Midnight endpoint convention instead of being restricted to `stagenet`.
62
+ * 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.
54
65
  */
55
66
  export const defaultMidnightNetworkConfig = (
56
67
  networkId: NetworkId.NetworkId,
57
- ): NetworkConfig =>
58
- networkId === "undeployed"
59
- ? undeployedNetworkConfig
60
- : deployedNetworkConfig(networkId);
68
+ ): NetworkConfig => {
69
+ if (networkId === "undeployed") return undeployedNetworkConfig;
70
+ if (networkId === "stagenet") return stagenetNetworkConfig;
71
+ return deployedNetworkConfig(networkId);
72
+ };
61
73
 
62
74
  const networkId = (env("MIDNIGHT_NETWORK_ID") || "undeployed") as NetworkId.NetworkId;
63
75
  const selectedNetworkConfig = defaultMidnightNetworkConfig(networkId);
@@ -72,17 +84,25 @@ if (env("MIDNIGHT_WALLET_SEED")) {
72
84
  walletSeed = selectedNetworkConfig.genesisWalletSeed;
73
85
  }
74
86
 
75
- export const midnightNetworkConfig = {
87
+ export type MidnightNetworkConfig = {
88
+ id: NetworkId.NetworkId;
89
+ indexer: string;
90
+ indexerWS: string;
91
+ node: string;
92
+ proofServer: string;
93
+ faucetUrl?: string;
94
+ walletSeed: string;
95
+ };
96
+
97
+ export const midnightNetworkConfig: MidnightNetworkConfig = {
76
98
  id: selectedNetworkConfig.networkId,
77
99
  indexer: env("MIDNIGHT_INDEXER_HTTP", selectedNetworkConfig.indexer),
78
100
  indexerWS: env("MIDNIGHT_INDEXER_WS", selectedNetworkConfig.indexerWS),
79
101
  node: env("MIDNIGHT_NODE_HTTP", selectedNetworkConfig.node),
80
102
  proofServer: env(["MIDNIGHT_PROOF_SERVER_URL", "MIDNIGHT_PROOF_SERVER"], selectedNetworkConfig.proofServer),
103
+ faucetUrl: selectedNetworkConfig.faucetUrl,
81
104
  walletSeed,
82
105
  };
83
106
 
84
107
  const isLocalProofServer = !!midnightNetworkConfig.proofServer.match(/(localhost|127\.0\.0\.1)/);
85
108
  export const isExternalProofServerConfigured = !isLocalProofServer;
86
-
87
- // Set this using MIDNIGHT_NETWORK_ID=<network-id>
88
- export type MidnightNetworkConfig = typeof midnightNetworkConfig;
@@ -19,7 +19,10 @@ import {
19
19
  resolveFacadeDustFundsReadiness,
20
20
  waitForDustFunds,
21
21
  } from "../src/get-wallet-info.ts";
22
- import { defaultMidnightNetworkConfig } from "../src/midnight-env.ts";
22
+ import {
23
+ defaultMidnightNetworkConfig,
24
+ type MidnightNetworkConfig,
25
+ } from "../src/midnight-env.ts";
23
26
  import type { WalletResult } from "../src/types.ts";
24
27
 
25
28
  describe("ledger-v9 compatibility traps", () => {
@@ -191,29 +194,172 @@ describe("wallet-v2 migration", () => {
191
194
  });
192
195
 
193
196
  describe("network defaults", () => {
194
- test("undeployed uses local GraphQL v4 endpoints", () => {
195
- const config = defaultMidnightNetworkConfig("undeployed");
196
- expect(config.indexer).toBe("http://127.0.0.1:8088/api/v4/graphql");
197
- expect(config.indexerWS).toBe("ws://127.0.0.1:8088/api/v4/graphql/ws");
197
+ test("public config type accepts the complete pre-faucet object shape", () => {
198
+ const preFaucetConfig: MidnightNetworkConfig = {
199
+ id: "undeployed",
200
+ indexer: "http://127.0.0.1:8088/api/v4/graphql",
201
+ indexerWS: "ws://127.0.0.1:8088/api/v4/graphql/ws",
202
+ node: "http://127.0.0.1:9944",
203
+ proofServer: "http://127.0.0.1:6300",
204
+ walletSeed: "legacy-wallet-seed",
205
+ };
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
+ });
198
220
  });
199
221
 
200
- for (const networkId of [
201
- "mainnet",
202
- "testnet",
203
- "devnet",
204
- "qanet",
205
- "preview",
206
- "preprod",
207
- "stagenet",
208
- "future-network",
209
- ]) {
210
- test(`preserves deployed network ID ${networkId}`, () => {
211
- const config = defaultMidnightNetworkConfig(networkId);
212
- expect(config.networkId).toBe(networkId);
213
- expect(config.indexer).toBe(
214
- `https://indexer.${networkId}.midnight.network/api/v4/graphql`,
215
- );
216
- expect(config.node).toBe(`https://rpc.${networkId}.midnight.network`);
222
+ test("undeployed retains the complete loopback v4 profile", () => {
223
+ expect(defaultMidnightNetworkConfig("undeployed")).toEqual({
224
+ indexer: "http://127.0.0.1:8088/api/v4/graphql",
225
+ indexerWS: "ws://127.0.0.1:8088/api/v4/graphql/ws",
226
+ node: "http://127.0.0.1:9944",
227
+ proofServer: "http://127.0.0.1:6300",
228
+ networkId: "undeployed",
229
+ genesisWalletSeed: "0000000000000000000000000000000000000000000000000000000000000001",
230
+ });
231
+ });
232
+
233
+ for (const networkId of ["preview", "preprod"] as const) {
234
+ test(`preserves node-1.x network ID ${networkId} without remapping`, () => {
235
+ expect(defaultMidnightNetworkConfig(networkId)).toEqual({
236
+ indexer: `https://indexer.${networkId}.midnight.network/api/v4/graphql`,
237
+ indexerWS: `wss://indexer.${networkId}.midnight.network/api/v4/graphql/ws`,
238
+ node: `https://rpc.${networkId}.midnight.network`,
239
+ proofServer: "http://127.0.0.1:6300",
240
+ networkId,
241
+ genesisWalletSeed: "",
242
+ });
217
243
  });
218
244
  }
245
+
246
+ test("preserves an arbitrary future network ID and hosted convention", () => {
247
+ expect(defaultMidnightNetworkConfig("future-network")).toEqual({
248
+ indexer: "https://indexer.future-network.midnight.network/api/v4/graphql",
249
+ indexerWS: "wss://indexer.future-network.midnight.network/api/v4/graphql/ws",
250
+ node: "https://rpc.future-network.midnight.network",
251
+ proofServer: "http://127.0.0.1:6300",
252
+ networkId: "future-network",
253
+ genesisWalletSeed: "",
254
+ });
255
+ });
256
+
257
+ test("selected stagenet defaults expose inert faucet metadata without fetch", async () => {
258
+ const moduleUrl = new URL("../src/midnight-env.ts", import.meta.url).href;
259
+ const probe = Bun.spawn([
260
+ process.execPath,
261
+ "--eval",
262
+ `
263
+ let networkCalls = 0;
264
+ globalThis.fetch = () => {
265
+ networkCalls += 1;
266
+ throw new Error("unexpected network call");
267
+ };
268
+ const { midnightNetworkConfig } = await import(${JSON.stringify(moduleUrl)});
269
+ console.log(JSON.stringify({ midnightNetworkConfig, networkCalls }));
270
+ `,
271
+ ], {
272
+ env: {
273
+ ...process.env,
274
+ MIDNIGHT_NETWORK_ID: "stagenet",
275
+ MIDNIGHT_INDEXER_HTTP: "",
276
+ MIDNIGHT_INDEXER_WS: "",
277
+ MIDNIGHT_NODE_HTTP: "",
278
+ MIDNIGHT_PROOF_SERVER_URL: "",
279
+ MIDNIGHT_PROOF_SERVER: "",
280
+ MIDNIGHT_WALLET_SEED: "",
281
+ MIDNIGHT_WALLET_MNEMONIC: "",
282
+ },
283
+ stdout: "pipe",
284
+ stderr: "pipe",
285
+ });
286
+
287
+ const [exitCode, stdout, stderr] = await Promise.all([
288
+ probe.exited,
289
+ new Response(probe.stdout).text(),
290
+ new Response(probe.stderr).text(),
291
+ ]);
292
+
293
+ expect(stderr).toBe("");
294
+ expect(exitCode).toBe(0);
295
+ expect(JSON.parse(stdout)).toEqual({
296
+ 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",
301
+ proofServer: "http://127.0.0.1:6300",
302
+ faucetUrl: "https://faucet.stagenet.shielded.tools/api/drips",
303
+ walletSeed: "",
304
+ },
305
+ networkCalls: 0,
306
+ });
307
+ });
308
+
309
+ test("selected stagenet environment overrides win while faucet stays inert", async () => {
310
+ const moduleUrl = new URL("../src/midnight-env.ts", import.meta.url).href;
311
+ const probe = Bun.spawn([
312
+ process.execPath,
313
+ "--eval",
314
+ `
315
+ let networkCalls = 0;
316
+ globalThis.fetch = () => {
317
+ networkCalls += 1;
318
+ throw new Error("unexpected network call");
319
+ };
320
+ const { midnightNetworkConfig } = await import(${JSON.stringify(moduleUrl)});
321
+ console.log(JSON.stringify({ midnightNetworkConfig, networkCalls }));
322
+ `,
323
+ ], {
324
+ env: {
325
+ ...process.env,
326
+ MIDNIGHT_NETWORK_ID: "stagenet",
327
+ MIDNIGHT_INDEXER_HTTP: "https://override.example/indexer",
328
+ MIDNIGHT_INDEXER_WS: "wss://override.example/indexer/ws",
329
+ MIDNIGHT_NODE_HTTP: "wss://override.example/node",
330
+ MIDNIGHT_PROOF_SERVER_URL: "http://override.example/proof",
331
+ MIDNIGHT_PROOF_SERVER: "http://ignored.example/proof",
332
+ MIDNIGHT_WALLET_SEED: "seed-override",
333
+ MIDNIGHT_WALLET_MNEMONIC: "invalid mnemonic must remain unused",
334
+ },
335
+ stdout: "pipe",
336
+ stderr: "pipe",
337
+ });
338
+
339
+ const [exitCode, stdout, stderr] = await Promise.all([
340
+ probe.exited,
341
+ new Response(probe.stdout).text(),
342
+ new Response(probe.stderr).text(),
343
+ ]);
344
+
345
+ expect(stderr).toBe("");
346
+ expect(exitCode).toBe(0);
347
+ const { midnightNetworkConfig, networkCalls } = JSON.parse(stdout);
348
+ expect({
349
+ id: midnightNetworkConfig.id,
350
+ indexer: midnightNetworkConfig.indexer,
351
+ indexerWS: midnightNetworkConfig.indexerWS,
352
+ node: midnightNetworkConfig.node,
353
+ proofServer: midnightNetworkConfig.proofServer,
354
+ walletSeed: midnightNetworkConfig.walletSeed,
355
+ }).toEqual({
356
+ id: "stagenet",
357
+ indexer: "https://override.example/indexer",
358
+ indexerWS: "wss://override.example/indexer/ws",
359
+ node: "wss://override.example/node",
360
+ proofServer: "http://override.example/proof",
361
+ walletSeed: "seed-override",
362
+ });
363
+ expect(networkCalls).toBe(0);
364
+ });
219
365
  });