create-miden-app 1.0.6 → 1.0.7

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.
Files changed (70) hide show
  1. package/package.json +1 -1
  2. package/template/.claude/commands/review-security.md +67 -0
  3. package/template/.claude/settings.json +1 -7
  4. package/template/.claude/settings.local.json +24 -0
  5. package/template/.claude/skills/frontend-pitfalls/SKILL.md +28 -31
  6. package/template/.claude/skills/frontend-source-guide/SKILL.md +14 -14
  7. package/template/.claude/skills/miden-concepts/SKILL.md +4 -2
  8. package/template/.claude/skills/react-sdk-patterns/SKILL.md +294 -28
  9. package/template/.claude/skills/signer-integration/SKILL.md +22 -3
  10. package/template/.claude/skills/testing-patterns/SKILL.md +201 -40
  11. package/template/.claude/skills/vite-wasm-setup/SKILL.md +20 -14
  12. package/template/.claude/skills/web-client-usage/SKILL.md +454 -0
  13. package/template/.env.example +15 -2
  14. package/template/.mcp.json +9 -0
  15. package/template/CLAUDE.md +49 -16
  16. package/template/README.md +85 -19
  17. package/template/package.json +5 -4
  18. package/template/public/packages/counter_account.masp +0 -0
  19. package/template/public/packages/increment_note.masp +0 -0
  20. package/template/src/__tests__/fixtures/accounts.ts +17 -6
  21. package/template/src/__tests__/fixtures/index.ts +1 -0
  22. package/template/src/__tests__/mocks/miden-sdk-react.ts +18 -1
  23. package/template/src/__tests__/patterns/mutation-hook.test.tsx +2 -2
  24. package/template/src/__tests__/patterns/provider-setup.test.tsx +2 -0
  25. package/template/src/components/AppContent.tsx +33 -3
  26. package/template/{create-miden-app/template/src/components/Counter.tsx → src/components/ConfiguredCounter.tsx} +7 -4
  27. package/template/src/components/Counter.tsx +12 -41
  28. package/template/src/components/__tests__/AppContent.test.tsx +192 -4
  29. package/template/src/components/__tests__/ConfiguredCounter.test.tsx +116 -0
  30. package/template/src/components/__tests__/Counter.test.tsx +24 -94
  31. package/template/src/config.ts +26 -6
  32. package/template/src/hooks/__tests__/useIncrementCounter.test.tsx +257 -0
  33. package/template/src/hooks/useIncrementCounter.ts +109 -50
  34. package/template/src/providers.tsx +20 -24
  35. package/template/vite.config.ts +1 -1
  36. package/template/vitest.config.ts +1 -2
  37. package/template/yarn.lock +761 -688
  38. package/template/create-miden-app/template/.claude/hooks/typecheck.sh +0 -27
  39. package/template/create-miden-app/template/.claude/settings.json +0 -17
  40. package/template/create-miden-app/template/.claude/skills/frontend-pitfalls/SKILL.md +0 -189
  41. package/template/create-miden-app/template/.claude/skills/frontend-source-guide/SKILL.md +0 -163
  42. package/template/create-miden-app/template/.claude/skills/miden-concepts/SKILL.md +0 -108
  43. package/template/create-miden-app/template/.claude/skills/react-sdk-patterns/SKILL.md +0 -294
  44. package/template/create-miden-app/template/.claude/skills/signer-integration/SKILL.md +0 -158
  45. package/template/create-miden-app/template/.claude/skills/vite-wasm-setup/SKILL.md +0 -128
  46. package/template/create-miden-app/template/.env.example +0 -5
  47. package/template/create-miden-app/template/CLAUDE.md +0 -116
  48. package/template/create-miden-app/template/README.md +0 -61
  49. package/template/create-miden-app/template/eslint.config.js +0 -23
  50. package/template/create-miden-app/template/index.html +0 -13
  51. package/template/create-miden-app/template/package.json +0 -34
  52. package/template/create-miden-app/template/public/vite.svg +0 -1
  53. package/template/create-miden-app/template/src/App.tsx +0 -10
  54. package/template/create-miden-app/template/src/assets/miden.svg +0 -3
  55. package/template/create-miden-app/template/src/assets/react.svg +0 -1
  56. package/template/create-miden-app/template/src/components/AppContent.css +0 -45
  57. package/template/create-miden-app/template/src/components/AppContent.tsx +0 -50
  58. package/template/create-miden-app/template/src/components/Counter.css +0 -27
  59. package/template/create-miden-app/template/src/config.ts +0 -21
  60. package/template/create-miden-app/template/src/hooks/useIncrementCounter.ts +0 -136
  61. package/template/create-miden-app/template/src/index.css +0 -75
  62. package/template/create-miden-app/template/src/lib/miden.ts +0 -9
  63. package/template/create-miden-app/template/src/main.tsx +0 -10
  64. package/template/create-miden-app/template/src/providers.tsx +0 -31
  65. package/template/create-miden-app/template/src/vite-env.d.ts +0 -1
  66. package/template/create-miden-app/template/tsconfig.app.json +0 -32
  67. package/template/create-miden-app/template/tsconfig.json +0 -7
  68. package/template/create-miden-app/template/tsconfig.node.json +0 -24
  69. package/template/create-miden-app/template/vite.config.ts +0 -17
  70. package/template/create-miden-app/template/yarn.lock +0 -1697
@@ -2,19 +2,130 @@ import { render, screen } from "@testing-library/react";
2
2
  import { vi, describe, it, expect, beforeEach } from "vitest";
3
3
 
4
4
  vi.mock("@miden-sdk/react", () => import("@/__tests__/mocks/miden-sdk-react"));
5
- vi.mock("@miden-sdk/miden-wallet-adapter", () => ({
6
- WalletMultiButton: () => <button>Connect Wallet</button>,
5
+ vi.mock("@miden-sdk/miden-wallet-adapter-react", () => ({
6
+ useMidenFiWallet: vi.fn(() => ({
7
+ autoConnect: false,
8
+ wallets: [],
9
+ wallet: null,
10
+ address: null,
11
+ publicKey: null,
12
+ connected: false,
13
+ connecting: false,
14
+ disconnecting: false,
15
+ select: vi.fn(),
16
+ connect: vi.fn(async () => undefined),
17
+ disconnect: vi.fn(async () => undefined),
18
+ requestTransaction: vi.fn(async () => "0xtx"),
19
+ requestAssets: undefined,
20
+ requestPrivateNotes: undefined,
21
+ signBytes: undefined,
22
+ importPrivateNote: undefined,
23
+ requestConsumableNotes: undefined,
24
+ waitForTransaction: undefined,
25
+ requestSend: undefined,
26
+ requestConsume: undefined,
27
+ createAccount: undefined,
28
+ })),
29
+ }));
30
+ vi.mock("@miden-sdk/miden-wallet-adapter-base", () => ({
31
+ WalletReadyState: {
32
+ Installed: "Installed",
33
+ NotDetected: "NotDetected",
34
+ Loadable: "Loadable",
35
+ Unsupported: "Unsupported",
36
+ },
7
37
  }));
8
38
  vi.mock("@/components/Counter", () => ({
9
39
  Counter: () => <div data-testid="counter">Counter Mock</div>,
10
40
  }));
11
41
 
12
42
  import { useMiden, useSyncState } from "@miden-sdk/react";
43
+ import { useMidenFiWallet } from "@miden-sdk/miden-wallet-adapter-react";
44
+ import userEvent from "@testing-library/user-event";
13
45
  import { AppContent } from "../AppContent";
14
46
 
47
+ type WalletState = ReturnType<typeof useMidenFiWallet>;
48
+ type WalletInner = NonNullable<WalletState["wallet"]>;
49
+
50
+ function walletState(
51
+ overrides: Partial<{
52
+ readyState: "Installed" | "NotDetected" | "Loadable" | "Unsupported";
53
+ connected: boolean;
54
+ connecting: boolean;
55
+ address: string | null;
56
+ connect: () => Promise<void>;
57
+ disconnect: () => Promise<void>;
58
+ requestTransaction: WalletState["requestTransaction"];
59
+ }> = {},
60
+ ): WalletState {
61
+ const {
62
+ readyState = "Installed",
63
+ connected = false,
64
+ connecting = false,
65
+ address = connected ? "mtst1arwk88k8smzcq5p30upr6eerw5npmnyz" : null,
66
+ connect = vi.fn(async () => undefined),
67
+ disconnect = vi.fn(async () => undefined),
68
+ requestTransaction = vi.fn(async () => "0xtx"),
69
+ } = overrides;
70
+ // Build a shape that satisfies WalletContextState; the inner `Wallet`
71
+ // (`{ adapter, readyState }`) shape requires an Adapter, which we stub
72
+ // with a structural cast since the component only reads `readyState`.
73
+ const innerWallet = {
74
+ adapter: {} as WalletInner["adapter"],
75
+ readyState,
76
+ } as WalletInner;
77
+ return {
78
+ autoConnect: false,
79
+ wallets: [innerWallet],
80
+ wallet: innerWallet,
81
+ address,
82
+ publicKey: null,
83
+ connected,
84
+ connecting,
85
+ disconnecting: false,
86
+ select: vi.fn(),
87
+ connect,
88
+ disconnect,
89
+ requestTransaction,
90
+ requestAssets: undefined,
91
+ requestPrivateNotes: undefined,
92
+ signBytes: undefined,
93
+ importPrivateNote: undefined,
94
+ requestConsumableNotes: undefined,
95
+ waitForTransaction: undefined,
96
+ requestSend: undefined,
97
+ requestConsume: undefined,
98
+ createAccount: undefined,
99
+ };
100
+ }
101
+
102
+ const midenReady = {
103
+ client: null,
104
+ isReady: true,
105
+ isInitializing: false,
106
+ error: null,
107
+ sync: vi.fn(),
108
+ runExclusive: vi.fn(),
109
+ prover: null,
110
+ signerAccountId: null,
111
+ signerConnected: null,
112
+ };
113
+
15
114
  describe("AppContent", () => {
16
115
  beforeEach(() => {
17
- vi.clearAllMocks();
116
+ vi.resetAllMocks();
117
+ // Restore default ready state after any test that overrides useMiden
118
+ vi.mocked(useMiden).mockReturnValue(midenReady);
119
+ vi.mocked(useSyncState).mockReturnValue({
120
+ syncHeight: 12345,
121
+ isSyncing: false,
122
+ lastSyncTime: Date.now(),
123
+ error: null,
124
+ sync: vi.fn(),
125
+ });
126
+ vi.mocked(useMidenFiWallet).mockReturnValue(
127
+ walletState({ readyState: "NotDetected" }),
128
+ );
18
129
  });
19
130
 
20
131
  it("renders main content when Miden is ready", () => {
@@ -24,7 +135,6 @@ describe("AppContent", () => {
24
135
  expect(screen.getByAltText("Vite logo")).toBeInTheDocument();
25
136
  expect(screen.getByAltText("React logo")).toBeInTheDocument();
26
137
  expect(screen.getByAltText("Miden logo")).toBeInTheDocument();
27
- expect(screen.getByText("Connect Wallet")).toBeInTheDocument();
28
138
  expect(screen.getByTestId("counter")).toBeInTheDocument();
29
139
  });
30
140
 
@@ -56,6 +166,7 @@ describe("AppContent", () => {
56
166
  runExclusive: vi.fn(),
57
167
  prover: null,
58
168
  signerAccountId: null,
169
+ signerConnected: null,
59
170
  });
60
171
 
61
172
  render(<AppContent />);
@@ -65,6 +176,82 @@ describe("AppContent", () => {
65
176
  expect(screen.queryByText("Vite + React + Miden")).not.toBeInTheDocument();
66
177
  });
67
178
 
179
+ it("shows disabled install-wallet button when extension is not detected", () => {
180
+ render(<AppContent />);
181
+ const button = screen.getByRole("button", { name: "Install MidenFi Wallet" });
182
+ expect(button).toBeDisabled();
183
+ });
184
+
185
+ it("shows connect button when wallet is installed and disconnected", () => {
186
+ vi.mocked(useMidenFiWallet).mockReturnValue(
187
+ walletState({ readyState: "Installed", connected: false }),
188
+ );
189
+
190
+ render(<AppContent />);
191
+ expect(
192
+ screen.getByRole("button", { name: "Connect Wallet" }),
193
+ ).toBeEnabled();
194
+ });
195
+
196
+ it("shows disconnect button when wallet is connected", () => {
197
+ vi.mocked(useMidenFiWallet).mockReturnValue(
198
+ walletState({ readyState: "Installed", connected: true }),
199
+ );
200
+
201
+ render(<AppContent />);
202
+ expect(
203
+ screen.getByRole("button", { name: "Disconnect Wallet" }),
204
+ ).toBeInTheDocument();
205
+ });
206
+
207
+ it("shows connecting state while the wallet request is in flight", () => {
208
+ vi.mocked(useMidenFiWallet).mockReturnValue(
209
+ walletState({
210
+ readyState: "Installed",
211
+ connected: false,
212
+ connecting: true,
213
+ }),
214
+ );
215
+
216
+ render(<AppContent />);
217
+ const button = screen.getByRole("button", { name: /Connecting/ });
218
+ expect(button).toBeDisabled();
219
+ });
220
+
221
+ it("calls connect on wallet button click", async () => {
222
+ const mockConnect = vi.fn(async () => undefined);
223
+ vi.mocked(useMidenFiWallet).mockReturnValue(
224
+ walletState({
225
+ readyState: "Installed",
226
+ connected: false,
227
+ connect: mockConnect,
228
+ }),
229
+ );
230
+
231
+ render(<AppContent />);
232
+ const user = userEvent.setup();
233
+ await user.click(screen.getByRole("button", { name: "Connect Wallet" }));
234
+ expect(mockConnect).toHaveBeenCalledOnce();
235
+ });
236
+
237
+ it("calls disconnect on wallet button click", async () => {
238
+ const mockDisconnect = vi.fn(async () => undefined);
239
+ vi.mocked(useMidenFiWallet).mockReturnValue(
240
+ walletState({
241
+ readyState: "Installed",
242
+ connected: true,
243
+ disconnect: mockDisconnect,
244
+ }),
245
+ );
246
+
247
+ render(<AppContent />);
248
+ const user = userEvent.setup();
249
+ await user.click(
250
+ screen.getByRole("button", { name: "Disconnect Wallet" }),
251
+ );
252
+ expect(mockDisconnect).toHaveBeenCalledOnce();
253
+ });
254
+
68
255
  it("shows error message on initialization failure", () => {
69
256
  vi.mocked(useMiden).mockReturnValue({
70
257
  client: null,
@@ -75,6 +262,7 @@ describe("AppContent", () => {
75
262
  runExclusive: vi.fn(),
76
263
  prover: null,
77
264
  signerAccountId: null,
265
+ signerConnected: null,
78
266
  });
79
267
 
80
268
  render(<AppContent />);
@@ -0,0 +1,116 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import userEvent from "@testing-library/user-event";
3
+ import { vi, describe, it, expect, beforeEach } from "vitest";
4
+
5
+ vi.mock("@/hooks/useIncrementCounter", () => ({
6
+ useIncrementCounter: vi.fn(),
7
+ }));
8
+
9
+ import { useIncrementCounter } from "@/hooks/useIncrementCounter";
10
+ import { ConfiguredCounter } from "../ConfiguredCounter";
11
+
12
+ const FIXTURE_ADDRESS = "0xdeadbeef00000001";
13
+
14
+ const defaultHookReturn = {
15
+ increment: vi.fn(),
16
+ count: 42,
17
+ isSubmitting: false,
18
+ isWaiting: false,
19
+ error: null,
20
+ walletConnected: true,
21
+ explorerUrl: `https://testnet.midenscan.com/account/${FIXTURE_ADDRESS}`,
22
+ };
23
+
24
+ describe("ConfiguredCounter", () => {
25
+ beforeEach(() => {
26
+ vi.clearAllMocks();
27
+ vi.mocked(useIncrementCounter).mockReturnValue(defaultHookReturn);
28
+ });
29
+
30
+ it("displays the current count on the button", () => {
31
+ render(<ConfiguredCounter counterAddress={FIXTURE_ADDRESS} />);
32
+ expect(
33
+ screen.getByRole("button", { name: "count is 42" }),
34
+ ).toBeInTheDocument();
35
+ });
36
+
37
+ it("calls increment on button click", async () => {
38
+ const mockIncrement = vi.fn();
39
+ vi.mocked(useIncrementCounter).mockReturnValue({
40
+ ...defaultHookReturn,
41
+ increment: mockIncrement,
42
+ });
43
+
44
+ render(<ConfiguredCounter counterAddress={FIXTURE_ADDRESS} />);
45
+ const user = userEvent.setup();
46
+ await user.click(screen.getByRole("button", { name: "count is 42" }));
47
+ expect(mockIncrement).toHaveBeenCalledOnce();
48
+ });
49
+
50
+ it("shows submitting state", () => {
51
+ vi.mocked(useIncrementCounter).mockReturnValue({
52
+ ...defaultHookReturn,
53
+ isSubmitting: true,
54
+ });
55
+
56
+ render(<ConfiguredCounter counterAddress={FIXTURE_ADDRESS} />);
57
+ const button = screen.getByRole("button", { name: "Submitting..." });
58
+ expect(button).toBeDisabled();
59
+ });
60
+
61
+ it("shows waiting for network state", () => {
62
+ vi.mocked(useIncrementCounter).mockReturnValue({
63
+ ...defaultHookReturn,
64
+ isWaiting: true,
65
+ });
66
+
67
+ render(<ConfiguredCounter counterAddress={FIXTURE_ADDRESS} />);
68
+ const button = screen.getByRole("button", {
69
+ name: "Waiting for network...",
70
+ });
71
+ expect(button).toBeDisabled();
72
+ });
73
+
74
+ it("disables button when wallet not connected", () => {
75
+ vi.mocked(useIncrementCounter).mockReturnValue({
76
+ ...defaultHookReturn,
77
+ walletConnected: false,
78
+ });
79
+
80
+ render(<ConfiguredCounter counterAddress={FIXTURE_ADDRESS} />);
81
+ expect(screen.getByRole("button")).toBeDisabled();
82
+ });
83
+
84
+ it("disables button when count is loading (null)", () => {
85
+ vi.mocked(useIncrementCounter).mockReturnValue({
86
+ ...defaultHookReturn,
87
+ count: null,
88
+ });
89
+
90
+ render(<ConfiguredCounter counterAddress={FIXTURE_ADDRESS} />);
91
+ const button = screen.getByRole("button", { name: "count is ..." });
92
+ expect(button).toBeDisabled();
93
+ });
94
+
95
+ it("displays error message", () => {
96
+ vi.mocked(useIncrementCounter).mockReturnValue({
97
+ ...defaultHookReturn,
98
+ error: "Transaction failed: insufficient funds",
99
+ });
100
+
101
+ render(<ConfiguredCounter counterAddress={FIXTURE_ADDRESS} />);
102
+ expect(
103
+ screen.getByText("Transaction failed: insufficient funds"),
104
+ ).toBeInTheDocument();
105
+ });
106
+
107
+ it("links to explorer with counter address", () => {
108
+ render(<ConfiguredCounter counterAddress={FIXTURE_ADDRESS} />);
109
+ const link = screen.getByRole("link");
110
+ expect(link).toHaveAttribute(
111
+ "href",
112
+ `https://testnet.midenscan.com/account/${FIXTURE_ADDRESS}`,
113
+ );
114
+ expect(link).toHaveAttribute("target", "_blank");
115
+ });
116
+ });
@@ -1,114 +1,44 @@
1
1
  import { render, screen } from "@testing-library/react";
2
- import userEvent from "@testing-library/user-event";
3
2
  import { vi, describe, it, expect, beforeEach } from "vitest";
4
3
 
5
- vi.mock("@/hooks/useIncrementCounter", () => ({
6
- useIncrementCounter: vi.fn(),
4
+ vi.mock("@/components/ConfiguredCounter", () => ({
5
+ ConfiguredCounter: ({ counterAddress }: { counterAddress: string }) => (
6
+ <div data-testid="configured-counter">{counterAddress}</div>
7
+ ),
7
8
  }));
8
9
 
9
- import { useIncrementCounter } from "@/hooks/useIncrementCounter";
10
- import { Counter } from "../Counter";
10
+ vi.mock("@/config", async () => {
11
+ const actual = await vi.importActual<typeof import("@/config")>("@/config");
12
+ return { ...actual };
13
+ });
11
14
 
12
- const defaultHookReturn = {
13
- increment: vi.fn(),
14
- count: 42,
15
- isSubmitting: false,
16
- isWaiting: false,
17
- error: null,
18
- walletConnected: true,
19
- explorerUrl: "https://testnet.midenscan.com/account/mtst1test",
20
- };
15
+ import { Counter } from "../Counter";
16
+ import * as config from "@/config";
21
17
 
22
- describe("Counter", () => {
18
+ describe("Counter gate", () => {
23
19
  beforeEach(() => {
24
20
  vi.clearAllMocks();
25
- vi.mocked(useIncrementCounter).mockReturnValue(defaultHookReturn);
26
21
  });
27
22
 
28
- it("displays the current count on the button", () => {
23
+ it("renders ConfiguredCounter when COUNTER_ADDRESS is set", () => {
29
24
  render(<Counter />);
30
- expect(
31
- screen.getByRole("button", { name: "count is 42" }),
32
- ).toBeInTheDocument();
25
+ const configured = screen.getByTestId("configured-counter");
26
+ expect(configured).toBeInTheDocument();
27
+ expect(configured).toHaveTextContent(config.COUNTER_ADDRESS!);
33
28
  });
34
29
 
35
- it("calls increment on button click", async () => {
36
- const mockIncrement = vi.fn();
37
- vi.mocked(useIncrementCounter).mockReturnValue({
38
- ...defaultHookReturn,
39
- increment: mockIncrement,
40
- });
41
-
42
- render(<Counter />);
43
- const user = userEvent.setup();
44
- await user.click(screen.getByRole("button", { name: "count is 42" }));
45
- expect(mockIncrement).toHaveBeenCalledOnce();
46
- });
47
-
48
- it("shows submitting state", () => {
49
- vi.mocked(useIncrementCounter).mockReturnValue({
50
- ...defaultHookReturn,
51
- isSubmitting: true,
52
- });
53
-
54
- render(<Counter />);
55
- const button = screen.getByRole("button", { name: "Submitting..." });
56
- expect(button).toBeDisabled();
57
- });
58
-
59
- it("shows waiting for network state", () => {
60
- vi.mocked(useIncrementCounter).mockReturnValue({
61
- ...defaultHookReturn,
62
- isWaiting: true,
63
- });
64
-
65
- render(<Counter />);
66
- const button = screen.getByRole("button", {
67
- name: "Waiting for network...",
68
- });
69
- expect(button).toBeDisabled();
70
- });
71
-
72
- it("disables button when wallet not connected", () => {
73
- vi.mocked(useIncrementCounter).mockReturnValue({
74
- ...defaultHookReturn,
75
- walletConnected: false,
76
- });
77
-
78
- render(<Counter />);
79
- expect(screen.getByRole("button")).toBeDisabled();
80
- });
81
-
82
- it("disables button when count is loading (null)", () => {
83
- vi.mocked(useIncrementCounter).mockReturnValue({
84
- ...defaultHookReturn,
85
- count: null,
86
- });
87
-
88
- render(<Counter />);
89
- const button = screen.getByRole("button", { name: "count is ..." });
90
- expect(button).toBeDisabled();
91
- });
92
-
93
- it("displays error message", () => {
94
- vi.mocked(useIncrementCounter).mockReturnValue({
95
- ...defaultHookReturn,
96
- error: "Transaction failed: insufficient funds",
97
- });
30
+ it("shows not-configured message when COUNTER_ADDRESS is null", () => {
31
+ // The env-wired resolver in `config.ts` returns `null` when
32
+ // `VITE_MIDEN_COUNTER_ADDRESS=""` (explicit empty string). Simulate that
33
+ // by overriding the module's exported value directly.
34
+ vi.spyOn(config, "COUNTER_ADDRESS", "get").mockReturnValue(null);
98
35
 
99
36
  render(<Counter />);
100
37
  expect(
101
- screen.getByText("Transaction failed: insufficient funds"),
38
+ screen.getByText(/counter address not configured/i),
102
39
  ).toBeInTheDocument();
103
- });
104
-
105
- it("links to explorer with counter address", () => {
106
- render(<Counter />);
107
- const link = screen.getByRole("link");
108
- expect(link).toHaveAttribute(
109
- "href",
110
- "https://testnet.midenscan.com/account/mtst1test",
111
- );
112
- expect(link).toHaveAttribute("target", "_blank");
40
+ expect(
41
+ screen.queryByTestId("configured-counter"),
42
+ ).not.toBeInTheDocument();
113
43
  });
114
44
  });
@@ -1,15 +1,35 @@
1
- // Network counter account deployed on Miden testnet
2
- export const COUNTER_ADDRESS = "mtst1aru8adnrqspgcsr3drk2n990lyc070ll";
1
+ // Network counter account deployed on Miden testnet.
2
+ //
3
+ // Resolution rules for `COUNTER_ADDRESS`:
4
+ // - `VITE_MIDEN_COUNTER_ADDRESS` unset (or omitted) → use the live default
5
+ // deployment (the testnet counter the template ships with).
6
+ // - `VITE_MIDEN_COUNTER_ADDRESS=""` (explicit empty string) → unconfigured,
7
+ // `<Counter>` renders the "address not configured" card.
8
+ // - Any other string → that string is used verbatim (e.g. your own deploy).
9
+ const DEFAULT_COUNTER_ADDRESS = "mtst1aqmx7qv6h3y92sqsmunh8uht4ujmfy4j";
10
+ const configuredCounterAddress: string | undefined =
11
+ import.meta.env.VITE_MIDEN_COUNTER_ADDRESS;
12
+
13
+ export const COUNTER_ADDRESS: string | null =
14
+ configuredCounterAddress === ""
15
+ ? null
16
+ : (configuredCounterAddress ?? DEFAULT_COUNTER_ADDRESS);
3
17
 
4
18
  // StorageMap slot name for the counter
5
19
  export const COUNTER_SLOT_NAME =
6
- "miden::component::miden_counter_account::count_map";
20
+ "miden_counter_account::counter_contract::count_map";
7
21
 
8
22
  // Block explorer base URL
9
23
  export const EXPLORER_BASE_URL = "https://testnet.midenscan.com";
10
24
 
11
- // Delay (ms) to wait for the network to process a note before re-syncing
12
- export const NETWORK_SYNC_DELAY_MS = 10_000;
25
+ // Poll interval (ms) while waiting for the network operator to consume an
26
+ // increment note and update the counter's on-chain state.
27
+ export const NETWORK_POLL_INTERVAL_MS = 2_500;
28
+
29
+ // Hard cap (ms) on how long to poll for the post-increment state change before
30
+ // giving up and showing whatever value the counter currently has. Covers
31
+ // ~3 block cycles at testnet's ~3s block time with margin.
32
+ export const NETWORK_POLL_TIMEOUT_MS = 30_000;
13
33
 
14
34
  // Application display name (used by wallet adapter)
15
35
  export const APP_NAME = "Miden Template";
@@ -18,4 +38,4 @@ export const APP_NAME = "Miden Template";
18
38
  export const MIDEN_RPC_URL =
19
39
  import.meta.env.VITE_MIDEN_RPC_URL ?? "testnet";
20
40
  export const MIDEN_PROVER =
21
- (import.meta.env.VITE_MIDEN_PROVER as "testnet" | "local") ?? "testnet";
41
+ (import.meta.env.VITE_MIDEN_PROVER as "devnet" | "testnet" | "local") ?? "testnet";