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
@@ -1,6 +1,6 @@
1
1
  # Miden Frontend Template
2
2
 
3
- Minimal Vite + React + TypeScript template for building Miden frontends. Includes a network counter demo that publishes an increment note via the MidenFi wallet adapter.
3
+ Minimal Vite + React + TypeScript template for building Miden frontends. Includes a live Miden testnet network counter demo that publishes an increment note via the MidenFi wallet adapter and lets the network operator auto-execute it against the counter.
4
4
 
5
5
  ## Getting Started
6
6
 
@@ -9,7 +9,7 @@ yarn install
9
9
  yarn dev
10
10
  ```
11
11
 
12
- Open [http://localhost:5173](http://localhost:5173). Connect your MidenFi wallet and click the counter button to publish an increment note.
12
+ Open [http://localhost:5173](http://localhost:5173). The app connects to Miden testnet out of the box and renders the current counter value. Install the [MidenFi wallet extension](https://chromewebstore.google.com/detail/midenfi), connect, and click the counter to submit an increment.
13
13
 
14
14
  ## Project Structure
15
15
 
@@ -20,42 +20,108 @@ src/
20
20
  ├── config.ts # Constants (counter address, explorer URL, SDK config)
21
21
  ├── components/
22
22
  │ ├── AppContent.tsx # Page layout, logos, wallet button
23
- └── Counter.tsx # Counter UI
23
+ ├── Counter.tsx # Counter UI (configured / unconfigured)
24
+ │ └── ConfiguredCounter.tsx # Counter UI when address is set
24
25
  ├── hooks/
25
- │ └── useIncrementCounter.ts # Note construction, wallet submission, re-sync
26
+ │ └── useIncrementCounter.ts # Note construction, wallet submission, bounded poll
26
27
  └── lib/
27
28
  └── miden.ts # Shared Miden utilities
29
+
30
+ public/packages/
31
+ ├── counter_account.masp # Compiled counter contract (MASP format v4)
32
+ └── increment_note.masp # Compiled increment note script
28
33
  ```
29
34
 
30
35
  ## Network Counter Demo
31
36
 
32
- The template demonstrates the network note pattern on Miden testnet:
37
+ The template demonstrates the Miden network-note pattern on testnet:
38
+
39
+ 1. A **counter account** is deployed as a network account (`AccountStorageMode::Network`) on testnet. This template ships with a live deployment at [`mtst1aqmx7qv6h3y92sqsmunh8uht4ujmfy4j`](https://testnet.midenscan.com/account/mtst1aqmx7qv6h3y92sqsmunh8uht4ujmfy4j).
40
+ 2. On button click, the frontend constructs a **public note** targeting the counter and submits it through the MidenFi wallet (the wallet signs and posts the transaction, not the in-browser client).
41
+ 3. The **network operator** picks up the note (tag + `NoteAttachment::newNetworkAccountTarget`) and executes it against the counter account, incrementing the on-chain count.
42
+ 4. The frontend polls `client.getAccount(counterAddress)` and re-reads the `StorageMap`; once the value changes it updates the UI. If the network is slow, polling falls back to a 30 s timeout.
43
+
44
+ Pre-compiled `.masp` packages built with `cargo-miden 0.8.1` (matching `@miden-sdk/miden-sdk@0.14.x`) live in `public/packages/`.
45
+
46
+ ### Pointing at your own counter
47
+
48
+ The counter address is resolved at runtime via the `VITE_MIDEN_COUNTER_ADDRESS` environment variable (`src/config.ts`):
33
49
 
34
- 1. A **counter account** is deployed as a network account (`AccountStorageMode::Network`) at [`mtst1aru8adnrqspgcsr3drk2n990lyc070ll`](https://testnet.midenscan.com/account/mtst1aru8adnrqspgcsr3drk2n990lyc070ll)
35
- 2. On button click, the frontend constructs a **public note** targeting the counter and submits it through the wallet adapter
36
- 3. The **network operator** picks up the note and executes it against the counter account, incrementing the on-chain count
37
- 4. The frontend re-syncs and reads the updated count from the counter's `StorageMap`
50
+ | `VITE_MIDEN_COUNTER_ADDRESS` value | Effect |
51
+ |---|---|
52
+ | unset / commented out (default) | Use the live testnet counter shipped with the template (`mtst1aqmx7qv6h3y92sqsmunh8uht4ujmfy4j`). |
53
+ | empty string (`VITE_MIDEN_COUNTER_ADDRESS=`) | Unconfigured — `<Counter>` renders the "address not configured" card and makes no network calls. |
54
+ | any bech32 string (`mtst1...`) | Uses your own deployment. |
38
55
 
39
- Pre-compiled `.masp` packages for the counter account and increment note are in `public/packages/`.
56
+ The slot-name constant is fixed in `src/config.ts` and must match the counter contract's storage map name.
57
+
58
+ To redeploy (e.g. after modifying contract sources):
59
+
60
+ 1. In the [project-template](https://github.com/0xMiden/project-template) repo on the `migrate-clien-v014` branch, run the deployment binary:
61
+ ```bash
62
+ cargo install cargo-miden --version 0.8.1
63
+ cargo run -p integration --release --bin increment_count
64
+ ```
65
+ The binary builds `contracts/counter-account` + `contracts/increment-note`, creates the counter with `AccountStorageMode::Network`, and prints the bech32 address.
66
+ 2. Copy the freshly built artifacts into this template:
67
+ ```bash
68
+ cp contracts/counter-account/target/miden/release/counter_account.masp \
69
+ <frontend-template>/public/packages/
70
+ cp contracts/increment-note/target/miden/release/increment_note.masp \
71
+ <frontend-template>/public/packages/
72
+ ```
73
+ 3. Set `VITE_MIDEN_COUNTER_ADDRESS=<your bech32 address>` in `.env` (or your shell environment) — no source edit required.
74
+ 4. Verify with `.claude/hooks/check-artifacts.sh` (checks MASP format version).
40
75
 
41
76
  ## Key Dependencies
42
77
 
43
- | Package | Purpose |
44
- |---------|---------|
45
- | `@miden-sdk/react` | React hooks for Miden (useAccount, useSyncState, useImportAccount, etc.) |
46
- | `@miden-sdk/miden-sdk` | Core SDK types (Note, NoteScript, AccountId, Word, etc.) |
47
- | `@miden-sdk/vite-plugin` | Vite plugin handling WASM loading, top-level await, and COOP/COEP |
48
- | `@miden-sdk/miden-wallet-adapter` | MidenFi wallet adapter for transaction submission |
78
+ | Package | Version pin | Purpose |
79
+ |---------|-------------|---------|
80
+ | `@miden-sdk/react` | `0.14.x` | React hooks for Miden (useAccount, useSyncState, useMiden, useMidenClient, useTransaction, …) |
81
+ | `@miden-sdk/miden-sdk` | `0.14.x` | Core SDK types (Note, NoteScript, AccountId, Word, Felt, …) |
82
+ | `@miden-sdk/vite-plugin` | `0.14.x` | Vite plugin that handles WASM loading, top-level await, and COOP/COEP |
83
+ | `@miden-sdk/miden-wallet-adapter-react` | `0.14.x` | MidenFi wallet adapter React context + hooks |
84
+ | `@miden-sdk/miden-wallet-adapter-base` | `0.14.x` | `Transaction.createCustomTransaction` helper used by the increment flow |
49
85
 
50
86
  ## Configuration
51
87
 
52
88
  SDK settings can be overridden via environment variables (see `.env.example`):
53
89
 
54
90
  ```bash
55
- VITE_MIDEN_RPC_URL=testnet # "testnet" | "localhost" | custom URL
56
- VITE_MIDEN_PROVER=testnet # "testnet" | "local"
91
+ VITE_MIDEN_RPC_URL=testnet # "devnet" | "testnet" | "localhost" | custom URL
92
+ VITE_MIDEN_PROVER=testnet # "devnet" | "testnet" | "local" | custom URL
57
93
  ```
58
94
 
95
+ ## Verification
96
+
97
+ Automated gates that must all stay green:
98
+
99
+ ```bash
100
+ npx tsc -b --noEmit # type check
101
+ npx vitest --run # 36 unit tests (components, hook, patterns)
102
+ npx vite build # production build (emits dist/)
103
+ npx eslint . # lint
104
+ ```
105
+
106
+ The PostToolUse hook runs typecheck + affected tests after every edit. The Stop hook runs the full suite when a task completes.
107
+
108
+ Browser-level verification (render correctness, no console errors, wallet popup, E2E increment) can be done with either:
109
+ - **Playwright MCP** for headless render / console checks
110
+ - **Claude in Chrome** (via the `/chrome` command) to exercise the real MidenFi extension
111
+
112
+ ## Known Temporary Workarounds
113
+
114
+ One active workaround remains after the 0.14.4 upgrade, covering an upstream feature gap. The inline comment in `src/hooks/useIncrementCounter.ts` describes the removal steps.
115
+
116
+ ### Fixed-interval network poll — waiting for Network-mode account updates ([miden-client#2111](https://github.com/0xMiden/miden-client/issues/2111))
117
+
118
+ After `wallet.requestTransaction` returns, `src/hooks/useIncrementCounter.ts` bounded-polls the counter's storage map until the value changes or a 30 s timeout elapses. The React SDK's `useWaitForCommit` only watches *locally-submitted* transactions — our increment is wallet-submitted and consumed externally by the network operator, so it never reaches the local client's transaction log. [`#2111`](https://github.com/0xMiden/miden-client/issues/2111) tracks a React-SDK subscription primitive for account-state updates driven by external consumers (scoped narrowly from the broader event-system discussion in [`#467`](https://github.com/0xMiden/miden-client/issues/467)).
119
+
120
+ **After #2111 lands a subscription primitive:**
121
+ 1. Replace the `while` poll loop in `useIncrementCounter.ts::increment` with the new subscription / waitFor API.
122
+ 2. Remove `NETWORK_POLL_INTERVAL_MS` + `NETWORK_POLL_TIMEOUT_MS` from `src/config.ts` if no other consumer depends on them.
123
+ 3. Remove the `#2111` TODO block.
124
+
59
125
  ## AI Developer Experience
60
126
 
61
- This template ships with `.claude/` skills for AI coding tools. Skills cover React SDK patterns, frontend pitfalls, Vite + WASM setup, signer integration, and Miden architecture. See `CLAUDE.md` for the full developer guide.
127
+ This template ships with `.claude/` skills for AI coding tools. Skills cover React SDK patterns, frontend pitfalls, Vite + WASM setup, signer integration, testing patterns, and Miden architecture. See `CLAUDE.md` for the full developer guide.
@@ -13,15 +13,16 @@
13
13
  "test:coverage": "vitest --run --coverage"
14
14
  },
15
15
  "dependencies": {
16
- "@miden-sdk/miden-wallet-adapter": "0.13.5",
17
- "@miden-sdk/miden-sdk": "0.13.2",
18
- "@miden-sdk/react": "0.13.3",
16
+ "@miden-sdk/miden-wallet-adapter-base": "0.14.3",
17
+ "@miden-sdk/miden-wallet-adapter-react": "0.14.3",
18
+ "@miden-sdk/miden-sdk": "0.14.4",
19
+ "@miden-sdk/react": "0.14.4",
19
20
  "react": "^19.1.1",
20
21
  "react-dom": "^19.1.1"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@eslint/js": "^9.36.0",
24
- "@miden-sdk/vite-plugin": "0.13.4",
25
+ "@miden-sdk/vite-plugin": "0.14.4",
25
26
  "@testing-library/dom": "^10.4.1",
26
27
  "@testing-library/jest-dom": "^6.9.1",
27
28
  "@testing-library/react": "^16.3.2",
@@ -1,12 +1,13 @@
1
1
  /**
2
- * Realistic account fixtures for Miden frontend tests.
3
- * Uses bech32 IDs and BigInt amounts matching real SDK shapes.
2
+ * Test fixtures for Miden frontend tests.
3
+ * Uses hex IDs (network-agnostic) and BigInt amounts matching real SDK shapes.
4
+ * These are mock values — never parsed by the real SDK at runtime.
4
5
  */
5
6
 
6
- export const WALLET_ID_1 = "mtst1qy35qfqdvpjx2e5zf9hkp4vr";
7
- export const WALLET_ID_2 = "mtst1qa7k9qjf8dp4x2e5zf9hkp5vr";
8
- export const FAUCET_ID = "mtst1qx9y8zjf2dp4x2e5zf9hkp3vr";
9
- export const COUNTER_ID = "mtst1aru8adnrqspgcsr3drk2n990lyc070ll";
7
+ export const WALLET_ID_1 = "0x0a00000000000001";
8
+ export const WALLET_ID_2 = "0x0a00000000000002";
9
+ export const FAUCET_ID = "0x0a00000000000003";
10
+ export const COUNTER_ID = "0x0a00000000000004";
10
11
 
11
12
  export const MOCK_WALLET_HEADER = {
12
13
  id: WALLET_ID_1,
@@ -52,6 +53,16 @@ export const MOCK_ASSET_METADATA = {
52
53
  decimals: 8,
53
54
  };
54
55
 
56
+ // TransactionResult shape — matches @miden-sdk/react types for useMint / useConsume /
57
+ // useSwap / useMultiSend / useTransaction result payloads.
55
58
  export const MOCK_TRANSACTION_RESULT = {
56
59
  transactionId: "0xabc123def456789012345678901234567890123456789012345678901234abcd",
57
60
  };
61
+
62
+ // SendResult shape — distinct from TransactionResult. @miden-sdk/react's useSend
63
+ // returns { txId, note }, not { transactionId }. Keep these fixtures separate so
64
+ // mocks for useSend don't bleed into mocks for TransactionResult-typed hooks.
65
+ export const MOCK_SEND_RESULT = {
66
+ txId: "0xabc123def456789012345678901234567890123456789012345678901234abcd",
67
+ note: null,
68
+ };
@@ -11,6 +11,7 @@ export {
11
11
  MOCK_ACCOUNT,
12
12
  MOCK_ASSET_METADATA,
13
13
  MOCK_TRANSACTION_RESULT,
14
+ MOCK_SEND_RESULT,
14
15
  } from "./accounts";
15
16
 
16
17
  export {
@@ -23,6 +23,7 @@ import {
23
23
  MOCK_CONSUMABLE_NOTE_RECORD,
24
24
  MOCK_NOTE_SUMMARY,
25
25
  MOCK_TRANSACTION_RESULT,
26
+ MOCK_SEND_RESULT,
26
27
  FAUCET_ID,
27
28
  } from "../fixtures";
28
29
 
@@ -95,6 +96,8 @@ export const useNoteStream = vi.fn(() => ({
95
96
  // Mutation hooks
96
97
  // ---------------------------------------------------------------------------
97
98
 
99
+ // Mutation hooks that resolve to TransactionResult { transactionId } —
100
+ // useMint / useConsume / useSwap / useMultiSend / useTransaction.
98
101
  function createMutationMock(mutateKey: string) {
99
102
  return vi.fn(() => ({
100
103
  [mutateKey]: vi.fn(async () => MOCK_TRANSACTION_RESULT),
@@ -106,6 +109,19 @@ function createMutationMock(mutateKey: string) {
106
109
  }));
107
110
  }
108
111
 
112
+ // useSend resolves to SendResult { txId, note } — not TransactionResult.
113
+ // Keep this separate so tests that call `send()` get the correct shape.
114
+ function createSendMock() {
115
+ return vi.fn(() => ({
116
+ send: vi.fn(async () => MOCK_SEND_RESULT),
117
+ result: null,
118
+ isLoading: false,
119
+ stage: "idle" as const,
120
+ error: null,
121
+ reset: vi.fn(),
122
+ }));
123
+ }
124
+
109
125
  export const useCreateWallet = vi.fn(() => ({
110
126
  createWallet: vi.fn(async () => MOCK_ACCOUNT),
111
127
  wallet: null,
@@ -122,7 +138,7 @@ export const useCreateFaucet = vi.fn(() => ({
122
138
  reset: vi.fn(),
123
139
  }));
124
140
 
125
- export const useSend = createMutationMock("send");
141
+ export const useSend = createSendMock();
126
142
  export const useMultiSend = createMutationMock("sendMany");
127
143
  export const useMint = createMutationMock("mint");
128
144
  export const useConsume = createMutationMock("consume");
@@ -181,6 +197,7 @@ export const useMiden = vi.fn(() => ({
181
197
  runExclusive: vi.fn(async <T>(fn: () => Promise<T>) => fn()),
182
198
  prover: null,
183
199
  signerAccountId: null,
200
+ signerConnected: null,
184
201
  }));
185
202
 
186
203
  export const useMidenClient = vi.fn(() => ({}));
@@ -85,7 +85,7 @@ describe("Mutation Hook Pattern", () => {
85
85
  it("shows success message after transaction completes", () => {
86
86
  vi.mocked(useSend).mockReturnValue({
87
87
  send: vi.fn(),
88
- result: { transactionId: "0xabc123" },
88
+ result: { txId: "0xabc123", note: null },
89
89
  isLoading: false,
90
90
  stage: "complete" as const,
91
91
  error: null,
@@ -121,7 +121,7 @@ describe("Mutation Hook Pattern", () => {
121
121
 
122
122
  // Test the actual send call — verify correct arguments
123
123
  it("calls send with correct arguments on click", async () => {
124
- const mockSend = vi.fn(async () => ({ transactionId: "0xtx" }));
124
+ const mockSend = vi.fn(async () => ({ txId: "0xtx", note: null }));
125
125
  vi.mocked(useSend).mockReturnValue({
126
126
  send: mockSend,
127
127
  result: null,
@@ -50,6 +50,7 @@ describe("Provider Setup Pattern", () => {
50
50
  runExclusive: vi.fn(),
51
51
  prover: null,
52
52
  signerAccountId: null,
53
+ signerConnected: null,
53
54
  });
54
55
 
55
56
  render(<StatusIndicator />);
@@ -67,6 +68,7 @@ describe("Provider Setup Pattern", () => {
67
68
  runExclusive: vi.fn(),
68
69
  prover: null,
69
70
  signerAccountId: null,
71
+ signerConnected: null,
70
72
  });
71
73
 
72
74
  render(<StatusIndicator />);
@@ -1,11 +1,41 @@
1
1
  import { useMiden, useSyncState } from "@miden-sdk/react";
2
- import { WalletMultiButton } from "@miden-sdk/miden-wallet-adapter";
2
+ import { useMidenFiWallet } from "@miden-sdk/miden-wallet-adapter-react";
3
+ import { WalletReadyState } from "@miden-sdk/miden-wallet-adapter-base";
3
4
  import reactLogo from "@/assets/react.svg";
4
5
  import midenLogo from "@/assets/miden.svg";
5
6
  import viteLogo from "/vite.svg";
6
7
  import { Counter } from "@/components/Counter";
7
8
  import "./AppContent.css";
8
9
 
10
+ function WalletButton() {
11
+ // Use the MidenFi-specific hook (not the generic `useSigner()`) so we can
12
+ // gate on `wallet.readyState`. `useSigner().connect()` calls through to the
13
+ // same provider, but at the moment the user clicks the button the adapter
14
+ // may not yet have detected `window.midenWallet` — detection is polled, see
15
+ // `scopePollingDetectionStrategy` in @miden-sdk/miden-wallet-adapter-base.
16
+ // When readyState is NotDetected, MidenFiSignerProvider falls back to
17
+ // `window.open(adapter.url, "_blank")` — the Chrome Web Store URL — which
18
+ // on some platforms redirects to the Play Store. Disabling the button
19
+ // until the extension is detected prevents the fallback from firing.
20
+ const { wallet, connected, connecting, connect, disconnect } =
21
+ useMidenFiWallet();
22
+ const readyState = wallet?.readyState;
23
+ const walletReady =
24
+ readyState === WalletReadyState.Installed ||
25
+ readyState === WalletReadyState.Loadable;
26
+
27
+ if (!walletReady) {
28
+ return <button disabled>Install MidenFi Wallet</button>;
29
+ }
30
+ if (connected) {
31
+ return <button onClick={disconnect}>Disconnect Wallet</button>;
32
+ }
33
+ if (connecting) {
34
+ return <button disabled>Connecting...</button>;
35
+ }
36
+ return <button onClick={connect}>Connect Wallet</button>;
37
+ }
38
+
9
39
  export function AppContent() {
10
40
  const { isReady, isInitializing, error } = useMiden();
11
41
  const { syncHeight } = useSyncState();
@@ -32,13 +62,13 @@ export function AppContent() {
32
62
  <a href="https://react.dev" target="_blank" rel="noreferrer">
33
63
  <img src={reactLogo} className="logo react" alt="React logo" />
34
64
  </a>
35
- <a href="https://docs.miden.io" target="_blank" rel="noreferrer">
65
+ <a href="https://docs.miden.xyz" target="_blank" rel="noreferrer">
36
66
  <img src={midenLogo} className="logo miden" alt="Miden logo" />
37
67
  </a>
38
68
  </div>
39
69
  <h1>Vite + React + Miden</h1>
40
70
  <div className="wallet-section">
41
- <WalletMultiButton />
71
+ <WalletButton />
42
72
  </div>
43
73
  <Counter />
44
74
  <p className="read-the-docs">
@@ -1,8 +1,11 @@
1
1
  import { useIncrementCounter } from "@/hooks/useIncrementCounter";
2
- import { COUNTER_ADDRESS } from "@/config";
3
2
  import "./Counter.css";
4
3
 
5
- export function Counter() {
4
+ export function ConfiguredCounter({
5
+ counterAddress,
6
+ }: {
7
+ counterAddress: string;
8
+ }) {
6
9
  const {
7
10
  increment,
8
11
  count,
@@ -11,7 +14,7 @@ export function Counter() {
11
14
  error,
12
15
  walletConnected,
13
16
  explorerUrl,
14
- } = useIncrementCounter(COUNTER_ADDRESS);
17
+ } = useIncrementCounter(counterAddress);
15
18
 
16
19
  const busy = isSubmitting || isWaiting;
17
20
  const buttonLabel = isSubmitting
@@ -36,7 +39,7 @@ export function Counter() {
36
39
  rel="noreferrer"
37
40
  className="account-id"
38
41
  >
39
- Counter: {COUNTER_ADDRESS}
42
+ Counter: {counterAddress}
40
43
  </a>
41
44
  </p>
42
45
  {error && <p className="error">{error}</p>}
@@ -1,45 +1,16 @@
1
- import { useIncrementCounter } from "@/hooks/useIncrementCounter";
2
1
  import { COUNTER_ADDRESS } from "@/config";
3
- import "./Counter.css";
2
+ import { ConfiguredCounter } from "./ConfiguredCounter";
4
3
 
5
4
  export function Counter() {
6
- const {
7
- increment,
8
- count,
9
- isSubmitting,
10
- isWaiting,
11
- error,
12
- walletConnected,
13
- explorerUrl,
14
- } = useIncrementCounter(COUNTER_ADDRESS);
15
-
16
- const busy = isSubmitting || isWaiting;
17
- const buttonLabel = isSubmitting
18
- ? "Submitting..."
19
- : isWaiting
20
- ? "Waiting for network..."
21
- : `count is ${count ?? "..."}`;
22
-
23
- return (
24
- <div className="card">
25
- <button
26
- className="counter-button"
27
- onClick={increment}
28
- disabled={busy || count === null || !walletConnected}
29
- >
30
- {buttonLabel}
31
- </button>
32
- <p>
33
- <a
34
- href={explorerUrl}
35
- target="_blank"
36
- rel="noreferrer"
37
- className="account-id"
38
- >
39
- Counter: {COUNTER_ADDRESS}
40
- </a>
41
- </p>
42
- {error && <p className="error">{error}</p>}
43
- </div>
44
- );
5
+ if (!COUNTER_ADDRESS) {
6
+ return (
7
+ <div className="card">
8
+ <p>
9
+ Counter address not configured — see README for deployment
10
+ instructions.
11
+ </p>
12
+ </div>
13
+ );
14
+ }
15
+ return <ConfiguredCounter counterAddress={COUNTER_ADDRESS} />;
45
16
  }