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.
- package/package.json +1 -1
- package/template/.claude/commands/review-security.md +67 -0
- package/template/.claude/settings.json +1 -7
- package/template/.claude/settings.local.json +24 -0
- package/template/.claude/skills/frontend-pitfalls/SKILL.md +28 -31
- package/template/.claude/skills/frontend-source-guide/SKILL.md +14 -14
- package/template/.claude/skills/miden-concepts/SKILL.md +4 -2
- package/template/.claude/skills/react-sdk-patterns/SKILL.md +294 -28
- package/template/.claude/skills/signer-integration/SKILL.md +22 -3
- package/template/.claude/skills/testing-patterns/SKILL.md +201 -40
- package/template/.claude/skills/vite-wasm-setup/SKILL.md +20 -14
- package/template/.claude/skills/web-client-usage/SKILL.md +454 -0
- package/template/.env.example +15 -2
- package/template/.mcp.json +9 -0
- package/template/CLAUDE.md +49 -16
- package/template/README.md +85 -19
- package/template/package.json +5 -4
- package/template/public/packages/counter_account.masp +0 -0
- package/template/public/packages/increment_note.masp +0 -0
- package/template/src/__tests__/fixtures/accounts.ts +17 -6
- package/template/src/__tests__/fixtures/index.ts +1 -0
- package/template/src/__tests__/mocks/miden-sdk-react.ts +18 -1
- package/template/src/__tests__/patterns/mutation-hook.test.tsx +2 -2
- package/template/src/__tests__/patterns/provider-setup.test.tsx +2 -0
- package/template/src/components/AppContent.tsx +33 -3
- package/template/{create-miden-app/template/src/components/Counter.tsx → src/components/ConfiguredCounter.tsx} +7 -4
- package/template/src/components/Counter.tsx +12 -41
- package/template/src/components/__tests__/AppContent.test.tsx +192 -4
- package/template/src/components/__tests__/ConfiguredCounter.test.tsx +116 -0
- package/template/src/components/__tests__/Counter.test.tsx +24 -94
- package/template/src/config.ts +26 -6
- package/template/src/hooks/__tests__/useIncrementCounter.test.tsx +257 -0
- package/template/src/hooks/useIncrementCounter.ts +109 -50
- package/template/src/providers.tsx +20 -24
- package/template/vite.config.ts +1 -1
- package/template/vitest.config.ts +1 -2
- package/template/yarn.lock +761 -688
- package/template/create-miden-app/template/.claude/hooks/typecheck.sh +0 -27
- package/template/create-miden-app/template/.claude/settings.json +0 -17
- package/template/create-miden-app/template/.claude/skills/frontend-pitfalls/SKILL.md +0 -189
- package/template/create-miden-app/template/.claude/skills/frontend-source-guide/SKILL.md +0 -163
- package/template/create-miden-app/template/.claude/skills/miden-concepts/SKILL.md +0 -108
- package/template/create-miden-app/template/.claude/skills/react-sdk-patterns/SKILL.md +0 -294
- package/template/create-miden-app/template/.claude/skills/signer-integration/SKILL.md +0 -158
- package/template/create-miden-app/template/.claude/skills/vite-wasm-setup/SKILL.md +0 -128
- package/template/create-miden-app/template/.env.example +0 -5
- package/template/create-miden-app/template/CLAUDE.md +0 -116
- package/template/create-miden-app/template/README.md +0 -61
- package/template/create-miden-app/template/eslint.config.js +0 -23
- package/template/create-miden-app/template/index.html +0 -13
- package/template/create-miden-app/template/package.json +0 -34
- package/template/create-miden-app/template/public/vite.svg +0 -1
- package/template/create-miden-app/template/src/App.tsx +0 -10
- package/template/create-miden-app/template/src/assets/miden.svg +0 -3
- package/template/create-miden-app/template/src/assets/react.svg +0 -1
- package/template/create-miden-app/template/src/components/AppContent.css +0 -45
- package/template/create-miden-app/template/src/components/AppContent.tsx +0 -50
- package/template/create-miden-app/template/src/components/Counter.css +0 -27
- package/template/create-miden-app/template/src/config.ts +0 -21
- package/template/create-miden-app/template/src/hooks/useIncrementCounter.ts +0 -136
- package/template/create-miden-app/template/src/index.css +0 -75
- package/template/create-miden-app/template/src/lib/miden.ts +0 -9
- package/template/create-miden-app/template/src/main.tsx +0 -10
- package/template/create-miden-app/template/src/providers.tsx +0 -31
- package/template/create-miden-app/template/src/vite-env.d.ts +0 -1
- package/template/create-miden-app/template/tsconfig.app.json +0 -32
- package/template/create-miden-app/template/tsconfig.json +0 -7
- package/template/create-miden-app/template/tsconfig.node.json +0 -24
- package/template/create-miden-app/template/vite.config.ts +0 -17
- package/template/create-miden-app/template/yarn.lock +0 -1697
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: react-sdk-patterns
|
|
3
|
-
description: Complete guide to building Miden frontends with @miden-sdk/react hooks. Covers MidenProvider setup, all query hooks (useAccounts, useAccount, useNotes, useSyncState, useAssetMetadata), all mutation hooks (useCreateWallet, useSend, useMultiSend, useMint, useConsume, useSwap, useTransaction, useCreateFaucet), transaction stages, signer integration, and utility functions. Use when writing, editing, or reviewing Miden React frontend code.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Miden React SDK Patterns
|
|
7
|
-
|
|
8
|
-
## SDK Choice
|
|
9
|
-
|
|
10
|
-
ALWAYS use `@miden-sdk/react` hooks. Only fall back to raw `@miden-sdk/miden-sdk` WebClient via `useMidenClient()` for operations not covered by hooks. The React SDK handles WASM safety (runExclusive), state management (Zustand), auto-sync, and transaction stage tracking automatically.
|
|
11
|
-
|
|
12
|
-
## MidenProvider Configuration
|
|
13
|
-
|
|
14
|
-
```tsx
|
|
15
|
-
import { MidenProvider } from "@miden-sdk/react";
|
|
16
|
-
|
|
17
|
-
<MidenProvider
|
|
18
|
-
config={{
|
|
19
|
-
rpcUrl: "devnet", // "devnet" | "testnet" | "localhost" | custom URL
|
|
20
|
-
prover: "devnet", // "local" | "devnet" | "testnet" | custom URL
|
|
21
|
-
autoSyncInterval: 15000, // ms, set to 0 to disable. Default: 15000
|
|
22
|
-
noteTransportUrl: "...", // optional: for private note delivery
|
|
23
|
-
}}
|
|
24
|
-
loadingComponent={<Loading />} // shown during WASM init
|
|
25
|
-
errorComponent={<Error />} // shown on init failure (receives Error prop)
|
|
26
|
-
>
|
|
27
|
-
<App />
|
|
28
|
-
</MidenProvider>
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
| Network | rpcUrl | Use When |
|
|
32
|
-
|---------|--------|----------|
|
|
33
|
-
| Devnet | `"devnet"` | Development, testing with fake tokens |
|
|
34
|
-
| Testnet | `"testnet"` | Pre-production testing |
|
|
35
|
-
| Localhost | `"localhost"` | Local node at `http://localhost:57291` |
|
|
36
|
-
|
|
37
|
-
## Query Hooks
|
|
38
|
-
|
|
39
|
-
All return `{ data, isLoading, error, refetch }`.
|
|
40
|
-
|
|
41
|
-
### useAccounts()
|
|
42
|
-
```tsx
|
|
43
|
-
const { data: accounts } = useAccounts();
|
|
44
|
-
// accounts.wallets — regular wallet accounts
|
|
45
|
-
// accounts.faucets — token faucet accounts
|
|
46
|
-
// accounts.all — everything (AccountHeader[])
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### useAccount(accountId: string)
|
|
50
|
-
```tsx
|
|
51
|
-
const { data: account } = useAccount(accountId);
|
|
52
|
-
// account.account — Account object (.id, .nonce, .bech32id())
|
|
53
|
-
// account.assets — AssetBalance[] (assetId, amount, symbol?, decimals?)
|
|
54
|
-
// account.getBalance(faucetId) — bigint balance for specific token
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### useNotes(filter?)
|
|
58
|
-
```tsx
|
|
59
|
-
const { data: notes } = useNotes();
|
|
60
|
-
// notes.notes — InputNoteRecord[]
|
|
61
|
-
// notes.consumableNotes — ConsumableNoteRecord[]
|
|
62
|
-
// notes.noteSummaries — NoteSummary[] (id, assets, sender)
|
|
63
|
-
// notes.consumableNoteSummaries — NoteSummary[]
|
|
64
|
-
|
|
65
|
-
// Filter by account:
|
|
66
|
-
const { data } = useNotes({ accountId: "0x..." });
|
|
67
|
-
// Filter by status:
|
|
68
|
-
const { data } = useNotes({ status: "committed" });
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### useSyncState()
|
|
72
|
-
```tsx
|
|
73
|
-
const { syncHeight, isSyncing, lastSyncTime, sync, error } = useSyncState();
|
|
74
|
-
await sync(); // Manual sync
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### useAssetMetadata(faucetId: string | string[])
|
|
78
|
-
```tsx
|
|
79
|
-
const { data: metadata } = useAssetMetadata(faucetId);
|
|
80
|
-
// metadata.symbol — "TEST"
|
|
81
|
-
// metadata.decimals — 8
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### useTransactionHistory(options?)
|
|
85
|
-
```tsx
|
|
86
|
-
const { records, record, status, isLoading } = useTransactionHistory({ id: txId });
|
|
87
|
-
// status: "pending" | "committed" | "discarded" | null
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Mutation Hooks
|
|
91
|
-
|
|
92
|
-
All return `{ mutate, data, isLoading, stage, error, reset }`.
|
|
93
|
-
|
|
94
|
-
**Transaction stages**: `"idle"` → `"executing"` → `"proving"` → `"submitting"` → `"complete"`
|
|
95
|
-
|
|
96
|
-
### useCreateWallet()
|
|
97
|
-
```tsx
|
|
98
|
-
const { mutate: createWallet, isLoading } = useCreateWallet();
|
|
99
|
-
const account = await createWallet({
|
|
100
|
-
storageMode: "private", // "private" | "public" | "network". Default: "private"
|
|
101
|
-
mutable: true, // Default: true
|
|
102
|
-
authScheme: 0, // 0 = RpoFalcon512, 1 = EcdsaK256Keccak. Default: 0
|
|
103
|
-
});
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### useCreateFaucet()
|
|
107
|
-
```tsx
|
|
108
|
-
const { mutate: createFaucet } = useCreateFaucet();
|
|
109
|
-
const faucet = await createFaucet({
|
|
110
|
-
tokenSymbol: "TEST",
|
|
111
|
-
decimals: 8, // Default: 8
|
|
112
|
-
maxSupply: 1000000n, // bigint!
|
|
113
|
-
storageMode: "public", // Default: "private"
|
|
114
|
-
});
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### useSend()
|
|
118
|
-
```tsx
|
|
119
|
-
const { mutate: send, stage } = useSend();
|
|
120
|
-
await send({
|
|
121
|
-
from: senderAccountId,
|
|
122
|
-
to: recipientAccountId,
|
|
123
|
-
assetId: faucetId, // token faucet ID
|
|
124
|
-
amount: 1000n, // bigint!
|
|
125
|
-
noteType: "private", // "private" | "public". Default: "private"
|
|
126
|
-
recallHeight: 100, // optional: sender can reclaim after this block
|
|
127
|
-
timelockHeight: 50, // optional: recipient can consume after this block
|
|
128
|
-
});
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### useMultiSend()
|
|
132
|
-
```tsx
|
|
133
|
-
const { mutate: multiSend } = useMultiSend();
|
|
134
|
-
await multiSend({
|
|
135
|
-
from: senderAccountId,
|
|
136
|
-
assetId: faucetId,
|
|
137
|
-
recipients: [
|
|
138
|
-
{ to: recipient1, amount: 500n },
|
|
139
|
-
{ to: recipient2, amount: 300n },
|
|
140
|
-
],
|
|
141
|
-
noteType: "private",
|
|
142
|
-
});
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### useMint()
|
|
146
|
-
```tsx
|
|
147
|
-
const { mutate: mint } = useMint();
|
|
148
|
-
await mint({
|
|
149
|
-
targetAccountId: recipientId,
|
|
150
|
-
faucetId: myFaucetId,
|
|
151
|
-
amount: 10000n, // bigint!
|
|
152
|
-
noteType: "public",
|
|
153
|
-
});
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### useConsume()
|
|
157
|
-
```tsx
|
|
158
|
-
const { mutate: consume } = useConsume();
|
|
159
|
-
await consume({
|
|
160
|
-
accountId: myAccountId,
|
|
161
|
-
noteIds: [noteId1, noteId2],
|
|
162
|
-
});
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### useSwap()
|
|
166
|
-
```tsx
|
|
167
|
-
const { mutate: swap } = useSwap();
|
|
168
|
-
await swap({
|
|
169
|
-
accountId: myAccountId,
|
|
170
|
-
offeredFaucetId: tokenA,
|
|
171
|
-
offeredAmount: 100n,
|
|
172
|
-
requestedFaucetId: tokenB,
|
|
173
|
-
requestedAmount: 50n,
|
|
174
|
-
noteType: "private",
|
|
175
|
-
paybackNoteType: "private",
|
|
176
|
-
});
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### useTransaction() — Escape Hatch
|
|
180
|
-
```tsx
|
|
181
|
-
const { mutate: execute } = useTransaction();
|
|
182
|
-
|
|
183
|
-
// With pre-built TransactionRequest:
|
|
184
|
-
await execute({ accountId, request: txRequest });
|
|
185
|
-
|
|
186
|
-
// With factory function (gets access to client):
|
|
187
|
-
await execute({
|
|
188
|
-
accountId,
|
|
189
|
-
request: (client) => client.newSwapTransactionRequest(/* ... */),
|
|
190
|
-
});
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### useWaitForCommit()
|
|
194
|
-
```tsx
|
|
195
|
-
const { mutate: waitForCommit } = useWaitForCommit();
|
|
196
|
-
await waitForCommit({
|
|
197
|
-
transactionId: result.transactionId,
|
|
198
|
-
timeoutMs: 10000, // Default: 10000
|
|
199
|
-
intervalMs: 1000, // Default: 1000
|
|
200
|
-
});
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
### useWaitForNotes()
|
|
204
|
-
```tsx
|
|
205
|
-
const { mutate: waitForNotes } = useWaitForNotes();
|
|
206
|
-
await waitForNotes({
|
|
207
|
-
accountId: myAccountId,
|
|
208
|
-
minCount: 1, // Default: 1
|
|
209
|
-
timeoutMs: 10000,
|
|
210
|
-
});
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## Transaction Progress UI
|
|
214
|
-
|
|
215
|
-
```tsx
|
|
216
|
-
function SendButton({ from, to, assetId, amount }) {
|
|
217
|
-
const { mutate: send, stage, isLoading, error } = useSend();
|
|
218
|
-
|
|
219
|
-
return (
|
|
220
|
-
<div>
|
|
221
|
-
<button onClick={() => send({ from, to, assetId, amount })} disabled={isLoading}>
|
|
222
|
-
{isLoading ? `${stage}...` : "Send"}
|
|
223
|
-
</button>
|
|
224
|
-
{error && <p>Error: {error.message}</p>}
|
|
225
|
-
</div>
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
## Signer Integration
|
|
231
|
-
|
|
232
|
-
### Local Keystore (Default)
|
|
233
|
-
No signer provider needed. Keys are managed in the browser via IndexedDB.
|
|
234
|
-
|
|
235
|
-
### External Signers
|
|
236
|
-
Wrap MidenProvider with a signer provider. Three pre-built options:
|
|
237
|
-
- `ParaSignerProvider` from `@miden-sdk/para` — EVM wallets
|
|
238
|
-
- `TurnkeySignerProvider` from `@miden-sdk/miden-turnkey-react` — passkey auth
|
|
239
|
-
- `MidenFiSignerProvider` from `@miden-sdk/wallet-adapter-react` — MidenFi wallet
|
|
240
|
-
|
|
241
|
-
```tsx
|
|
242
|
-
// Example: Para signer wrapping MidenProvider
|
|
243
|
-
import { ParaSignerProvider } from "@miden-sdk/para";
|
|
244
|
-
<ParaSignerProvider apiKey="..." environment="PRODUCTION">
|
|
245
|
-
<MidenProvider config={...}><App /></MidenProvider>
|
|
246
|
-
</ParaSignerProvider>
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### useSigner() — Unified Interface
|
|
250
|
-
```tsx
|
|
251
|
-
const { isConnected, connect, disconnect, name } = useSigner();
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### Custom Signer
|
|
255
|
-
Implement `SignerContextValue` interface via `SignerContext.Provider`. Requires: `name`, `storeName` (unique per user for DB isolation), `accountConfig`, `signCb`, `connect`, `disconnect`. See `frontend-source-guide` skill for source references.
|
|
256
|
-
|
|
257
|
-
## Utility Functions
|
|
258
|
-
|
|
259
|
-
```tsx
|
|
260
|
-
import { formatAssetAmount, parseAssetAmount, getNoteSummary, formatNoteSummary, toBech32AccountId } from "@miden-sdk/react";
|
|
261
|
-
|
|
262
|
-
formatAssetAmount(1000000n, 8) // "0.01"
|
|
263
|
-
parseAssetAmount("0.01", 8) // 1000000n
|
|
264
|
-
const summary = getNoteSummary(note); // { id, assets, sender }
|
|
265
|
-
formatNoteSummary(summary); // "1.5 TEST"
|
|
266
|
-
toBech32AccountId("0x1234..."); // "miden1qy35..."
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
## Direct Client Access
|
|
270
|
-
|
|
271
|
-
```tsx
|
|
272
|
-
const client = useMidenClient(); // throws if not ready
|
|
273
|
-
const { runExclusive } = useMiden();
|
|
274
|
-
|
|
275
|
-
// For operations not covered by hooks:
|
|
276
|
-
await runExclusive(async (client) => {
|
|
277
|
-
const header = await client.getBlockHeaderByNumber(100);
|
|
278
|
-
});
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
## Type Imports
|
|
282
|
-
|
|
283
|
-
```tsx
|
|
284
|
-
import type {
|
|
285
|
-
MidenConfig, QueryResult, MutationResult, TransactionStage,
|
|
286
|
-
AccountsResult, AccountResult, AssetBalance, NotesResult, NoteSummary,
|
|
287
|
-
SendOptions, MultiSendOptions, MintOptions, ConsumeOptions, SwapOptions,
|
|
288
|
-
CreateWalletOptions, CreateFaucetOptions, ExecuteTransactionOptions,
|
|
289
|
-
TransactionResult, SyncState, WaitForCommitOptions, WaitForNotesOptions,
|
|
290
|
-
Account, AccountId, InputNoteRecord, ConsumableNoteRecord,
|
|
291
|
-
TransactionRecord, TransactionRequest, NoteType, AccountStorageMode,
|
|
292
|
-
SignerContextValue, SignCallback, SignerAccountConfig,
|
|
293
|
-
} from "@miden-sdk/react";
|
|
294
|
-
```
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: signer-integration
|
|
3
|
-
description: Guide to integrating external signers (Para, Turnkey, MidenFi wallet adapter) and building custom signers for Miden React frontends. Covers provider setup, passkey authentication, unified signer interface, custom SignerContext implementation, and custom account components. Use when adding wallet connection, authentication, or external key management to a Miden frontend.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Miden Signer Integration
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
By default, MidenProvider uses a **local keystore** (keys in IndexedDB, no wallet connection needed). For production apps, wrap MidenProvider with a signer provider to use external key management.
|
|
11
|
-
|
|
12
|
-
Signer providers must wrap MidenProvider (outer → inner):
|
|
13
|
-
```
|
|
14
|
-
<SignerProvider> ← manages keys + auth
|
|
15
|
-
<MidenProvider> ← manages Miden client
|
|
16
|
-
<App />
|
|
17
|
-
</MidenProvider>
|
|
18
|
-
</SignerProvider>
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Pre-Built Signer Providers
|
|
22
|
-
|
|
23
|
-
### Para (EVM Wallets)
|
|
24
|
-
```tsx
|
|
25
|
-
import { ParaSignerProvider } from "@miden-sdk/para";
|
|
26
|
-
|
|
27
|
-
<ParaSignerProvider apiKey="your-api-key" environment="PRODUCTION">
|
|
28
|
-
<MidenProvider config={{ rpcUrl: "testnet" }}>
|
|
29
|
-
<App />
|
|
30
|
-
</MidenProvider>
|
|
31
|
-
</ParaSignerProvider>
|
|
32
|
-
|
|
33
|
-
const { para, wallet, isConnected } = useParaSigner();
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Turnkey (Passkey Authentication)
|
|
37
|
-
```tsx
|
|
38
|
-
import { TurnkeySignerProvider } from "@miden-sdk/miden-turnkey-react";
|
|
39
|
-
|
|
40
|
-
// Config is optional — defaults to https://api.turnkey.com
|
|
41
|
-
// and reads VITE_TURNKEY_ORG_ID from environment
|
|
42
|
-
<TurnkeySignerProvider>
|
|
43
|
-
<MidenProvider config={{ rpcUrl: "testnet" }}>
|
|
44
|
-
<App />
|
|
45
|
-
</MidenProvider>
|
|
46
|
-
</TurnkeySignerProvider>
|
|
47
|
-
|
|
48
|
-
// Or with explicit config:
|
|
49
|
-
<TurnkeySignerProvider config={{
|
|
50
|
-
apiBaseUrl: "https://api.turnkey.com",
|
|
51
|
-
defaultOrganizationId: "your-org-id",
|
|
52
|
-
}}>
|
|
53
|
-
...
|
|
54
|
-
</TurnkeySignerProvider>
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Connect via passkey:
|
|
58
|
-
```tsx
|
|
59
|
-
import { useSigner } from "@miden-sdk/react";
|
|
60
|
-
import { useTurnkeySigner } from "@miden-sdk/miden-turnkey-react";
|
|
61
|
-
|
|
62
|
-
const { isConnected, connect, disconnect } = useSigner();
|
|
63
|
-
await connect(); // triggers passkey flow, auto-selects account
|
|
64
|
-
|
|
65
|
-
// Turnkey-specific extras
|
|
66
|
-
const { client, account, setAccount } = useTurnkeySigner();
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### MidenFi Wallet Adapter (Browser Extension)
|
|
70
|
-
```tsx
|
|
71
|
-
import { MidenFiSignerProvider } from "@miden-sdk/wallet-adapter-react";
|
|
72
|
-
|
|
73
|
-
<MidenFiSignerProvider network="Testnet">
|
|
74
|
-
<MidenProvider config={{ rpcUrl: "testnet" }}>
|
|
75
|
-
<App />
|
|
76
|
-
</MidenProvider>
|
|
77
|
-
</MidenFiSignerProvider>
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Unified Signer Interface
|
|
81
|
-
|
|
82
|
-
Works with any signer provider above:
|
|
83
|
-
```tsx
|
|
84
|
-
import { useSigner } from "@miden-sdk/react";
|
|
85
|
-
|
|
86
|
-
const { isConnected, connect, disconnect, name } = useSigner();
|
|
87
|
-
|
|
88
|
-
if (!isConnected) {
|
|
89
|
-
return <button onClick={connect}>Connect {name}</button>;
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Building a Custom Signer
|
|
94
|
-
|
|
95
|
-
Implement `SignerContextValue` via `SignerContext.Provider`:
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
import { SignerContext } from "@miden-sdk/react";
|
|
99
|
-
|
|
100
|
-
<SignerContext.Provider value={{
|
|
101
|
-
name: "MyWallet",
|
|
102
|
-
storeName: `mywallet_${userAddress}`, // unique per user for DB isolation
|
|
103
|
-
isConnected: true,
|
|
104
|
-
accountConfig: {
|
|
105
|
-
publicKey: userPublicKeyCommitment, // Uint8Array
|
|
106
|
-
storageMode: "private",
|
|
107
|
-
},
|
|
108
|
-
signCb: async (pubKey, signingInputs) => {
|
|
109
|
-
// Route to your signing service
|
|
110
|
-
return signature; // Uint8Array
|
|
111
|
-
},
|
|
112
|
-
connect: async () => { /* trigger wallet connection */ },
|
|
113
|
-
disconnect: async () => { /* clear session */ },
|
|
114
|
-
}}>
|
|
115
|
-
<MidenProvider config={{ rpcUrl: "testnet" }}>
|
|
116
|
-
<App />
|
|
117
|
-
</MidenProvider>
|
|
118
|
-
</SignerContext.Provider>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Required fields:**
|
|
122
|
-
- `name` — Display name for the signer
|
|
123
|
-
- `storeName` — Unique string per user (isolates IndexedDB data between users)
|
|
124
|
-
- `accountConfig` — Public key commitment + storage mode
|
|
125
|
-
- `signCb` — Callback that signs transaction data with your key management service
|
|
126
|
-
- `connect` / `disconnect` — Session lifecycle handlers
|
|
127
|
-
|
|
128
|
-
## Custom Account Components
|
|
129
|
-
|
|
130
|
-
Attach application-specific `AccountComponent` instances (e.g., DEX logic from `.masp` packages) to accounts created by the signer:
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
import { type SignerAccountConfig } from "@miden-sdk/react";
|
|
134
|
-
import { AccountComponent } from "@miden-sdk/miden-sdk";
|
|
135
|
-
|
|
136
|
-
const myDexComponent: AccountComponent = await loadCompiledComponent();
|
|
137
|
-
|
|
138
|
-
const accountConfig: SignerAccountConfig = {
|
|
139
|
-
publicKeyCommitment: userPublicKeyCommitment,
|
|
140
|
-
accountType: "RegularAccountUpdatableCode",
|
|
141
|
-
storageMode: myStorageMode,
|
|
142
|
-
customComponents: [myDexComponent],
|
|
143
|
-
};
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Components are appended to the `AccountBuilder` after the default basic wallet component. The field is optional — omitting it preserves default behavior.
|
|
147
|
-
|
|
148
|
-
## Which Signer to Choose
|
|
149
|
-
|
|
150
|
-
| Signer | Auth Method | Keys Stored | Best For |
|
|
151
|
-
|--------|-------------|-------------|----------|
|
|
152
|
-
| Local keystore (default) | None | Browser IndexedDB | Development, demos |
|
|
153
|
-
| Para | EVM wallet | Para servers | Apps with existing EVM users |
|
|
154
|
-
| Turnkey | Passkey (biometric) | Turnkey servers | Consumer apps, no seed phrases |
|
|
155
|
-
| MidenFi Wallet | Browser extension | Extension | Power users with MidenFi wallet |
|
|
156
|
-
| Custom | Your choice | Your infrastructure | Enterprise, custom auth flows |
|
|
157
|
-
|
|
158
|
-
**Key trade-off**: Local keystore requires no setup but keys are lost if the user clears browser data. External signers persist keys server-side but add a dependency.
|
|
@@ -1,128 +0,0 @@
|
|
|
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) |
|
|
@@ -1,116 +0,0 @@
|
|
|
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
|
-
- `vite.config.ts` — Vite config with midenVitePlugin() from @miden-sdk/vite-plugin
|
|
12
|
-
- `package.json` — Dependencies: @miden-sdk/react, @miden-sdk/miden-sdk, @demox-labs/miden-wallet-adapter
|
|
13
|
-
|
|
14
|
-
## Build & Dev
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
npm run dev # Start dev server (Vite)
|
|
18
|
-
npm run build # Type check + production build (tsc -b && vite build)
|
|
19
|
-
npm run lint # ESLint
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Type checking alone (verification command):
|
|
23
|
-
```
|
|
24
|
-
npx tsc -b --noEmit
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## SDK Choice: React SDK over Raw WebClient
|
|
28
|
-
|
|
29
|
-
ALWAYS prefer `@miden-sdk/react` hooks over raw `@miden-sdk/miden-sdk` WebClient methods.
|
|
30
|
-
Only use WebClient directly via `useMidenClient()` for operations not covered by hooks.
|
|
31
|
-
|
|
32
|
-
### Setup (main.tsx or App.tsx)
|
|
33
|
-
```tsx
|
|
34
|
-
import { MidenProvider } from "@miden-sdk/react";
|
|
35
|
-
|
|
36
|
-
<MidenProvider config={{ rpcUrl: "devnet", prover: "devnet" }}>
|
|
37
|
-
<App />
|
|
38
|
-
</MidenProvider>
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Query Hooks (return { data, isLoading, error, refetch })
|
|
42
|
-
```tsx
|
|
43
|
-
const { data: accounts } = useAccounts(); // .wallets, .faucets, .all
|
|
44
|
-
const { data: account } = useAccount(accountId); // .balance(faucetId)
|
|
45
|
-
const { data: notes } = useNotes(); // .input, .consumable
|
|
46
|
-
const { syncHeight, sync } = useSyncState();
|
|
47
|
-
const { data: metadata } = useAssetMetadata(faucetId); // .symbol, .decimals
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Mutation Hooks (return { mutate, isLoading, stage, error, reset })
|
|
51
|
-
Transaction stages: `idle → executing → proving → submitting → complete`
|
|
52
|
-
```tsx
|
|
53
|
-
const { mutate: createWallet } = useCreateWallet();
|
|
54
|
-
const { mutate: send, stage } = useSend();
|
|
55
|
-
const { mutate: consume } = useConsume();
|
|
56
|
-
const { mutate: mint } = useMint();
|
|
57
|
-
const { mutate: swap } = useSwap();
|
|
58
|
-
const { mutate: execute } = useTransaction(); // arbitrary tx requests
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Token Amounts Are BigInt
|
|
62
|
-
```tsx
|
|
63
|
-
import { formatAssetAmount, parseAssetAmount } from "@miden-sdk/react";
|
|
64
|
-
const display = formatAssetAmount(balance, 8); // bigint → string
|
|
65
|
-
const amount = parseAssetAmount("1.5", 8); // string → bigint
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Critical Pitfalls
|
|
69
|
-
|
|
70
|
-
**WASM init must complete first**: Always use MidenProvider's `loadingComponent` or check `useMiden().isReady`. Components rendering before WASM init will crash.
|
|
71
|
-
|
|
72
|
-
**Recursive WASM access crashes**: Never call client methods concurrently. Use `runExclusive()` from `useMiden()` for sequential execution. Built-in hooks handle this automatically.
|
|
73
|
-
|
|
74
|
-
**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.
|
|
75
|
-
|
|
76
|
-
**Token amounts are bigint, not number**: `send({ amount: 1000 })` will fail. Use `amount: 1000n` or `parseAssetAmount("10", 8)`.
|
|
77
|
-
|
|
78
|
-
## General Frontend Skills (Recommended)
|
|
79
|
-
|
|
80
|
-
For general React, TypeScript, and design capabilities, install these official skills alongside our Miden-specific ones:
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
# Vercel's React/design skills
|
|
84
|
-
git clone https://github.com/vercel-labs/agent-skills.git
|
|
85
|
-
# Install: react-best-practices, web-design-guidelines, composition-patterns
|
|
86
|
-
|
|
87
|
-
# Anthropic's frontend design skill (Claude Code plugin)
|
|
88
|
-
# See: https://github.com/anthropics/claude-code/tree/main/plugins/frontend-design
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
These provide the general frontend layer. The Miden-specific skills below layer on top.
|
|
92
|
-
|
|
93
|
-
## Miden Skills
|
|
94
|
-
|
|
95
|
-
For Miden-specific guidance, Claude will auto-load these skills when relevant:
|
|
96
|
-
- `react-sdk-patterns` — Complete React SDK hook API reference
|
|
97
|
-
- `frontend-pitfalls` — All frontend/WASM/browser pitfalls with safe/unsafe examples
|
|
98
|
-
- `miden-concepts` — Miden architecture from a developer perspective
|
|
99
|
-
- `vite-wasm-setup` — Vite + WASM configuration, deployment headers, troubleshooting
|
|
100
|
-
|
|
101
|
-
## Advanced Development
|
|
102
|
-
|
|
103
|
-
For complex applications beyond basic hook usage (custom signers, raw WebClient, advanced note flows):
|
|
104
|
-
|
|
105
|
-
1. Clone `miden-client` repo alongside this project (see `frontend-source-guide` skill)
|
|
106
|
-
2. Use Plan Mode first — Claude explores React SDK source + examples before coding
|
|
107
|
-
3. Claude uses sub-agents to explore repos efficiently without filling main context
|
|
108
|
-
4. The type check hook provides verification — check types, fix errors, recheck
|
|
109
|
-
|
|
110
|
-
The basic skills cover ~80% of patterns. Source repos provide the remaining 20% for advanced builders.
|
|
111
|
-
|
|
112
|
-
## Verification Workflow
|
|
113
|
-
|
|
114
|
-
After modifying TypeScript/React code, always:
|
|
115
|
-
1. Type check: `npx tsc -b --noEmit`
|
|
116
|
-
2. Build test: `npm run build`
|