create-miden-app 1.0.4 → 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.
- package/cli.js +6 -6
- package/package.json +1 -1
- package/template/.claude/commands/review-security.md +67 -0
- package/template/.claude/hooks/check-artifacts.sh +45 -0
- package/template/.claude/hooks/run-affected-tests.sh +31 -0
- package/template/.claude/hooks/typecheck.sh +27 -0
- package/template/.claude/settings.json +29 -0
- package/template/.claude/settings.local.json +24 -0
- package/template/.claude/skills/frontend-pitfalls/SKILL.md +186 -0
- package/template/.claude/skills/frontend-source-guide/SKILL.md +163 -0
- package/template/.claude/skills/miden-concepts/SKILL.md +110 -0
- package/template/.claude/skills/react-sdk-patterns/SKILL.md +562 -0
- package/template/.claude/skills/signer-integration/SKILL.md +177 -0
- package/template/.claude/skills/testing-patterns/SKILL.md +338 -0
- package/template/.claude/skills/vite-wasm-setup/SKILL.md +134 -0
- package/template/.claude/skills/web-client-usage/SKILL.md +454 -0
- package/template/.env.example +18 -0
- package/template/.mcp.json +9 -0
- package/template/CLAUDE.md +243 -0
- package/template/README.md +119 -14
- package/template/index.html +1 -1
- package/template/package.json +18 -8
- package/template/public/packages/counter_account.masp +0 -0
- package/template/public/packages/increment_note.masp +0 -0
- package/template/src/App.tsx +6 -59
- package/template/src/__tests__/fixtures/accounts.ts +68 -0
- package/template/src/__tests__/fixtures/index.ts +22 -0
- package/template/src/__tests__/fixtures/notes.ts +33 -0
- package/template/src/__tests__/mocks/miden-sdk-react.ts +261 -0
- package/template/src/__tests__/patterns/README.md +44 -0
- package/template/src/__tests__/patterns/mutation-hook.test.tsx +146 -0
- package/template/src/__tests__/patterns/provider-setup.test.tsx +77 -0
- package/template/src/__tests__/patterns/query-hook.test.tsx +143 -0
- package/template/src/{App.css → components/AppContent.css} +9 -9
- package/template/src/components/AppContent.tsx +80 -0
- package/template/src/components/ConfiguredCounter.tsx +48 -0
- package/template/src/components/Counter.css +27 -0
- package/template/src/components/Counter.tsx +16 -0
- package/template/src/components/__tests__/AppContent.test.tsx +274 -0
- package/template/src/components/__tests__/ConfiguredCounter.test.tsx +116 -0
- package/template/src/components/__tests__/Counter.test.tsx +44 -0
- package/template/src/config.ts +41 -0
- package/template/src/hooks/__tests__/useIncrementCounter.test.tsx +257 -0
- package/template/src/hooks/useIncrementCounter.ts +195 -0
- package/template/src/index.css +7 -0
- package/template/src/lib/miden.ts +9 -0
- package/template/src/main.tsx +6 -6
- package/template/src/providers.tsx +27 -0
- package/template/src/vite-env.d.ts +1 -0
- package/template/tsconfig.app.json +8 -4
- package/template/tsconfig.node.json +1 -3
- package/template/vite.config.ts +5 -17
- package/template/vitest.config.ts +25 -0
- package/template/vitest.setup.ts +1 -0
- package/template/yarn.lock +1687 -815
- package/template/src/miden/lib/demo.ts +0 -106
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Miden Frontend App
|
|
2
|
+
|
|
3
|
+
React 19 + TypeScript + Vite frontend for the Miden blockchain.
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
- `src/` — React application source
|
|
8
|
+
- `src/components/` — UI components (Counter, AppContent)
|
|
9
|
+
- `src/hooks/` — Custom hooks (useIncrementCounter)
|
|
10
|
+
- `src/lib/` — Shared utilities
|
|
11
|
+
- `src/__tests__/` — Test infrastructure (mocks, fixtures, patterns)
|
|
12
|
+
- `src/components/__tests__/` — Component tests
|
|
13
|
+
- `vite.config.ts` — Vite config with midenVitePlugin() from @miden-sdk/vite-plugin
|
|
14
|
+
- `vitest.config.ts` — Vitest test runner config
|
|
15
|
+
- `package.json` — Dependencies: @miden-sdk/react, @miden-sdk/miden-sdk
|
|
16
|
+
|
|
17
|
+
## Build, Dev & Test
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
yarn dev # Start dev server (Vite)
|
|
21
|
+
yarn build # Type check + production build (tsc -b && vite build)
|
|
22
|
+
yarn lint # ESLint
|
|
23
|
+
yarn test # Run all tests once (vitest --run)
|
|
24
|
+
yarn test:watch # Run tests in watch mode (vitest)
|
|
25
|
+
yarn test:coverage # Run tests with coverage report
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Type checking alone:
|
|
29
|
+
```
|
|
30
|
+
npx tsc -b --noEmit
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## SDK Choice: React SDK Hooks First
|
|
34
|
+
|
|
35
|
+
ALWAYS prefer `@miden-sdk/react` hooks over low-level `WasmWebClient` methods.
|
|
36
|
+
Only use the WASM client directly via `useMidenClient()` for operations not covered by hooks.
|
|
37
|
+
|
|
38
|
+
### Setup — this template's actual providers (`src/providers.tsx`)
|
|
39
|
+
```tsx
|
|
40
|
+
import { MidenProvider } from "@miden-sdk/react";
|
|
41
|
+
import { MidenFiSignerProvider } from "@miden-sdk/miden-wallet-adapter-react";
|
|
42
|
+
|
|
43
|
+
<MidenFiSignerProvider
|
|
44
|
+
appName={APP_NAME}
|
|
45
|
+
network={WalletAdapterNetwork.Testnet}
|
|
46
|
+
autoConnect
|
|
47
|
+
>
|
|
48
|
+
<MidenProvider
|
|
49
|
+
config={{ rpcUrl: MIDEN_RPC_URL, prover: MIDEN_PROVER }}
|
|
50
|
+
loadingComponent={<div className="loading">Loading Miden WASM...</div>}
|
|
51
|
+
>
|
|
52
|
+
<App />
|
|
53
|
+
</MidenProvider>
|
|
54
|
+
</MidenFiSignerProvider>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> `MidenFiSignerProvider` must wrap `MidenProvider`. `MidenProvider` reads from `SignerContext` during initialization (to wire its external-keystore client), so the signer context has to exist before `MidenProvider` mounts.
|
|
58
|
+
|
|
59
|
+
### Query Hooks
|
|
60
|
+
Each returns its own result shape plus `isLoading`, `error`, `refetch`:
|
|
61
|
+
```tsx
|
|
62
|
+
const { wallets, faucets } = useAccounts();
|
|
63
|
+
const { account, assets, getBalance } = useAccount(accountId);
|
|
64
|
+
const { notes, consumableNotes } = useNotes();
|
|
65
|
+
const { syncHeight, sync } = useSyncState();
|
|
66
|
+
const { assetMetadata } = useAssetMetadata(faucetId);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Mutation Hooks
|
|
70
|
+
Each returns its own action function plus `isLoading`, `stage`, `error`, `reset`.
|
|
71
|
+
Transaction stages: `idle → executing → proving → submitting → complete`
|
|
72
|
+
```tsx
|
|
73
|
+
const { createWallet } = useCreateWallet();
|
|
74
|
+
const { send, stage } = useSend();
|
|
75
|
+
const { consume } = useConsume();
|
|
76
|
+
const { mint } = useMint();
|
|
77
|
+
const { swap } = useSwap();
|
|
78
|
+
const { execute } = useTransaction(); // arbitrary tx requests
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Token Amounts Are BigInt
|
|
82
|
+
```tsx
|
|
83
|
+
import { formatAssetAmount, parseAssetAmount } from "@miden-sdk/react";
|
|
84
|
+
const display = formatAssetAmount(balance, 8); // bigint → string
|
|
85
|
+
const amount = parseAssetAmount("1.5", 8); // string → bigint
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For exhaustive hook API reference, read `node_modules/@miden-sdk/react/CLAUDE.md` and `node_modules/@miden-sdk/react/README.md`.
|
|
89
|
+
|
|
90
|
+
## TDD Workflow
|
|
91
|
+
|
|
92
|
+
When building features, follow this test-driven cycle:
|
|
93
|
+
|
|
94
|
+
1. **Write a failing test** for the feature/component
|
|
95
|
+
2. **Run tests** — confirm the test fails (red)
|
|
96
|
+
3. **Implement** the minimum code to make the test pass
|
|
97
|
+
4. **Run tests** — confirm all tests pass (green)
|
|
98
|
+
5. **Refactor** if needed, re-run tests
|
|
99
|
+
6. Type checking runs automatically after each edit (PostToolUse hook)
|
|
100
|
+
7. Affected tests run automatically after each edit (PostToolUse hook)
|
|
101
|
+
|
|
102
|
+
### Test file conventions
|
|
103
|
+
- Component tests: `src/components/__tests__/ComponentName.test.tsx`
|
|
104
|
+
- Hook tests: `src/hooks/__tests__/hookName.test.ts`
|
|
105
|
+
- Pattern references: `src/__tests__/patterns/` — copy and adapt these
|
|
106
|
+
|
|
107
|
+
### Writing tests for Miden components
|
|
108
|
+
```tsx
|
|
109
|
+
// 1. Mock the SDK at module level (always required)
|
|
110
|
+
vi.mock("@miden-sdk/react", () => import("@/__tests__/mocks/miden-sdk-react"));
|
|
111
|
+
|
|
112
|
+
// 2. Import hooks to override per-test
|
|
113
|
+
import { useAccounts } from "@miden-sdk/react";
|
|
114
|
+
|
|
115
|
+
// 3. Override in individual tests
|
|
116
|
+
vi.mocked(useAccounts).mockReturnValue({ wallets: [], ... });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
See `testing-patterns` skill for full mock factory reference and fixture data.
|
|
120
|
+
|
|
121
|
+
## Verification Sequence
|
|
122
|
+
|
|
123
|
+
Automated verification runs in layers (each catches different failure classes):
|
|
124
|
+
|
|
125
|
+
1. **TypeScript type check** (auto, per-edit) — catches type errors immediately
|
|
126
|
+
2. **Affected tests** (auto, per-edit) — catches logic regressions from changes
|
|
127
|
+
3. **Full test suite + type check + build** (auto, on each edit via PostToolUse) — catches integration issues
|
|
128
|
+
4. **Browser verification** (Playwright MCP / Claude in Chrome) — catches "compiles but doesn't work" failures
|
|
129
|
+
|
|
130
|
+
### Browser verification (when needed)
|
|
131
|
+
|
|
132
|
+
Two tools are available for browser verification. Use whichever is appropriate:
|
|
133
|
+
|
|
134
|
+
#### Playwright MCP (visual verification, no wallet)
|
|
135
|
+
Configured in `.mcp.json`. Use for checking that the UI renders correctly, no console errors, layout looks right. Cannot interact with the MidenFi wallet extension.
|
|
136
|
+
|
|
137
|
+
1. Start dev server: `yarn dev`
|
|
138
|
+
2. Use Playwright MCP tools to navigate to `http://localhost:5173`
|
|
139
|
+
3. Take a screenshot, check for render errors
|
|
140
|
+
4. Check the browser console for errors
|
|
141
|
+
|
|
142
|
+
#### Claude in Chrome (full verification, with wallet)
|
|
143
|
+
Use for wallet-dependent features. Connects to the user's real browser where MidenFi is installed.
|
|
144
|
+
|
|
145
|
+
1. Start Claude Code with `claude --chrome` (or run `/chrome` in session)
|
|
146
|
+
2. Start dev server: `yarn dev`
|
|
147
|
+
3. Navigate to `http://localhost:5173`
|
|
148
|
+
4. Interact with wallet connect, transaction flows, etc.
|
|
149
|
+
|
|
150
|
+
## Contract Artifact Handoff
|
|
151
|
+
|
|
152
|
+
Frontend loads pre-compiled `.masp` packages from `public/packages/` at runtime.
|
|
153
|
+
|
|
154
|
+
### Artifact location
|
|
155
|
+
```
|
|
156
|
+
public/packages/
|
|
157
|
+
├── counter_account.masp # Counter account component
|
|
158
|
+
└── increment_note.masp # Increment note script
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Building artifacts
|
|
162
|
+
In the contract project (e.g., `project-template/`):
|
|
163
|
+
```bash
|
|
164
|
+
cargo miden build --release
|
|
165
|
+
# Copy .masp files from contracts/*/target/miden/release/ to public/packages/
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Validate artifacts
|
|
169
|
+
```bash
|
|
170
|
+
.claude/hooks/check-artifacts.sh
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Failure recovery
|
|
174
|
+
- **Missing artifacts**: Build contracts with `cargo miden build` or ask the PM to supply the `.masp` files
|
|
175
|
+
- **Stale artifacts**: Rebuild and re-copy after contract changes
|
|
176
|
+
- **Deserialization failure at runtime**: Version mismatch — rebuild contracts with the SDK version matching `@miden-sdk/miden-sdk` in `package.json`
|
|
177
|
+
|
|
178
|
+
## Known Temporary Workarounds
|
|
179
|
+
|
|
180
|
+
One remaining upstream feature gap. The README has full rationale + removal steps; summary below.
|
|
181
|
+
|
|
182
|
+
**Fixed-interval network poll** ([0xMiden/miden-client#2111](https://github.com/0xMiden/miden-client/issues/2111)): `useIncrementCounter.ts::increment` polls the counter's storage map every `NETWORK_POLL_INTERVAL_MS` (2.5 s) until the value changes or `NETWORK_POLL_TIMEOUT_MS` (30 s) elapses. `useWaitForCommit` doesn't apply because the increment is wallet-submitted and consumed externally by the network operator — the local client never sees the tx. #2111 tracks a React-SDK subscription primitive for this case (narrowed from the broader event-system discussion in #467).
|
|
183
|
+
|
|
184
|
+
## Critical Pitfalls
|
|
185
|
+
|
|
186
|
+
**WASM init must complete first**: Always use MidenProvider's `loadingComponent` or check `useMiden().isReady`. Components rendering before WASM init will crash.
|
|
187
|
+
|
|
188
|
+
**Recursive WASM access crashes**: Never call client methods concurrently. Use `runExclusive()` from `useMiden()` for sequential execution. Built-in hooks handle this automatically.
|
|
189
|
+
|
|
190
|
+
**COOP/COEP headers required**: WASM SharedArrayBuffer needs `Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp` in vite.config.ts AND production server.
|
|
191
|
+
|
|
192
|
+
**Token amounts are bigint, not number**: `send({ amount: 1000 })` will fail. Use `amount: 1000n` or `parseAssetAmount("10", 8)`.
|
|
193
|
+
|
|
194
|
+
## PM Workflow
|
|
195
|
+
|
|
196
|
+
For non-developer users building with this template:
|
|
197
|
+
|
|
198
|
+
1. Clone the repository and run `yarn install`
|
|
199
|
+
2. Start Claude Code in the project directory
|
|
200
|
+
3. Describe the app you want to build in natural language
|
|
201
|
+
4. Claude will implement features using TDD — tests are written first, then code
|
|
202
|
+
5. Automated hooks verify correctness at every step
|
|
203
|
+
6. Automated hooks run full tests + build after each code edit
|
|
204
|
+
7. Review the app in the browser: `yarn dev` → open `http://localhost:5173`
|
|
205
|
+
8. If using wallet features, install the MidenFi browser extension to test
|
|
206
|
+
|
|
207
|
+
### Known limitations
|
|
208
|
+
- **Visual correctness**: Automated tests verify structure and behavior, not visual appearance. Review the app in the browser for styling issues.
|
|
209
|
+
- **Wallet extension**: Real wallet interactions require the MidenFi browser extension. Tests mock the wallet adapter.
|
|
210
|
+
- **Network-dependent features**: Some features (syncing, transaction submission) require testnet connectivity.
|
|
211
|
+
|
|
212
|
+
## Miden Skills
|
|
213
|
+
|
|
214
|
+
For Miden-specific guidance, Claude will auto-load these skills when relevant:
|
|
215
|
+
- `react-sdk-patterns` — Complete React SDK hook API reference
|
|
216
|
+
- `testing-patterns` — Test mock factory, fixtures, and TDD conventions
|
|
217
|
+
- `frontend-pitfalls` — All frontend/WASM/browser pitfalls with safe/unsafe examples
|
|
218
|
+
- `miden-concepts` — Miden architecture from a developer perspective
|
|
219
|
+
- `vite-wasm-setup` — Vite + WASM configuration, deployment headers, troubleshooting
|
|
220
|
+
- `signer-integration` — External signer setup (Para, Turnkey, MidenFi)
|
|
221
|
+
|
|
222
|
+
## General Frontend Skills (Recommended)
|
|
223
|
+
|
|
224
|
+
For general React, TypeScript, and design capabilities, install these official skills alongside our Miden-specific ones:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Vercel's React/design skills
|
|
228
|
+
git clone https://github.com/vercel-labs/agent-skills.git
|
|
229
|
+
# Install: react-best-practices, web-design-guidelines, composition-patterns
|
|
230
|
+
|
|
231
|
+
# Anthropic's frontend design skill (Claude Code plugin)
|
|
232
|
+
# See: https://github.com/anthropics/claude-code/tree/main/plugins/frontend-design
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Advanced Development
|
|
236
|
+
|
|
237
|
+
For complex applications beyond basic hook usage (custom signers, direct WasmWebClient access, advanced note flows):
|
|
238
|
+
|
|
239
|
+
1. Clone `miden-client` repo alongside this project (see `frontend-source-guide` skill)
|
|
240
|
+
2. Use Plan Mode first — Claude explores React SDK source + examples before coding
|
|
241
|
+
3. Claude uses sub-agents to explore repos efficiently without filling main context
|
|
242
|
+
|
|
243
|
+
The basic skills cover ~80% of patterns. Source repos provide the remaining 20% for advanced builders.
|
package/template/README.md
CHANGED
|
@@ -1,22 +1,127 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Miden Frontend Template
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
yarn install
|
|
9
|
+
yarn dev
|
|
10
|
+
```
|
|
8
11
|
|
|
9
|
-
The
|
|
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.
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
- **Account Creation**: Creating new wallet accounts (Alice)
|
|
13
|
-
- **Faucet Setup**: Creating a fungible token faucet with custom tokens
|
|
14
|
-
- **Token Minting**: Minting tokens to an account via P2ID notes
|
|
15
|
-
- **Note Consumption**: Consuming notes to receive fungible assets
|
|
16
|
-
- **Token Transfers**: Sending tokens between accounts
|
|
14
|
+
## Project Structure
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
```
|
|
17
|
+
src/
|
|
18
|
+
├── App.tsx # Root component
|
|
19
|
+
├── providers.tsx # MidenProvider + wallet adapter setup
|
|
20
|
+
├── config.ts # Constants (counter address, explorer URL, SDK config)
|
|
21
|
+
├── components/
|
|
22
|
+
│ ├── AppContent.tsx # Page layout, logos, wallet button
|
|
23
|
+
│ ├── Counter.tsx # Counter UI (configured / unconfigured)
|
|
24
|
+
│ └── ConfiguredCounter.tsx # Counter UI when address is set
|
|
25
|
+
├── hooks/
|
|
26
|
+
│ └── useIncrementCounter.ts # Note construction, wallet submission, bounded poll
|
|
27
|
+
└── lib/
|
|
28
|
+
└── miden.ts # Shared Miden utilities
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
public/packages/
|
|
31
|
+
├── counter_account.masp # Compiled counter contract (MASP format v4)
|
|
32
|
+
└── increment_note.masp # Compiled increment note script
|
|
33
|
+
```
|
|
21
34
|
|
|
22
|
-
|
|
35
|
+
## Network Counter Demo
|
|
36
|
+
|
|
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`):
|
|
49
|
+
|
|
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. |
|
|
55
|
+
|
|
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).
|
|
75
|
+
|
|
76
|
+
## Key Dependencies
|
|
77
|
+
|
|
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 |
|
|
85
|
+
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
SDK settings can be overridden via environment variables (see `.env.example`):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
VITE_MIDEN_RPC_URL=testnet # "devnet" | "testnet" | "localhost" | custom URL
|
|
92
|
+
VITE_MIDEN_PROVER=testnet # "devnet" | "testnet" | "local" | custom URL
|
|
93
|
+
```
|
|
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
|
+
|
|
125
|
+
## AI Developer Experience
|
|
126
|
+
|
|
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.
|
package/template/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>
|
|
7
|
+
<title>Miden Template</title>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div id="root"></div>
|
package/template/package.json
CHANGED
|
@@ -7,28 +7,38 @@
|
|
|
7
7
|
"dev": "vite",
|
|
8
8
|
"build": "tsc -b && vite build",
|
|
9
9
|
"lint": "eslint .",
|
|
10
|
-
"preview": "vite preview"
|
|
10
|
+
"preview": "vite preview",
|
|
11
|
+
"test": "vitest --run",
|
|
12
|
+
"test:watch": "vitest",
|
|
13
|
+
"test:coverage": "vitest --run --coverage"
|
|
11
14
|
},
|
|
12
15
|
"dependencies": {
|
|
13
|
-
"@miden-sdk/miden-
|
|
14
|
-
"
|
|
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",
|
|
15
20
|
"react": "^19.1.1",
|
|
16
21
|
"react-dom": "^19.1.1"
|
|
17
22
|
},
|
|
18
23
|
"devDependencies": {
|
|
19
24
|
"@eslint/js": "^9.36.0",
|
|
25
|
+
"@miden-sdk/vite-plugin": "0.14.4",
|
|
26
|
+
"@testing-library/dom": "^10.4.1",
|
|
27
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
28
|
+
"@testing-library/react": "^16.3.2",
|
|
29
|
+
"@testing-library/user-event": "^14.6.1",
|
|
20
30
|
"@types/node": "^24.6.0",
|
|
21
31
|
"@types/react": "^19.1.16",
|
|
22
32
|
"@types/react-dom": "^19.1.9",
|
|
23
|
-
"@vitejs/plugin-react": "^
|
|
33
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
24
34
|
"eslint": "^9.36.0",
|
|
25
35
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
26
36
|
"eslint-plugin-react-refresh": "^0.4.22",
|
|
27
37
|
"globals": "^16.4.0",
|
|
28
|
-
"
|
|
38
|
+
"jsdom": "^28.1.0",
|
|
39
|
+
"typescript": "~5.7.0",
|
|
29
40
|
"typescript-eslint": "^8.45.0",
|
|
30
|
-
"vite": "^
|
|
31
|
-
"
|
|
32
|
-
"vite-plugin-wasm": "^3.5.0"
|
|
41
|
+
"vite": "^6.0.0",
|
|
42
|
+
"vitest": "^4.0.18"
|
|
33
43
|
}
|
|
34
44
|
}
|
|
Binary file
|
|
Binary file
|
package/template/src/App.tsx
CHANGED
|
@@ -1,63 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import midenLogo from "./assets/miden.svg";
|
|
4
|
-
import viteLogo from "/vite.svg";
|
|
5
|
-
import "./App.css";
|
|
6
|
-
import { demo } from "./miden/lib/demo";
|
|
7
|
-
|
|
8
|
-
function App() {
|
|
9
|
-
const [count, setCount] = useState(0);
|
|
10
|
-
const [isRunning, setIsRunning] = useState(false);
|
|
11
|
-
const [result, setResult] = useState<string>("");
|
|
12
|
-
|
|
13
|
-
const handleDemoClick = async () => {
|
|
14
|
-
setIsRunning(true);
|
|
15
|
-
setResult("");
|
|
16
|
-
try {
|
|
17
|
-
await demo();
|
|
18
|
-
setResult("Demo executed successfully! Check console for output.");
|
|
19
|
-
} catch (error) {
|
|
20
|
-
setResult(
|
|
21
|
-
`Error: ${error instanceof Error ? error.message : String(error)}`
|
|
22
|
-
);
|
|
23
|
-
} finally {
|
|
24
|
-
setIsRunning(false);
|
|
25
|
-
}
|
|
26
|
-
};
|
|
1
|
+
import { AppProviders } from "@/providers";
|
|
2
|
+
import { AppContent } from "@/components/AppContent";
|
|
27
3
|
|
|
4
|
+
export default function App() {
|
|
28
5
|
return (
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
<img src={viteLogo} className="logo" alt="Vite logo" />
|
|
33
|
-
</a>
|
|
34
|
-
<a href="https://react.dev" target="_blank">
|
|
35
|
-
<img src={reactLogo} className="logo react" alt="React logo" />
|
|
36
|
-
</a>
|
|
37
|
-
<a href="https://docs.miden.xyz" target="_blank">
|
|
38
|
-
<img src={midenLogo} className="logo miden" alt="Miden logo" />
|
|
39
|
-
</a>
|
|
40
|
-
</div>
|
|
41
|
-
<h1>Vite + React + Miden</h1>
|
|
42
|
-
<div className="card">
|
|
43
|
-
<button onClick={() => setCount((count) => count + 1)}>
|
|
44
|
-
count is {count}
|
|
45
|
-
</button>
|
|
46
|
-
<p>
|
|
47
|
-
Edit <code>src/App.tsx</code> and save to test HMR
|
|
48
|
-
</p>
|
|
49
|
-
</div>
|
|
50
|
-
<div className="card">
|
|
51
|
-
<button onClick={handleDemoClick} disabled={isRunning}>
|
|
52
|
-
{isRunning ? "Running Demo..." : "Run Miden Demo"}
|
|
53
|
-
</button>
|
|
54
|
-
{result && <p style={{ marginTop: "1rem" }}>{result}</p>}
|
|
55
|
-
</div>
|
|
56
|
-
<p className="read-the-docs">
|
|
57
|
-
Click on the Vite, React, and Miden logos to learn more
|
|
58
|
-
</p>
|
|
59
|
-
</>
|
|
6
|
+
<AppProviders>
|
|
7
|
+
<AppContent />
|
|
8
|
+
</AppProviders>
|
|
60
9
|
);
|
|
61
10
|
}
|
|
62
|
-
|
|
63
|
-
export default App;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
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.
|
|
5
|
+
*/
|
|
6
|
+
|
|
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";
|
|
11
|
+
|
|
12
|
+
export const MOCK_WALLET_HEADER = {
|
|
13
|
+
id: WALLET_ID_1,
|
|
14
|
+
nonce: 1n,
|
|
15
|
+
storageCommitment: "0x0000000000000000000000000000000000000000000000000000000000000001",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const MOCK_WALLET_HEADER_2 = {
|
|
19
|
+
id: WALLET_ID_2,
|
|
20
|
+
nonce: 0n,
|
|
21
|
+
storageCommitment: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const MOCK_FAUCET_HEADER = {
|
|
25
|
+
id: FAUCET_ID,
|
|
26
|
+
nonce: 5n,
|
|
27
|
+
storageCommitment: "0x0000000000000000000000000000000000000000000000000000000000000005",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const MOCK_ASSET_BALANCE = {
|
|
31
|
+
assetId: FAUCET_ID,
|
|
32
|
+
amount: 1000000000n, // 10.0 tokens with 8 decimals
|
|
33
|
+
symbol: "TEST",
|
|
34
|
+
decimals: 8,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const MOCK_ASSET_BALANCE_EMPTY = {
|
|
38
|
+
assetId: FAUCET_ID,
|
|
39
|
+
amount: 0n,
|
|
40
|
+
symbol: "TEST",
|
|
41
|
+
decimals: 8,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const MOCK_ACCOUNT = {
|
|
45
|
+
id: WALLET_ID_1,
|
|
46
|
+
nonce: 1n,
|
|
47
|
+
bech32id: () => WALLET_ID_1,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const MOCK_ASSET_METADATA = {
|
|
51
|
+
assetId: FAUCET_ID,
|
|
52
|
+
symbol: "TEST",
|
|
53
|
+
decimals: 8,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// TransactionResult shape — matches @miden-sdk/react types for useMint / useConsume /
|
|
57
|
+
// useSwap / useMultiSend / useTransaction result payloads.
|
|
58
|
+
export const MOCK_TRANSACTION_RESULT = {
|
|
59
|
+
transactionId: "0xabc123def456789012345678901234567890123456789012345678901234abcd",
|
|
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
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export {
|
|
2
|
+
WALLET_ID_1,
|
|
3
|
+
WALLET_ID_2,
|
|
4
|
+
FAUCET_ID,
|
|
5
|
+
COUNTER_ID,
|
|
6
|
+
MOCK_WALLET_HEADER,
|
|
7
|
+
MOCK_WALLET_HEADER_2,
|
|
8
|
+
MOCK_FAUCET_HEADER,
|
|
9
|
+
MOCK_ASSET_BALANCE,
|
|
10
|
+
MOCK_ASSET_BALANCE_EMPTY,
|
|
11
|
+
MOCK_ACCOUNT,
|
|
12
|
+
MOCK_ASSET_METADATA,
|
|
13
|
+
MOCK_TRANSACTION_RESULT,
|
|
14
|
+
MOCK_SEND_RESULT,
|
|
15
|
+
} from "./accounts";
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
MOCK_NOTE_ASSET,
|
|
19
|
+
MOCK_NOTE_SUMMARY,
|
|
20
|
+
MOCK_INPUT_NOTE_RECORD,
|
|
21
|
+
MOCK_CONSUMABLE_NOTE_RECORD,
|
|
22
|
+
} from "./notes";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realistic note fixtures for Miden frontend tests.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { FAUCET_ID, WALLET_ID_1, WALLET_ID_2 } from "./accounts";
|
|
6
|
+
|
|
7
|
+
export const MOCK_NOTE_ASSET = {
|
|
8
|
+
assetId: FAUCET_ID,
|
|
9
|
+
amount: 500000000n, // 5.0 tokens with 8 decimals
|
|
10
|
+
symbol: "TEST",
|
|
11
|
+
decimals: 8,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const MOCK_NOTE_SUMMARY = {
|
|
15
|
+
id: "0xnote1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab",
|
|
16
|
+
assets: [MOCK_NOTE_ASSET],
|
|
17
|
+
sender: WALLET_ID_2,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const MOCK_INPUT_NOTE_RECORD = {
|
|
21
|
+
id: () => "0xnote1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab",
|
|
22
|
+
assets: () => [{ faucetId: () => FAUCET_ID, amount: () => 500000000n }],
|
|
23
|
+
metadata: () => ({
|
|
24
|
+
sender: () => ({ toBech32: () => WALLET_ID_2 }),
|
|
25
|
+
noteType: () => 1,
|
|
26
|
+
}),
|
|
27
|
+
status: () => "committed",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const MOCK_CONSUMABLE_NOTE_RECORD = {
|
|
31
|
+
...MOCK_INPUT_NOTE_RECORD,
|
|
32
|
+
accountId: WALLET_ID_1,
|
|
33
|
+
};
|