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,454 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-client-usage
|
|
3
|
+
description: Conventions for writing JavaScript/TypeScript code that uses the Miden web SDK (`@miden-sdk/miden-sdk`). Use when building apps on Miden, writing integration tests, or calling MidenClient methods - covers initialization, the resource-based API (accounts, transactions, notes, keystore, compile), sync ordering, type conversions, transaction flows, custom contracts, private note transport, and pitfalls.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
NOTE: In the frontend-template context, prefer @miden-sdk/react hooks for all
|
|
7
|
+
operations they cover. Use the raw WebClient patterns documented here ONLY for
|
|
8
|
+
operations not available through React hooks (e.g., custom TransactionRequests,
|
|
9
|
+
direct store import/export, advanced sync control).
|
|
10
|
+
|
|
11
|
+
# Web SDK Usage Patterns
|
|
12
|
+
|
|
13
|
+
This skill targets the `@miden-sdk/miden-sdk` npm package shipped from
|
|
14
|
+
[`0xMiden/miden-client`](https://github.com/0xMiden/miden-client). For
|
|
15
|
+
React-hook usage, prefer the `react-sdk-patterns` skill - only fall through
|
|
16
|
+
to the raw client when a hook does not cover what you need.
|
|
17
|
+
|
|
18
|
+
## API Overview
|
|
19
|
+
|
|
20
|
+
The SDK exposes a top-level `MidenClient` whose state is split across typed
|
|
21
|
+
**resources**:
|
|
22
|
+
|
|
23
|
+
| Resource | What it covers |
|
|
24
|
+
|----------|----------------|
|
|
25
|
+
| `client.accounts` | Wallets, faucets, custom contracts, listing, import/export |
|
|
26
|
+
| `client.transactions` | `send` / `mint` / `consume` / `consumeAll` / `swap` / `execute` / `preview` / `waitFor` |
|
|
27
|
+
| `client.notes` | Listing, fetching, importing/exporting, private-note transport |
|
|
28
|
+
| `client.tags` | Note-tag subscriptions |
|
|
29
|
+
| `client.settings` | Persistent client settings |
|
|
30
|
+
| `client.compile` | Compiling MASM into account components, tx scripts, note scripts |
|
|
31
|
+
| `client.keystore` | Inserting / fetching / removing secret keys |
|
|
32
|
+
|
|
33
|
+
`MidenClient` is the public surface. The underlying WASM-bound class is still
|
|
34
|
+
exported as `WasmWebClient` (alias for the legacy `WebClient`) for low-level
|
|
35
|
+
operations the resource API does not yet wrap. To reach it, either use the
|
|
36
|
+
React `useMidenClient()` hook or import `WasmWebClient` directly from
|
|
37
|
+
`@miden-sdk/miden-sdk` (the class is `@internal` but exported). `MidenClient`
|
|
38
|
+
keeps its wrapped client in a real JS private field (`#inner`), so external
|
|
39
|
+
code cannot reach in directly.
|
|
40
|
+
|
|
41
|
+
## Client Initialization
|
|
42
|
+
|
|
43
|
+
### Convenience constructors (recommended)
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { MidenClient } from "@miden-sdk/miden-sdk";
|
|
47
|
+
|
|
48
|
+
// Testnet - autoSync on, testnet RPC + prover + note transport
|
|
49
|
+
const client = await MidenClient.createTestnet();
|
|
50
|
+
|
|
51
|
+
// Devnet equivalent
|
|
52
|
+
const client = await MidenClient.createDevnet();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Both accept the same `ClientOptions` for overrides:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
const client = await MidenClient.createTestnet({
|
|
59
|
+
storeName: "my-app-tests", // isolates the IndexedDB store
|
|
60
|
+
proverUrl: "local", // prove locally instead of remote
|
|
61
|
+
autoSync: false, // disable initial sync
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Generic constructor
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const client = await MidenClient.create({
|
|
69
|
+
rpcUrl: "https://rpc.testnet.miden.io", // string URL or "testnet"/"devnet"/"localhost"
|
|
70
|
+
noteTransportUrl: "https://ntx.testnet.miden.io",
|
|
71
|
+
storeName: "my-store",
|
|
72
|
+
seed: new Uint8Array(32), // optional - deterministic key generation
|
|
73
|
+
proverUrl: "testnet", // optional - sets a default prover
|
|
74
|
+
autoSync: true, // optional - call sync() after init
|
|
75
|
+
keystore: { // optional - external HSM/keystore
|
|
76
|
+
getKey: async (pubKey) => { /* return secretKey or null */ },
|
|
77
|
+
insertKey: async (pubKey, secretKey) => { /* persist */ },
|
|
78
|
+
sign: async (pubKey, signingInputs) => { /* return signature */ },
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If `rpcUrl` is omitted, `create()` delegates to `createTestnet()`.
|
|
84
|
+
|
|
85
|
+
### Lazy / SSR-safe init
|
|
86
|
+
|
|
87
|
+
Some bundles (Next.js, Capacitor, raw `/lazy` entry) cannot await WASM at
|
|
88
|
+
import time. Use `MidenClient.ready()` to wait for WASM in-band - it is
|
|
89
|
+
idempotent and shared across callers:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
await MidenClient.ready();
|
|
93
|
+
const client = await MidenClient.createTestnet();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Termination
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
client.terminate(); // free WASM resources, close the store handle
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
After `terminate()`, every method throws - guard against late callbacks on
|
|
103
|
+
unmount.
|
|
104
|
+
|
|
105
|
+
## Sync - Always Sync First
|
|
106
|
+
|
|
107
|
+
The client's view of the chain is only as fresh as its last sync. **Always
|
|
108
|
+
call `sync()` before reading account state or building a transaction that
|
|
109
|
+
depends on freshly received notes.**
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const summary = await client.sync(); // returns SyncSummary
|
|
113
|
+
const height = await client.getSyncHeight(); // current local block number
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Common patterns:
|
|
117
|
+
|
|
118
|
+
- Sync before consuming notes (notes must be committed on-chain)
|
|
119
|
+
- Sync after submitting a transaction to observe the result
|
|
120
|
+
- Pass `waitForConfirmation: true` to a `transactions.send/mint/consume/swap`
|
|
121
|
+
call to let the SDK wait for the tx commit instead of polling manually
|
|
122
|
+
- Use `client.waitForIdle()` to flush all queued WASM calls before doing a
|
|
123
|
+
side-effect that must not race with a kernel callback (e.g. clearing an
|
|
124
|
+
in-memory unlock token after a wallet "lock")
|
|
125
|
+
|
|
126
|
+
`autoSync: true` (default for `createTestnet`/`createDevnet`) only triggers a
|
|
127
|
+
single sync at construction time - it is not a polling loop. Use the React
|
|
128
|
+
SDK's `useSyncState` or `MidenProvider` `autoSyncInterval` for periodic sync.
|
|
129
|
+
|
|
130
|
+
## Type Conversions
|
|
131
|
+
|
|
132
|
+
Type confusion across the WASM boundary is the leading source of bugs.
|
|
133
|
+
|
|
134
|
+
### `AccountId`
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const id = AccountId.fromHex("0xabc123..."); // throws on invalid hex
|
|
138
|
+
const id = Address.fromBech32("mtst1abc...").accountId();
|
|
139
|
+
const hex = id.toString(); // "0x..."
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Pass `AccountId` (or any account ref the resource accepts: `string` hex,
|
|
143
|
+
`Address`, `Account`, `AccountHeader`) to resource methods - never raw
|
|
144
|
+
strings to methods that ask for `AccountId` directly.
|
|
145
|
+
|
|
146
|
+
`AccountId.fromHex` throws on malformed input; wrap in `try/catch` when
|
|
147
|
+
accepting user input.
|
|
148
|
+
|
|
149
|
+
### Amounts - Always `BigInt`
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
BigInt(1000)
|
|
153
|
+
1000n // numeric literal
|
|
154
|
+
BigInt("1000")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
All token amounts in the SDK are `bigint`. Mixing `number` causes silent
|
|
158
|
+
precision loss above `Number.MAX_SAFE_INTEGER` and `TypeError` below.
|
|
159
|
+
|
|
160
|
+
### Visibility & Account Types
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { NoteVisibility, AccountType, AuthScheme, StorageMode } from "@miden-sdk/miden-sdk";
|
|
164
|
+
|
|
165
|
+
NoteVisibility.Public // "public"
|
|
166
|
+
NoteVisibility.Private // "private"
|
|
167
|
+
|
|
168
|
+
AccountType.MutableWallet
|
|
169
|
+
AccountType.ImmutableWallet
|
|
170
|
+
AccountType.FungibleFaucet
|
|
171
|
+
AccountType.NonFungibleFaucet
|
|
172
|
+
AccountType.MutableContract
|
|
173
|
+
AccountType.ImmutableContract
|
|
174
|
+
|
|
175
|
+
AuthScheme.Falcon // default - Falcon-512 over Poseidon2
|
|
176
|
+
AuthScheme.ECDSA // EcdsaK256Keccak
|
|
177
|
+
|
|
178
|
+
StorageMode.Public
|
|
179
|
+
StorageMode.Private
|
|
180
|
+
StorageMode.Network
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Use `NoteVisibility` (not the legacy `NoteType` enum) and `AuthScheme.Falcon`
|
|
184
|
+
for the Poseidon2-based Falcon-512 scheme.
|
|
185
|
+
|
|
186
|
+
## Account Creation
|
|
187
|
+
|
|
188
|
+
Type discriminator on `auth`: wallets and faucets take `auth: AuthSchemeType` (a `"falcon" | "ecdsa"` string-union, e.g. `AuthScheme.Falcon`); custom contracts take `auth: AuthSecretKey` (a concrete WASM instance).
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Wallet - defaults: mutable, private, Falcon
|
|
192
|
+
const wallet = await client.accounts.create();
|
|
193
|
+
|
|
194
|
+
// Wallet with explicit options
|
|
195
|
+
const wallet = await client.accounts.create({
|
|
196
|
+
type: AccountType.MutableWallet,
|
|
197
|
+
storage: "private",
|
|
198
|
+
auth: AuthScheme.Falcon,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Faucet
|
|
202
|
+
const faucet = await client.accounts.create({
|
|
203
|
+
type: AccountType.FungibleFaucet,
|
|
204
|
+
storage: "public",
|
|
205
|
+
symbol: "DAG",
|
|
206
|
+
decimals: 8,
|
|
207
|
+
maxSupply: 10_000_000n,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Custom contract - requires seed and AuthSecretKey
|
|
211
|
+
const component = await client.compile.component({ code: contractMasm, slots: [] });
|
|
212
|
+
const contract = await client.accounts.create({
|
|
213
|
+
type: AccountType.MutableContract,
|
|
214
|
+
seed: new Uint8Array(32),
|
|
215
|
+
auth: secretKey, // AuthSecretKey, not the AuthScheme enum
|
|
216
|
+
components: [component],
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Transactions
|
|
221
|
+
|
|
222
|
+
The transactions API is option-bag-based and accepts any account ref
|
|
223
|
+
(`Account`, `AccountHeader`, hex string, `AccountId`).
|
|
224
|
+
|
|
225
|
+
### Send
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const { txId } = await client.transactions.send({
|
|
229
|
+
account: wallet, // sender
|
|
230
|
+
to: "0xrecipient...", // any account ref
|
|
231
|
+
token: faucet, // faucet account ref - identifies the asset
|
|
232
|
+
amount: 100n,
|
|
233
|
+
type: NoteVisibility.Public, // optional, defaults to "public" (raw SDK; resolveNoteType in dist/index.js:307-316). React useSend defaults to "private".
|
|
234
|
+
reclaimAfter: 100, // optional - sender can reclaim after this block
|
|
235
|
+
timelockUntil: 50, // optional - recipient can consume after this block
|
|
236
|
+
waitForConfirmation: true,
|
|
237
|
+
timeout: 30_000,
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
For private sends where you also need to deliver the note out-of-band, set
|
|
242
|
+
`returnNote: true` and the call returns the constructed `Note` object -
|
|
243
|
+
incompatible with `reclaimAfter`/`timelockUntil`.
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
const { txId, note } = await client.transactions.send({
|
|
247
|
+
account: wallet,
|
|
248
|
+
to: recipientAddress,
|
|
249
|
+
token: faucet,
|
|
250
|
+
amount: 100n,
|
|
251
|
+
type: NoteVisibility.Private,
|
|
252
|
+
returnNote: true,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Stream the note via the note-transport service
|
|
256
|
+
await client.notes.sendPrivate({ note, to: Address.fromBech32("mtst1...") });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Mint
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
const { txId } = await client.transactions.mint({
|
|
263
|
+
account: faucet, // faucet executes the mint
|
|
264
|
+
to: targetAccountId, // recipient
|
|
265
|
+
amount: 1000n,
|
|
266
|
+
type: NoteVisibility.Public,
|
|
267
|
+
waitForConfirmation: true,
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The transaction executes on the **faucet** - a frequent bug is passing the
|
|
272
|
+
recipient as `account`.
|
|
273
|
+
|
|
274
|
+
### Consume
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// Specific notes
|
|
278
|
+
await client.transactions.consume({
|
|
279
|
+
account: wallet,
|
|
280
|
+
notes: [noteId1, noteRecord, "0xnote..."], // any of: hex, NoteId, InputNoteRecord, Note
|
|
281
|
+
waitForConfirmation: true,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Drain everything consumable for the account
|
|
285
|
+
const { txId, consumed, remaining } = await client.transactions.consumeAll({
|
|
286
|
+
account: wallet,
|
|
287
|
+
maxNotes: 50, // optional cap
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Swap
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
await client.transactions.swap({
|
|
295
|
+
account: wallet,
|
|
296
|
+
offered: { token: tokenA, amount: 100n },
|
|
297
|
+
requested: { token: tokenB, amount: 50n },
|
|
298
|
+
type: NoteVisibility.Public, // swap-note visibility
|
|
299
|
+
paybackType: NoteVisibility.Private, // payback-note visibility
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Execute (custom scripts)
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
const script = await client.compile.txScript({
|
|
307
|
+
code: scriptMasm,
|
|
308
|
+
libraries: [{ namespace: "my::lib", code: libMasm, linking: "dynamic" }],
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
await client.transactions.execute({
|
|
312
|
+
account: contract,
|
|
313
|
+
script,
|
|
314
|
+
foreignAccounts: [
|
|
315
|
+
publicAccountId, // public - auto-fetched via RPC
|
|
316
|
+
{ id: privateContractId, storage: storageRequirements },
|
|
317
|
+
],
|
|
318
|
+
waitForConfirmation: true,
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Public foreign accounts are auto-fetched** during execution - only private
|
|
323
|
+
foreign accounts must be supplied with their storage requirements.
|
|
324
|
+
|
|
325
|
+
### Preview (dry run)
|
|
326
|
+
|
|
327
|
+
`transactions.preview({ operation: "send" | "mint" | "consume" | "swap" | "custom", ... })`
|
|
328
|
+
runs the same kernel as the real call but without proving or submitting,
|
|
329
|
+
returning a summary suitable for UI confirmation screens.
|
|
330
|
+
|
|
331
|
+
## Notes
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
await client.notes.list(); // all input notes
|
|
335
|
+
await client.notes.list({ status: "committed" }); // filter
|
|
336
|
+
await client.notes.get(noteId); // single record
|
|
337
|
+
await client.notes.listSent(); // output notes
|
|
338
|
+
await client.notes.listAvailable({ account: wallet });// consumable for an account
|
|
339
|
+
|
|
340
|
+
// Import/export
|
|
341
|
+
await client.notes.import(noteFile);
|
|
342
|
+
const file = await client.notes.export(noteId);
|
|
343
|
+
|
|
344
|
+
// Private-note transport
|
|
345
|
+
await client.notes.fetchPrivate(); // pulls anything addressed to tracked accounts
|
|
346
|
+
await client.notes.sendPrivate({ note, to: addr }); // delivers via the transport service
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Accounts (querying)
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
await client.accounts.list(); // tracked accounts
|
|
353
|
+
await client.accounts.get(ref); // single (returns null if not tracked)
|
|
354
|
+
await client.accounts.getOrImport(ref); // tries get(), falls back to import()
|
|
355
|
+
await client.accounts.getDetails(ref); // header + status + vault summary
|
|
356
|
+
await client.accounts.insert({ account, overwrite }); // start tracking an existing account
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
For balance reads, use `getDetails` or - if you need a single asset balance
|
|
360
|
+
without loading the full vault - drop into the underlying WASM client's
|
|
361
|
+
`accountReader(id)` lazy reader.
|
|
362
|
+
|
|
363
|
+
## Keystore
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
await client.keystore.insert(accountId, secretKey);
|
|
367
|
+
await client.keystore.get(pubKeyCommitment);
|
|
368
|
+
await client.keystore.remove(pubKeyCommitment);
|
|
369
|
+
await client.keystore.getCommitments(accountId);
|
|
370
|
+
await client.keystore.getAccountId(pubKeyCommitment);
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
`keystore.insert` is the single call that both stores the key and registers
|
|
374
|
+
its commitment with the account.
|
|
375
|
+
|
|
376
|
+
## Compile
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
await client.compile.component({ code, slots, supportAllTypes: true }); // supportAllTypes defaults to true (api-types.d.ts:784); set false if your component supplies its own auth-tx kernel invocation.
|
|
380
|
+
await client.compile.txScript({ code, libraries });
|
|
381
|
+
await client.compile.noteScript({ code, libraries });
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Note scripts are **MASM libraries with a single `@note_script`-annotated
|
|
385
|
+
procedure**, not begin/end programs - `client.compile.noteScript` builds the
|
|
386
|
+
correct shape from a procedure body. The same applies to `@auth_script` for
|
|
387
|
+
authentication scripts.
|
|
388
|
+
|
|
389
|
+
## Common Workflows
|
|
390
|
+
|
|
391
|
+
### Mint and consume (fund a fresh wallet)
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
const wallet = await client.accounts.create();
|
|
395
|
+
const faucet = await client.accounts.create({
|
|
396
|
+
type: AccountType.FungibleFaucet,
|
|
397
|
+
storage: "public",
|
|
398
|
+
symbol: "TEST",
|
|
399
|
+
decimals: 8,
|
|
400
|
+
maxSupply: 1_000_000n,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
await client.transactions.mint({
|
|
404
|
+
account: faucet,
|
|
405
|
+
to: wallet,
|
|
406
|
+
amount: 10_000n,
|
|
407
|
+
type: NoteVisibility.Public,
|
|
408
|
+
waitForConfirmation: true,
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
await client.sync();
|
|
412
|
+
await client.transactions.consumeAll({
|
|
413
|
+
account: wallet,
|
|
414
|
+
waitForConfirmation: true,
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Wait for an external transfer
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
await client.sync();
|
|
422
|
+
const before = (await client.notes.listAvailable({ account: wallet })).length;
|
|
423
|
+
|
|
424
|
+
while (true) {
|
|
425
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
426
|
+
await client.sync();
|
|
427
|
+
const now = (await client.notes.listAvailable({ account: wallet })).length;
|
|
428
|
+
if (now > before) break;
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Common Pitfalls
|
|
433
|
+
|
|
434
|
+
1. **Forgetting to sync.** Notes won't appear, balances will be stale, foreign
|
|
435
|
+
accounts will be at the wrong block.
|
|
436
|
+
2. **`number` instead of `BigInt`** for amounts - silent precision loss.
|
|
437
|
+
3. **Passing strings where the SDK expects an account ref** - pre-parse with
|
|
438
|
+
`AccountId.fromHex()` (and catch its throw).
|
|
439
|
+
4. **Consuming notes before they're committed** - sync first, check status.
|
|
440
|
+
5. **Submitting `mint` with the recipient as `account`** - mint executes on
|
|
441
|
+
the faucet account, not the target.
|
|
442
|
+
6. **Private notes without transport** - must call `notes.sendPrivate()` (or
|
|
443
|
+
pass `returnNote: true` to `transactions.send` and deliver out-of-band).
|
|
444
|
+
7. **Holding WASM-owned objects across `terminate()`** - every `Account`,
|
|
445
|
+
`Note`, `AccountId`, `NoteAndArgsArray` etc. owns Rust memory through the
|
|
446
|
+
WASM ArrayBuffer. After `terminate()` they panic with "null pointer
|
|
447
|
+
passed to rust" - drop references on unmount.
|
|
448
|
+
8. **Calling `accountReader(...)` in parallel with a write** (the method lives
|
|
449
|
+
on the raw `WasmWebClient`, accessed via React's `useMidenClient()` or a
|
|
450
|
+
direct `WasmWebClient` import; not on `MidenClient`) - the readers share
|
|
451
|
+
the WASM client. Wrap concurrent flows with `client.waitForIdle()` or rely
|
|
452
|
+
on the React SDK's `runExclusive`. For multiple browser clients or tabs,
|
|
453
|
+
give each isolated client a distinct `storeName`; see the initialization
|
|
454
|
+
examples above and `signer-integration` for per-user store isolation.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Miden SDK network configuration
|
|
2
|
+
# Supported values: devnet | testnet | local | https://your-rpc-url
|
|
3
|
+
VITE_MIDEN_RPC_URL=testnet
|
|
4
|
+
|
|
5
|
+
# Prover configuration
|
|
6
|
+
# Supported values: devnet | testnet | local | https://your-prover-url
|
|
7
|
+
VITE_MIDEN_PROVER=testnet
|
|
8
|
+
|
|
9
|
+
# Counter contract address (bech32, e.g. mtst1...).
|
|
10
|
+
#
|
|
11
|
+
# Resolution rules:
|
|
12
|
+
# - Leave this line commented out / unset → app uses the live testnet
|
|
13
|
+
# default counter shipped with the template (recommended for first-run).
|
|
14
|
+
# - Set to "" (empty string) → unconfigured; <Counter> shows the
|
|
15
|
+
# "address not configured" card (useful when you've deployed nothing
|
|
16
|
+
# yet and don't want network calls).
|
|
17
|
+
# - Set to your own bech32 address → app reads from that counter.
|
|
18
|
+
# VITE_MIDEN_COUNTER_ADDRESS=
|