create-miden-app 1.0.4 → 1.0.6
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/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 +35 -0
- package/template/.claude/skills/frontend-pitfalls/SKILL.md +189 -0
- package/template/.claude/skills/frontend-source-guide/SKILL.md +163 -0
- package/template/.claude/skills/miden-concepts/SKILL.md +108 -0
- package/template/.claude/skills/react-sdk-patterns/SKILL.md +296 -0
- package/template/.claude/skills/signer-integration/SKILL.md +158 -0
- package/template/.claude/skills/testing-patterns/SKILL.md +177 -0
- package/template/.claude/skills/vite-wasm-setup/SKILL.md +128 -0
- package/template/.env.example +5 -0
- package/template/CLAUDE.md +210 -0
- package/template/README.md +53 -14
- package/template/create-miden-app/template/.claude/hooks/typecheck.sh +27 -0
- package/template/create-miden-app/template/.claude/settings.json +17 -0
- package/template/create-miden-app/template/.claude/skills/frontend-pitfalls/SKILL.md +189 -0
- package/template/create-miden-app/template/.claude/skills/frontend-source-guide/SKILL.md +163 -0
- package/template/create-miden-app/template/.claude/skills/miden-concepts/SKILL.md +108 -0
- package/template/create-miden-app/template/.claude/skills/react-sdk-patterns/SKILL.md +294 -0
- package/template/create-miden-app/template/.claude/skills/signer-integration/SKILL.md +158 -0
- package/template/create-miden-app/template/.claude/skills/vite-wasm-setup/SKILL.md +128 -0
- package/template/create-miden-app/template/.env.example +5 -0
- package/template/create-miden-app/template/CLAUDE.md +116 -0
- package/template/create-miden-app/template/README.md +61 -0
- package/template/create-miden-app/template/eslint.config.js +23 -0
- package/template/create-miden-app/template/index.html +13 -0
- package/template/create-miden-app/template/package.json +34 -0
- package/template/create-miden-app/template/public/vite.svg +1 -0
- package/template/create-miden-app/template/src/App.tsx +10 -0
- package/template/create-miden-app/template/src/assets/miden.svg +3 -0
- package/template/create-miden-app/template/src/assets/react.svg +1 -0
- package/template/{src/App.css → create-miden-app/template/src/components/AppContent.css} +9 -9
- package/template/create-miden-app/template/src/components/AppContent.tsx +50 -0
- package/template/create-miden-app/template/src/components/Counter.css +27 -0
- package/template/create-miden-app/template/src/components/Counter.tsx +45 -0
- package/template/create-miden-app/template/src/config.ts +21 -0
- package/template/create-miden-app/template/src/hooks/useIncrementCounter.ts +136 -0
- package/template/create-miden-app/template/src/index.css +75 -0
- package/template/create-miden-app/template/src/lib/miden.ts +9 -0
- package/template/create-miden-app/template/src/main.tsx +10 -0
- package/template/create-miden-app/template/src/providers.tsx +31 -0
- package/template/create-miden-app/template/src/vite-env.d.ts +1 -0
- package/template/create-miden-app/template/tsconfig.app.json +32 -0
- package/template/create-miden-app/template/tsconfig.json +7 -0
- package/template/create-miden-app/template/tsconfig.node.json +24 -0
- package/template/create-miden-app/template/vite.config.ts +17 -0
- package/template/create-miden-app/template/yarn.lock +1697 -0
- package/template/index.html +1 -1
- package/template/package.json +17 -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 +57 -0
- package/template/src/__tests__/fixtures/index.ts +21 -0
- package/template/src/__tests__/fixtures/notes.ts +33 -0
- package/template/src/__tests__/mocks/miden-sdk-react.ts +244 -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 +75 -0
- package/template/src/__tests__/patterns/query-hook.test.tsx +143 -0
- package/template/src/components/AppContent.css +45 -0
- package/template/src/components/AppContent.tsx +50 -0
- package/template/src/components/Counter.css +27 -0
- package/template/src/components/Counter.tsx +45 -0
- package/template/src/components/__tests__/AppContent.test.tsx +86 -0
- package/template/src/components/__tests__/Counter.test.tsx +114 -0
- package/template/src/config.ts +21 -0
- package/template/src/hooks/useIncrementCounter.ts +136 -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 +31 -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 +26 -0
- package/template/vitest.setup.ts +1 -0
- package/template/yarn.lock +1580 -781
- package/template/src/miden/lib/demo.ts +0 -106
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vite-wasm-setup
|
|
3
|
+
description: Guide to configuring Vite for Miden WASM applications. Covers the midenVitePlugin() setup, COOP/COEP headers, production deployment headers, TypeScript compatibility, and troubleshooting common Vite + WASM issues. Use when setting up a new Miden frontend, debugging build or runtime errors related to WASM or Vite configuration, or deploying to production.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Vite + WASM Configuration for Miden
|
|
7
|
+
|
|
8
|
+
## Required vite.config.ts
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { defineConfig } from "vite";
|
|
12
|
+
import react from "@vitejs/plugin-react";
|
|
13
|
+
import { midenVitePlugin } from "@miden-sdk/vite-plugin";
|
|
14
|
+
|
|
15
|
+
export default defineConfig({
|
|
16
|
+
plugins: [react(), midenVitePlugin()],
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`midenVitePlugin()` accepts an options object:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
midenVitePlugin({ crossOriginIsolation: true })
|
|
24
|
+
// Enables COOP/COEP headers in dev server. Defaults to false to avoid breaking OAuth popups.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## What midenVitePlugin() Handles
|
|
28
|
+
|
|
29
|
+
`@miden-sdk/vite-plugin` abstracts all Miden-specific Vite configuration:
|
|
30
|
+
|
|
31
|
+
- **WASM loading** — Configures Vite to correctly import `.wasm` modules
|
|
32
|
+
- **Top-level await** — Enables top-level `await` required by the WASM SDK initialization
|
|
33
|
+
- **optimizeDeps** — Excludes `@miden-sdk/miden-sdk` from pre-bundling (pre-bundling corrupts the WASM binary)
|
|
34
|
+
- **COOP/COEP headers** — Optionally adds `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` headers via `crossOriginIsolation` option
|
|
35
|
+
|
|
36
|
+
You do not need to install or configure `vite-plugin-wasm`, `vite-plugin-top-level-await`, or dexie aliases manually.
|
|
37
|
+
|
|
38
|
+
## Required Dependencies
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@miden-sdk/react": "^0.13.0",
|
|
44
|
+
"@miden-sdk/miden-sdk": "^0.13.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@miden-sdk/vite-plugin": "^0.13.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Production Deployment Headers
|
|
53
|
+
|
|
54
|
+
COOP/COEP headers must be set on the production server. `midenVitePlugin({ crossOriginIsolation: true })` only affects the Vite dev server.
|
|
55
|
+
|
|
56
|
+
### Nginx
|
|
57
|
+
```nginx
|
|
58
|
+
add_header Cross-Origin-Opener-Policy same-origin;
|
|
59
|
+
add_header Cross-Origin-Embedder-Policy require-corp;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Vercel (vercel.json)
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"headers": [
|
|
66
|
+
{
|
|
67
|
+
"source": "/(.*)",
|
|
68
|
+
"headers": [
|
|
69
|
+
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
|
|
70
|
+
{ "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Cloudflare Pages (_headers)
|
|
78
|
+
```
|
|
79
|
+
/*
|
|
80
|
+
Cross-Origin-Opener-Policy: same-origin
|
|
81
|
+
Cross-Origin-Embedder-Policy: require-corp
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### WASM MIME Type
|
|
85
|
+
Ensure your server serves `.wasm` files with `application/wasm` MIME type.
|
|
86
|
+
|
|
87
|
+
## COOP/COEP Gotchas
|
|
88
|
+
|
|
89
|
+
These headers break:
|
|
90
|
+
- **Third-party iframes** (YouTube embeds, Twitter embeds, analytics)
|
|
91
|
+
- **External scripts** without CORS headers
|
|
92
|
+
- **OAuth popups** from different origins
|
|
93
|
+
|
|
94
|
+
Workaround: Use `credentialless` for COEP if you need cross-origin resources:
|
|
95
|
+
```
|
|
96
|
+
Cross-Origin-Embedder-Policy: credentialless
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Note: `credentialless` provides weaker isolation but allows most cross-origin resources.
|
|
100
|
+
|
|
101
|
+
## TypeScript Compatibility
|
|
102
|
+
|
|
103
|
+
Standard Vite-compatible tsconfig settings work with Miden. The only actual constraint is ES2020+ for `bigint` support:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"compilerOptions": {
|
|
108
|
+
"target": "ES2022",
|
|
109
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
110
|
+
"module": "ESNext",
|
|
111
|
+
"moduleResolution": "bundler"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`module: "ESNext"` and `moduleResolution: "bundler"` are standard Vite defaults, not Miden-specific requirements. If you're using the Vite-generated tsconfig, no changes are needed beyond ensuring `target` is ES2020+.
|
|
117
|
+
|
|
118
|
+
## Troubleshooting
|
|
119
|
+
|
|
120
|
+
| Issue | Cause | Fix |
|
|
121
|
+
|-------|-------|-----|
|
|
122
|
+
| "SharedArrayBuffer is not defined" | Missing COOP/COEP headers | Use `midenVitePlugin({ crossOriginIsolation: true })` in dev; set headers on production server |
|
|
123
|
+
| WASM module not found | SDK not configured correctly | Ensure `midenVitePlugin()` is in plugins array |
|
|
124
|
+
| "Top-level await not supported" | Missing plugin setup | Ensure `midenVitePlugin()` is in plugins array |
|
|
125
|
+
| WASM init hangs | COEP blocking WASM fetch | Check network tab for blocked requests; enable `crossOriginIsolation` |
|
|
126
|
+
| Build succeeds but WASM fails at runtime | Wrong MIME type | Serve .wasm as application/wasm |
|
|
127
|
+
| "recursive use of an object" | Concurrent WASM access | Use runExclusive() from useMiden() |
|
|
128
|
+
| Double initialization in dev | React StrictMode | Use MidenProvider (handles this internally) |
|
|
@@ -0,0 +1,210 @@
|
|
|
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 over Raw WebClient
|
|
34
|
+
|
|
35
|
+
ALWAYS prefer `@miden-sdk/react` hooks over raw `@miden-sdk/miden-sdk` WebClient methods.
|
|
36
|
+
Only use WebClient directly via `useMidenClient()` for operations not covered by hooks.
|
|
37
|
+
|
|
38
|
+
### Setup (main.tsx or App.tsx)
|
|
39
|
+
```tsx
|
|
40
|
+
import { MidenProvider } from "@miden-sdk/react";
|
|
41
|
+
|
|
42
|
+
<MidenProvider config={{ rpcUrl: "testnet", prover: "testnet" }}>
|
|
43
|
+
<App />
|
|
44
|
+
</MidenProvider>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Query Hooks
|
|
48
|
+
Each returns its own result shape plus `isLoading`, `error`, `refetch`:
|
|
49
|
+
```tsx
|
|
50
|
+
const { wallets, faucets } = useAccounts();
|
|
51
|
+
const { account, assets, getBalance } = useAccount(accountId);
|
|
52
|
+
const { notes, consumableNotes } = useNotes();
|
|
53
|
+
const { syncHeight, sync } = useSyncState();
|
|
54
|
+
const { assetMetadata } = useAssetMetadata(faucetId);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Mutation Hooks
|
|
58
|
+
Each returns its own action function plus `isLoading`, `stage`, `error`, `reset`.
|
|
59
|
+
Transaction stages: `idle → executing → proving → submitting → complete`
|
|
60
|
+
```tsx
|
|
61
|
+
const { createWallet } = useCreateWallet();
|
|
62
|
+
const { send, stage } = useSend();
|
|
63
|
+
const { consume } = useConsume();
|
|
64
|
+
const { mint } = useMint();
|
|
65
|
+
const { swap } = useSwap();
|
|
66
|
+
const { execute } = useTransaction(); // arbitrary tx requests
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Token Amounts Are BigInt
|
|
70
|
+
```tsx
|
|
71
|
+
import { formatAssetAmount, parseAssetAmount } from "@miden-sdk/react";
|
|
72
|
+
const display = formatAssetAmount(balance, 8); // bigint → string
|
|
73
|
+
const amount = parseAssetAmount("1.5", 8); // string → bigint
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## TDD Workflow
|
|
77
|
+
|
|
78
|
+
When building features, follow this test-driven cycle:
|
|
79
|
+
|
|
80
|
+
1. **Write a failing test** for the feature/component
|
|
81
|
+
2. **Run tests** — confirm the test fails (red)
|
|
82
|
+
3. **Implement** the minimum code to make the test pass
|
|
83
|
+
4. **Run tests** — confirm all tests pass (green)
|
|
84
|
+
5. **Refactor** if needed, re-run tests
|
|
85
|
+
6. Type checking runs automatically after each edit (PostToolUse hook)
|
|
86
|
+
7. Affected tests run automatically after each edit (PostToolUse hook)
|
|
87
|
+
|
|
88
|
+
### Test file conventions
|
|
89
|
+
- Component tests: `src/components/__tests__/ComponentName.test.tsx`
|
|
90
|
+
- Hook tests: `src/hooks/__tests__/hookName.test.ts`
|
|
91
|
+
- Pattern references: `src/__tests__/patterns/` — copy and adapt these
|
|
92
|
+
|
|
93
|
+
### Writing tests for Miden components
|
|
94
|
+
```tsx
|
|
95
|
+
// 1. Mock the SDK at module level (always required)
|
|
96
|
+
vi.mock("@miden-sdk/react", () => import("@/__tests__/mocks/miden-sdk-react"));
|
|
97
|
+
|
|
98
|
+
// 2. Import hooks to override per-test
|
|
99
|
+
import { useAccounts } from "@miden-sdk/react";
|
|
100
|
+
|
|
101
|
+
// 3. Override in individual tests
|
|
102
|
+
vi.mocked(useAccounts).mockReturnValue({ wallets: [], ... });
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
See `testing-patterns` skill for full mock factory reference and fixture data.
|
|
106
|
+
|
|
107
|
+
## Verification Sequence
|
|
108
|
+
|
|
109
|
+
Automated verification runs in layers (each catches different failure classes):
|
|
110
|
+
|
|
111
|
+
1. **TypeScript type check** (auto, per-edit) — catches type errors immediately
|
|
112
|
+
2. **Affected tests** (auto, per-edit) — catches logic regressions from changes
|
|
113
|
+
3. **Full test suite + type check + build** (auto, on task completion via Stop hook) — catches integration issues
|
|
114
|
+
4. **Browser verification** (manual/MCP) — catches "compiles but doesn't work" failures
|
|
115
|
+
|
|
116
|
+
### Browser verification (when needed)
|
|
117
|
+
After completing a feature, verify in the browser:
|
|
118
|
+
1. Start dev server: `yarn dev`
|
|
119
|
+
2. Navigate to `http://localhost:5173`
|
|
120
|
+
3. Check for render errors and console messages
|
|
121
|
+
4. If wallet-dependent features: ask the PM to verify with the MidenFi wallet extension
|
|
122
|
+
|
|
123
|
+
## Contract Artifact Handoff
|
|
124
|
+
|
|
125
|
+
Frontend loads pre-compiled `.masp` packages from `public/packages/` at runtime.
|
|
126
|
+
|
|
127
|
+
### Artifact location
|
|
128
|
+
```
|
|
129
|
+
public/packages/
|
|
130
|
+
├── counter_account.masp # Counter account component
|
|
131
|
+
└── increment_note.masp # Increment note script
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Building artifacts
|
|
135
|
+
In the contract project (e.g., `project-template/`):
|
|
136
|
+
```bash
|
|
137
|
+
cargo miden build --release
|
|
138
|
+
# Copy .masp files from contracts/*/target/miden/release/ to public/packages/
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Validate artifacts
|
|
142
|
+
```bash
|
|
143
|
+
.claude/hooks/check-artifacts.sh
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Failure recovery
|
|
147
|
+
- **Missing artifacts**: Build contracts with `cargo miden build` or ask the PM to supply the `.masp` files
|
|
148
|
+
- **Stale artifacts**: Rebuild and re-copy after contract changes
|
|
149
|
+
- **Deserialization failure at runtime**: Version mismatch — rebuild contracts with the SDK version matching `@miden-sdk/miden-sdk` in `package.json`
|
|
150
|
+
|
|
151
|
+
## Critical Pitfalls
|
|
152
|
+
|
|
153
|
+
**WASM init must complete first**: Always use MidenProvider's `loadingComponent` or check `useMiden().isReady`. Components rendering before WASM init will crash.
|
|
154
|
+
|
|
155
|
+
**Recursive WASM access crashes**: Never call client methods concurrently. Use `runExclusive()` from `useMiden()` for sequential execution. Built-in hooks handle this automatically.
|
|
156
|
+
|
|
157
|
+
**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.
|
|
158
|
+
|
|
159
|
+
**Token amounts are bigint, not number**: `send({ amount: 1000 })` will fail. Use `amount: 1000n` or `parseAssetAmount("10", 8)`.
|
|
160
|
+
|
|
161
|
+
## PM Workflow
|
|
162
|
+
|
|
163
|
+
For non-developer users building with this template:
|
|
164
|
+
|
|
165
|
+
1. Clone the repository and run `yarn install`
|
|
166
|
+
2. Start Claude Code in the project directory
|
|
167
|
+
3. Describe the app you want to build in natural language
|
|
168
|
+
4. Claude will implement features using TDD — tests are written first, then code
|
|
169
|
+
5. Automated hooks verify correctness at every step
|
|
170
|
+
6. When Claude says "done", the Stop hook runs full tests + build automatically
|
|
171
|
+
7. Review the app in the browser: `yarn dev` → open `http://localhost:5173`
|
|
172
|
+
8. If using wallet features, install the MidenFi browser extension to test
|
|
173
|
+
|
|
174
|
+
### Known limitations
|
|
175
|
+
- **Visual correctness**: Automated tests verify structure and behavior, not visual appearance. Review the app in the browser for styling issues.
|
|
176
|
+
- **Wallet extension**: Real wallet interactions require the MidenFi browser extension. Tests mock the wallet adapter.
|
|
177
|
+
- **Network-dependent features**: Some features (syncing, transaction submission) require testnet connectivity.
|
|
178
|
+
|
|
179
|
+
## Miden Skills
|
|
180
|
+
|
|
181
|
+
For Miden-specific guidance, Claude will auto-load these skills when relevant:
|
|
182
|
+
- `react-sdk-patterns` — Complete React SDK hook API reference
|
|
183
|
+
- `testing-patterns` — Test mock factory, fixtures, and TDD conventions
|
|
184
|
+
- `frontend-pitfalls` — All frontend/WASM/browser pitfalls with safe/unsafe examples
|
|
185
|
+
- `miden-concepts` — Miden architecture from a developer perspective
|
|
186
|
+
- `vite-wasm-setup` — Vite + WASM configuration, deployment headers, troubleshooting
|
|
187
|
+
- `signer-integration` — External signer setup (Para, Turnkey, MidenFi)
|
|
188
|
+
|
|
189
|
+
## General Frontend Skills (Recommended)
|
|
190
|
+
|
|
191
|
+
For general React, TypeScript, and design capabilities, install these official skills alongside our Miden-specific ones:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Vercel's React/design skills
|
|
195
|
+
git clone https://github.com/vercel-labs/agent-skills.git
|
|
196
|
+
# Install: react-best-practices, web-design-guidelines, composition-patterns
|
|
197
|
+
|
|
198
|
+
# Anthropic's frontend design skill (Claude Code plugin)
|
|
199
|
+
# See: https://github.com/anthropics/claude-code/tree/main/plugins/frontend-design
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Advanced Development
|
|
203
|
+
|
|
204
|
+
For complex applications beyond basic hook usage (custom signers, raw WebClient, advanced note flows):
|
|
205
|
+
|
|
206
|
+
1. Clone `miden-client` repo alongside this project (see `frontend-source-guide` skill)
|
|
207
|
+
2. Use Plan Mode first — Claude explores React SDK source + examples before coding
|
|
208
|
+
3. Claude uses sub-agents to explore repos efficiently without filling main context
|
|
209
|
+
|
|
210
|
+
The basic skills cover ~80% of patterns. Source repos provide the remaining 20% for advanced builders.
|
package/template/README.md
CHANGED
|
@@ -1,22 +1,61 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Miden Frontend Template
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Getting Started
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
yarn install
|
|
9
|
+
yarn dev
|
|
10
|
+
```
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
Open [http://localhost:5173](http://localhost:5173). Connect your MidenFi wallet and click the counter button to publish an increment note.
|
|
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
|
|
24
|
+
├── hooks/
|
|
25
|
+
│ └── useIncrementCounter.ts # Note construction, wallet submission, re-sync
|
|
26
|
+
└── lib/
|
|
27
|
+
└── miden.ts # Shared Miden utilities
|
|
28
|
+
```
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
## Network Counter Demo
|
|
21
31
|
|
|
22
|
-
|
|
32
|
+
The template demonstrates the network note pattern on Miden testnet:
|
|
33
|
+
|
|
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`
|
|
38
|
+
|
|
39
|
+
Pre-compiled `.masp` packages for the counter account and increment note are in `public/packages/`.
|
|
40
|
+
|
|
41
|
+
## Key Dependencies
|
|
42
|
+
|
|
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 |
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
SDK settings can be overridden via environment variables (see `.env.example`):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
VITE_MIDEN_RPC_URL=testnet # "testnet" | "localhost" | custom URL
|
|
56
|
+
VITE_MIDEN_PROVER=testnet # "testnet" | "local"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## AI Developer Experience
|
|
60
|
+
|
|
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.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Post-edit hook: runs TypeScript type checking when .ts/.tsx files in src/ are modified.
|
|
3
|
+
# Reads JSON input from stdin (Claude Code hook protocol).
|
|
4
|
+
|
|
5
|
+
INPUT=$(cat)
|
|
6
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty')
|
|
7
|
+
|
|
8
|
+
# Only trigger for TypeScript/React files in src/
|
|
9
|
+
if [[ -z "$FILE_PATH" ]] || [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.tsx ]]; then
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if [[ "$FILE_PATH" != *"/src/"* ]]; then
|
|
14
|
+
exit 0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Run TypeScript type checking from project root
|
|
18
|
+
cd "$CLAUDE_PROJECT_DIR" || exit 0
|
|
19
|
+
|
|
20
|
+
if npx tsc -b --noEmit 2>&1; then
|
|
21
|
+
echo '{"hookSpecificOutput": {"additionalContext": "TypeScript type check passed"}}'
|
|
22
|
+
exit 0
|
|
23
|
+
else
|
|
24
|
+
CHECK_OUTPUT=$(npx tsc -b --noEmit 2>&1 | tail -30)
|
|
25
|
+
echo "{\"hookSpecificOutput\": {\"additionalContext\": \"TypeScript type check FAILED. Fix type errors before continuing.\n$CHECK_OUTPUT\"}}"
|
|
26
|
+
exit 2
|
|
27
|
+
fi
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PostToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Edit|Write",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/typecheck.sh",
|
|
10
|
+
"timeout": 60,
|
|
11
|
+
"statusMessage": "Type checking modified files..."
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-pitfalls
|
|
3
|
+
description: Critical pitfalls and safety rules for Miden frontend development. Covers WASM initialization, concurrent access crashes, COOP/COEP headers, BigInt handling, Bech32 network mismatches, IndexedDB state loss, auto-sync side effects, Vite configuration, and React rendering race conditions. Use when reviewing, debugging, or writing Miden frontend code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Miden Frontend Pitfalls
|
|
7
|
+
|
|
8
|
+
## FP1: WASM Initialization Race (CRITICAL)
|
|
9
|
+
|
|
10
|
+
Components that use Miden hooks before MidenProvider finishes WASM initialization will crash.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
// WRONG — crashes if WASM not ready
|
|
14
|
+
function App() {
|
|
15
|
+
const { data } = useAccounts(); // throws before init
|
|
16
|
+
return <div>{data?.wallets.length}</div>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// CORRECT — use loadingComponent or check isReady
|
|
20
|
+
<MidenProvider
|
|
21
|
+
config={{ rpcUrl: "devnet" }}
|
|
22
|
+
loadingComponent={<p>Loading WASM...</p>}
|
|
23
|
+
>
|
|
24
|
+
<App />
|
|
25
|
+
</MidenProvider>
|
|
26
|
+
|
|
27
|
+
// CORRECT — guard with isReady
|
|
28
|
+
function App() {
|
|
29
|
+
const { isReady } = useMiden();
|
|
30
|
+
if (!isReady) return <p>Loading...</p>;
|
|
31
|
+
return <WalletView />;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## FP2: Recursive WASM Access Crash (CRITICAL)
|
|
36
|
+
|
|
37
|
+
The WASM client is single-threaded. Concurrent calls crash with "recursive use of an object detected".
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
// WRONG — two operations running simultaneously
|
|
41
|
+
const handleClick = async () => {
|
|
42
|
+
sync(); // fires async
|
|
43
|
+
await send({ ... }); // runs concurrently — CRASH
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// CORRECT — use runExclusive for sequential execution
|
|
47
|
+
const { runExclusive } = useMiden();
|
|
48
|
+
await runExclusive(async (client) => {
|
|
49
|
+
await client.syncState();
|
|
50
|
+
// now safe to do next operation
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Built-in hooks (useSend, useConsume, etc.) already use runExclusive internally. This pitfall applies when using `useMidenClient()` directly or mixing manual client calls with hook mutations.
|
|
55
|
+
|
|
56
|
+
## FP3: COOP/COEP Headers Missing (CRITICAL)
|
|
57
|
+
|
|
58
|
+
WASM SharedArrayBuffer requires these headers. Without them, WASM init silently fails.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// REQUIRED in vite.config.ts
|
|
62
|
+
server: {
|
|
63
|
+
headers: {
|
|
64
|
+
"Cross-Origin-Opener-Policy": "same-origin",
|
|
65
|
+
"Cross-Origin-Embedder-Policy": "require-corp",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// ALSO REQUIRED on production server (nginx, Vercel, Cloudflare)
|
|
70
|
+
// See vite-wasm-setup skill for deployment configs
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Gotcha**: These headers break third-party iframes, external scripts without CORS, and OAuth popups. Use `credentialless` for COEP if cross-origin resources are needed.
|
|
74
|
+
|
|
75
|
+
**Note**: `midenVitePlugin()` from `@miden-sdk/vite-plugin` handles COOP/COEP automatically via its `crossOriginIsolation` option (defaults to `false` to avoid breaking OAuth popups). Enable it instead of setting headers manually.
|
|
76
|
+
|
|
77
|
+
## FP4: BigInt Type Mismatch (HIGH)
|
|
78
|
+
|
|
79
|
+
All token amounts in the SDK are `bigint`. Passing `number` causes TypeScript errors or runtime failures.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
// WRONG
|
|
83
|
+
await send({ from, to, assetId, amount: 1000 }); // number — fails
|
|
84
|
+
await createFaucet({ maxSupply: 1000000, ... }); // number — fails
|
|
85
|
+
|
|
86
|
+
// CORRECT
|
|
87
|
+
await send({ from, to, assetId, amount: 1000n }); // bigint literal
|
|
88
|
+
await createFaucet({ maxSupply: BigInt(1000000), ... }); // BigInt constructor
|
|
89
|
+
|
|
90
|
+
// CORRECT — use parseAssetAmount for user input
|
|
91
|
+
import { parseAssetAmount } from "@miden-sdk/react";
|
|
92
|
+
const amount = parseAssetAmount(inputValue, 8); // string → bigint
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Gotcha**: `JSON.stringify` cannot serialize `bigint`. Use a custom replacer or convert to string first.
|
|
96
|
+
|
|
97
|
+
## FP5: Bech32 Network Mismatch (HIGH)
|
|
98
|
+
|
|
99
|
+
Bech32-encoded account IDs include the network. A devnet address on testnet points to a different or nonexistent account.
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// WRONG — hardcoding a bech32 address used across networks
|
|
103
|
+
const ADMIN = "miden1qy35..."; // this is network-specific!
|
|
104
|
+
|
|
105
|
+
// CORRECT — use hex format for cross-network compatibility
|
|
106
|
+
const ADMIN = "0x1234567890abcdef";
|
|
107
|
+
|
|
108
|
+
// CORRECT — derive bech32 per network
|
|
109
|
+
account.bech32id(); // returns correct bech32 for current network
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Both hex and bech32 formats work in all hooks. Prefer hex for constants, bech32 for display.
|
|
113
|
+
|
|
114
|
+
## FP6: Auto-Sync Side Effects (MEDIUM)
|
|
115
|
+
|
|
116
|
+
Default `autoSyncInterval` is 15000ms (15 seconds). Each sync triggers re-renders in useAccounts, useAccount, useNotes, etc.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// PROBLEM — form resets every 15 seconds because parent re-renders
|
|
120
|
+
<MidenProvider config={{ rpcUrl: "devnet" }}>
|
|
121
|
+
<SendForm /> {/* re-renders on every sync */}
|
|
122
|
+
</MidenProvider>
|
|
123
|
+
|
|
124
|
+
// SOLUTION 1 — preferred: use stable keys and memoization
|
|
125
|
+
const MemoizedForm = React.memo(SendForm);
|
|
126
|
+
|
|
127
|
+
// SOLUTION 2 — disable auto-sync for manual control
|
|
128
|
+
<MidenProvider config={{ rpcUrl: "devnet", autoSyncInterval: 0 }}>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## FP7: IndexedDB State Loss (MEDIUM)
|
|
132
|
+
|
|
133
|
+
The client persists accounts, keys, and notes in IndexedDB. Browser "Clear site data", private browsing, or storage pressure can delete everything.
|
|
134
|
+
|
|
135
|
+
- Warn users that clearing browser data deletes their wallet
|
|
136
|
+
- Consider external signers (Para, Turnkey) for production — keys are server-side
|
|
137
|
+
- Implement account export/backup for local keystore users
|
|
138
|
+
|
|
139
|
+
## FP8: Vite Configuration Requirements (MEDIUM)
|
|
140
|
+
|
|
141
|
+
The `@miden-sdk/vite-plugin` package handles all Miden-specific Vite config.
|
|
142
|
+
The minimal setup is:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { midenVitePlugin } from "@miden-sdk/vite-plugin";
|
|
146
|
+
|
|
147
|
+
export default defineConfig({
|
|
148
|
+
plugins: [react(), midenVitePlugin()],
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`midenVitePlugin()` handles: WASM loading, top-level await, WASM pre-bundling exclusion,
|
|
153
|
+
and optionally COOP/COEP headers (via the `crossOriginIsolation` option, defaults to `false`
|
|
154
|
+
to avoid breaking OAuth popups).
|
|
155
|
+
|
|
156
|
+
| Option | Default | Purpose |
|
|
157
|
+
|--------|---------|---------|
|
|
158
|
+
| `crossOriginIsolation` | `false` | Add COOP/COEP headers for SharedArrayBuffer |
|
|
159
|
+
|
|
160
|
+
Enable `crossOriginIsolation: true` only if your app doesn't use OAuth or cross-origin iframes.
|
|
161
|
+
For production COOP/COEP, set headers at the server level (see vite-wasm-setup skill).
|
|
162
|
+
|
|
163
|
+
## FP9: React StrictMode Double-Init (LOW)
|
|
164
|
+
|
|
165
|
+
React 19 StrictMode double-invokes effects in development. MidenProvider handles this via `isInitializedRef`, but direct `WebClient.createClient()` calls will initialize twice.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
// WRONG — manual client creation in useEffect
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
const client = await WebClient.createClient(url); // called twice in dev
|
|
171
|
+
}, []);
|
|
172
|
+
|
|
173
|
+
// CORRECT — always use MidenProvider
|
|
174
|
+
<MidenProvider config={{ rpcUrl: "devnet" }}>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Quick Reference
|
|
178
|
+
|
|
179
|
+
| # | Pitfall | Severity | One-Line Rule |
|
|
180
|
+
|---|---------|----------|---------------|
|
|
181
|
+
| FP1 | WASM init race | CRITICAL | Use loadingComponent or check isReady |
|
|
182
|
+
| FP2 | Recursive WASM | CRITICAL | Use runExclusive() for all direct client access |
|
|
183
|
+
| FP3 | COOP/COEP | CRITICAL | Add headers in vite.config.ts AND production server |
|
|
184
|
+
| FP4 | BigInt | HIGH | All amounts are bigint (1000n not 1000) |
|
|
185
|
+
| FP5 | Bech32 mismatch | HIGH | Match network in rpcUrl and addresses |
|
|
186
|
+
| FP6 | Auto-sync | MEDIUM | Set autoSyncInterval: 0 if UI stability matters |
|
|
187
|
+
| FP7 | IndexedDB loss | MEDIUM | Warn users; use external signers for production |
|
|
188
|
+
| FP8 | Vite config | MEDIUM | Use `midenVitePlugin()` — it handles all Miden Vite config |
|
|
189
|
+
| FP9 | StrictMode | LOW | Use MidenProvider, not manual client creation |
|