@miden-sdk/react 0.13.0
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/README.md +1031 -0
- package/dist/index.d.mts +927 -0
- package/dist/index.d.ts +927 -0
- package/dist/index.js +2214 -0
- package/dist/index.mjs +2198 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,1031 @@
|
|
|
1
|
+
# @miden-sdk/react
|
|
2
|
+
|
|
3
|
+
React hooks library for the Miden Web Client. Provides a simple, ergonomic interface for building React applications on the Miden rollup.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Easy Setup** - Single provider component handles WASM initialization and client setup
|
|
8
|
+
- **Sensible Defaults** - Privacy-first defaults that work out of the box
|
|
9
|
+
- **Auto-Sync** - Automatic background synchronization with the network
|
|
10
|
+
- **TypeScript First** - Full type safety with comprehensive type exports
|
|
11
|
+
- **Consistent Patterns** - All hooks follow predictable patterns for loading, errors, and state
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @miden-sdk/react @miden-sdk/miden-sdk
|
|
17
|
+
# or
|
|
18
|
+
yarn add @miden-sdk/react @miden-sdk/miden-sdk
|
|
19
|
+
# or
|
|
20
|
+
pnpm add @miden-sdk/react @miden-sdk/miden-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Testing
|
|
24
|
+
|
|
25
|
+
From `packages/react-sdk`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Unit tests
|
|
29
|
+
yarn test:unit
|
|
30
|
+
|
|
31
|
+
# Integration tests (Playwright) in test/
|
|
32
|
+
# Build the web-client dist first:
|
|
33
|
+
cd ../../crates/web-client && yarn build
|
|
34
|
+
cd ../../packages/react-sdk
|
|
35
|
+
yarn playwright install --with-deps
|
|
36
|
+
yarn test:integration
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
Wrap your app with `MidenProvider` and start using hooks:
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { MidenProvider, useMiden, useCreateWallet, useAccounts } from '@miden-sdk/react';
|
|
45
|
+
|
|
46
|
+
function App() {
|
|
47
|
+
return (
|
|
48
|
+
<MidenProvider>
|
|
49
|
+
<Wallet />
|
|
50
|
+
</MidenProvider>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function Wallet() {
|
|
55
|
+
const { isReady } = useMiden();
|
|
56
|
+
const { wallets, isLoading } = useAccounts();
|
|
57
|
+
const { createWallet, isCreating } = useCreateWallet();
|
|
58
|
+
|
|
59
|
+
if (!isReady) return <div>Initializing Miden...</div>;
|
|
60
|
+
if (isLoading) return <div>Loading accounts...</div>;
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div>
|
|
64
|
+
<h2>My Wallets ({wallets.length})</h2>
|
|
65
|
+
<ul>
|
|
66
|
+
{wallets.map(wallet => (
|
|
67
|
+
<li key={wallet.id().toString()}>
|
|
68
|
+
{wallet.id().toString()}
|
|
69
|
+
</li>
|
|
70
|
+
))}
|
|
71
|
+
</ul>
|
|
72
|
+
|
|
73
|
+
<button onClick={() => createWallet()} disabled={isCreating}>
|
|
74
|
+
{isCreating ? 'Creating...' : 'Create Wallet'}
|
|
75
|
+
</button>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Provider Configuration
|
|
82
|
+
|
|
83
|
+
The `MidenProvider` handles WASM initialization and client setup:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { MidenProvider } from '@miden-sdk/react';
|
|
87
|
+
|
|
88
|
+
function App() {
|
|
89
|
+
return (
|
|
90
|
+
<MidenProvider
|
|
91
|
+
config={{
|
|
92
|
+
// RPC endpoint (defaults to testnet). You can also use 'devnet' or 'testnet'.
|
|
93
|
+
rpcUrl: 'devnet',
|
|
94
|
+
|
|
95
|
+
// Auto-sync interval in milliseconds (default: 15000)
|
|
96
|
+
// Set to 0 to disable auto-sync
|
|
97
|
+
autoSyncInterval: 15000,
|
|
98
|
+
|
|
99
|
+
// Optional: prover selection ('local' | 'devnet' | 'testnet' | URL)
|
|
100
|
+
// prover: 'local',
|
|
101
|
+
}}
|
|
102
|
+
// Optional: Custom loading component
|
|
103
|
+
loadingComponent={<div>Loading Miden...</div>}
|
|
104
|
+
|
|
105
|
+
// Optional: Custom error component
|
|
106
|
+
errorComponent={(error) => (
|
|
107
|
+
<div>Failed to initialize: {error.message}</div>
|
|
108
|
+
)}
|
|
109
|
+
>
|
|
110
|
+
<YourApp />
|
|
111
|
+
</MidenProvider>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Hooks Reference
|
|
117
|
+
|
|
118
|
+
### Core Hooks
|
|
119
|
+
|
|
120
|
+
**ID formats:** All hooks that accept account IDs or asset IDs accept both
|
|
121
|
+
bech32 and hex strings (auto-detected).
|
|
122
|
+
|
|
123
|
+
#### `useMiden()`
|
|
124
|
+
|
|
125
|
+
Access the Miden client instance and initialization state. This is your entry
|
|
126
|
+
point for low-level control (syncing, direct client access, and prover-aware
|
|
127
|
+
transactions) while still playing nicely with the provider lifecycle. It
|
|
128
|
+
centralizes readiness and error handling so you avoid sprinkling guards across
|
|
129
|
+
components. You also get a single place to hook sync and exclusive access
|
|
130
|
+
without wiring your own locks.
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { useMiden } from '@miden-sdk/react';
|
|
134
|
+
|
|
135
|
+
function MyComponent() {
|
|
136
|
+
const {
|
|
137
|
+
client, // WebClient instance (null if not ready)
|
|
138
|
+
isReady, // true when client is initialized
|
|
139
|
+
error, // Initialization error if any
|
|
140
|
+
sync, // Function to trigger manual sync
|
|
141
|
+
} = useMiden();
|
|
142
|
+
|
|
143
|
+
if (error) {
|
|
144
|
+
return <div>Error: {error.message}</div>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!isReady) {
|
|
148
|
+
return <div>Initializing...</div>;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return <div>Connected! Block height: {/* ... */}</div>;
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### `useMidenClient()`
|
|
156
|
+
|
|
157
|
+
Get the ready `WebClient` instance directly. It’s a convenience for advanced
|
|
158
|
+
flows where you want to call SDK methods yourself without re-checking readiness
|
|
159
|
+
every time; it throws if the client isn't ready yet. This keeps your component
|
|
160
|
+
logic clean by avoiding boilerplate null checks. You still benefit from the
|
|
161
|
+
provider lifecycle while opting into raw SDK control.
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
import { useMidenClient } from '@miden-sdk/react';
|
|
165
|
+
|
|
166
|
+
function MyComponent() {
|
|
167
|
+
const client = useMidenClient();
|
|
168
|
+
// Safe to use client here (initialized)
|
|
169
|
+
return <div>Client ready</div>;
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `useSyncState()`
|
|
174
|
+
|
|
175
|
+
Monitor network sync status and trigger manual syncs. Useful for UI indicators,
|
|
176
|
+
pull-to-refresh, or forcing a sync before running a transaction pipeline. It
|
|
177
|
+
wraps the shared sync lock so multiple components don't stampede the node. You
|
|
178
|
+
get consistent timestamps and error state without wiring your own timers.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { useSyncState } from '@miden-sdk/react';
|
|
182
|
+
|
|
183
|
+
function SyncStatus() {
|
|
184
|
+
const {
|
|
185
|
+
syncHeight, // Current synced block height
|
|
186
|
+
isSyncing, // true during sync operation
|
|
187
|
+
lastSyncTime, // Timestamp of last successful sync
|
|
188
|
+
error, // Sync error if any
|
|
189
|
+
sync, // Function to trigger manual sync
|
|
190
|
+
} = useSyncState();
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<div>
|
|
194
|
+
<p>Block Height: {syncHeight}</p>
|
|
195
|
+
<p>Last Sync: {lastSyncTime ? new Date(lastSyncTime).toLocaleString() : 'Never'}</p>
|
|
196
|
+
<button onClick={sync} disabled={isSyncing}>
|
|
197
|
+
{isSyncing ? 'Syncing...' : 'Sync Now'}
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Account Hooks
|
|
205
|
+
|
|
206
|
+
#### `useAccounts()`
|
|
207
|
+
|
|
208
|
+
List all accounts tracked by the local client, automatically categorized into
|
|
209
|
+
wallets and faucets. Great for dashboards, account pickers, and quick summaries
|
|
210
|
+
without extra filtering logic. It hides the bit twiddling needed to recognize
|
|
211
|
+
faucet IDs. Refetch runs through the same sync-safe path so you avoid stale
|
|
212
|
+
caches or double fetches.
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
import { useAccounts } from '@miden-sdk/react';
|
|
216
|
+
|
|
217
|
+
function AccountList() {
|
|
218
|
+
const {
|
|
219
|
+
accounts, // All accounts
|
|
220
|
+
wallets, // Regular wallet accounts
|
|
221
|
+
faucets, // Faucet accounts
|
|
222
|
+
isLoading, // Loading state
|
|
223
|
+
error, // Error if fetch failed
|
|
224
|
+
refetch, // Function to refresh the list
|
|
225
|
+
} = useAccounts();
|
|
226
|
+
|
|
227
|
+
if (isLoading) return <div>Loading...</div>;
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<div>
|
|
231
|
+
<h2>Wallets ({wallets.length})</h2>
|
|
232
|
+
{wallets.map(w => (
|
|
233
|
+
<div key={w.id().toString()}>
|
|
234
|
+
{w.id().toString()}
|
|
235
|
+
</div>
|
|
236
|
+
))}
|
|
237
|
+
|
|
238
|
+
<h2>Faucets ({faucets.length})</h2>
|
|
239
|
+
{faucets.map(f => (
|
|
240
|
+
<div key={f.id().toString()}>
|
|
241
|
+
{f.id().toString()}
|
|
242
|
+
</div>
|
|
243
|
+
))}
|
|
244
|
+
|
|
245
|
+
<button onClick={refetch}>Refresh</button>
|
|
246
|
+
</div>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### `useAccount(accountId)`
|
|
252
|
+
|
|
253
|
+
Get detailed information for a single account, including assets. The hook
|
|
254
|
+
hydrates balances with token metadata (symbol/decimals) and keeps data fresh
|
|
255
|
+
after syncs. It spares you from manual vault parsing and metadata joins.
|
|
256
|
+
Balances update automatically with sync so UI stays in step.
|
|
257
|
+
|
|
258
|
+
```tsx
|
|
259
|
+
import { useAccount } from '@miden-sdk/react';
|
|
260
|
+
|
|
261
|
+
function AccountDetails({ accountId }: { accountId: string }) {
|
|
262
|
+
const {
|
|
263
|
+
account, // Full account object
|
|
264
|
+
assets, // Array of { assetId, amount, symbol?, decimals? } balances
|
|
265
|
+
isLoading,
|
|
266
|
+
error,
|
|
267
|
+
refetch,
|
|
268
|
+
getBalance, // Helper to get balance for specific asset
|
|
269
|
+
} = useAccount(accountId);
|
|
270
|
+
|
|
271
|
+
if (isLoading) return <div>Loading...</div>;
|
|
272
|
+
if (!account) return <div>Account not found</div>;
|
|
273
|
+
|
|
274
|
+
// Get balance for a specific token
|
|
275
|
+
const usdcBalance = getBalance('0xasset123...');
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<div>
|
|
279
|
+
<h2>Account: {account.id().toString()}</h2>
|
|
280
|
+
<p>Nonce: {account.nonce().toString()}</p>
|
|
281
|
+
|
|
282
|
+
<h3>Assets</h3>
|
|
283
|
+
{assets.map(asset => (
|
|
284
|
+
<div key={asset.assetId}>
|
|
285
|
+
{asset.symbol ?? asset.assetId}: {asset.amount.toString()}
|
|
286
|
+
</div>
|
|
287
|
+
))}
|
|
288
|
+
|
|
289
|
+
<p>USDC Balance: {usdcBalance.toString()}</p>
|
|
290
|
+
</div>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
#### `useCreateWallet()`
|
|
296
|
+
|
|
297
|
+
Create new wallet accounts. Supports storage mode, mutability, and auth scheme
|
|
298
|
+
so you can quickly spin up accounts for demos or customize for production needs.
|
|
299
|
+
It wraps ID parsing and defaults so you can start with a one-liner. The hook
|
|
300
|
+
also tracks creation state so you can wire UI without extra reducers.
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
import { useCreateWallet } from '@miden-sdk/react';
|
|
304
|
+
|
|
305
|
+
function CreateWalletButton() {
|
|
306
|
+
const {
|
|
307
|
+
createWallet, // Function to create wallet
|
|
308
|
+
wallet, // Created wallet (after success)
|
|
309
|
+
isCreating, // Loading state
|
|
310
|
+
error, // Error if creation failed
|
|
311
|
+
reset, // Reset state for new creation
|
|
312
|
+
} = useCreateWallet();
|
|
313
|
+
|
|
314
|
+
const handleCreate = async () => {
|
|
315
|
+
try {
|
|
316
|
+
// With defaults (private storage, mutable, Falcon auth)
|
|
317
|
+
const newWallet = await createWallet();
|
|
318
|
+
console.log('Created wallet:', newWallet.id().toString());
|
|
319
|
+
|
|
320
|
+
// Or with custom options
|
|
321
|
+
const customWallet = await createWallet({
|
|
322
|
+
storageMode: 'private', // 'private' | 'public' | 'network'
|
|
323
|
+
mutable: true, // Allow code updates
|
|
324
|
+
authScheme: 0, // 0 = Falcon (default), 1 = ECDSA
|
|
325
|
+
});
|
|
326
|
+
} catch (err) {
|
|
327
|
+
console.error('Failed to create wallet:', err);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<div>
|
|
333
|
+
{error && (
|
|
334
|
+
<div>
|
|
335
|
+
Error: {error.message}
|
|
336
|
+
<button onClick={reset}>Try Again</button>
|
|
337
|
+
</div>
|
|
338
|
+
)}
|
|
339
|
+
|
|
340
|
+
<button onClick={handleCreate} disabled={isCreating}>
|
|
341
|
+
{isCreating ? 'Creating...' : 'Create Wallet'}
|
|
342
|
+
</button>
|
|
343
|
+
|
|
344
|
+
{wallet && <div>Created: {wallet.id().toString()}</div>}
|
|
345
|
+
</div>
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### `useCreateFaucet()`
|
|
351
|
+
|
|
352
|
+
Create new faucets for minting tokens. Ideal for dev/test flows where you need
|
|
353
|
+
a controlled token source and quick bootstrap of balances.
|
|
354
|
+
It handles storage/auth defaults and returns a ready faucet object. That
|
|
355
|
+
removes the usual setup friction when you just want tokens to exist.
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
import { useCreateFaucet } from '@miden-sdk/react';
|
|
359
|
+
|
|
360
|
+
function CreateFaucetForm() {
|
|
361
|
+
const { createFaucet, faucet, isCreating, error, reset } = useCreateFaucet();
|
|
362
|
+
|
|
363
|
+
const handleCreate = async () => {
|
|
364
|
+
try {
|
|
365
|
+
const newFaucet = await createFaucet({
|
|
366
|
+
tokenSymbol: 'USDC', // 1-4 character symbol
|
|
367
|
+
decimals: 6, // Token decimals (default: 8)
|
|
368
|
+
maxSupply: 1000000000n * 10n**6n, // Max supply in smallest units
|
|
369
|
+
storageMode: 'private', // Optional (default: 'private')
|
|
370
|
+
authScheme: 0, // Optional (default: 0 = Falcon)
|
|
371
|
+
});
|
|
372
|
+
console.log('Created faucet:', newFaucet.id().toString());
|
|
373
|
+
} catch (err) {
|
|
374
|
+
console.error('Failed:', err);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
return (
|
|
379
|
+
<div>
|
|
380
|
+
{error && <div>Error: {error.message}</div>}
|
|
381
|
+
<button onClick={handleCreate} disabled={isCreating}>
|
|
382
|
+
{isCreating ? 'Creating...' : 'Create USDC Faucet'}
|
|
383
|
+
</button>
|
|
384
|
+
</div>
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### `useImportAccount()`
|
|
390
|
+
|
|
391
|
+
Import an existing account into the client. This lets you start tracking an
|
|
392
|
+
on-chain account by ID, or restore a private account from a file/seed.
|
|
393
|
+
The hook normalizes ID formats and hides SDK branching. It also exposes the
|
|
394
|
+
imported account so you can update UI immediately.
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
import { useImportAccount } from '@miden-sdk/react';
|
|
398
|
+
|
|
399
|
+
function ImportAccountButton({ accountId }: { accountId: string }) {
|
|
400
|
+
const { importAccount, account, isImporting, error, reset } = useImportAccount();
|
|
401
|
+
|
|
402
|
+
const handleImport = async () => {
|
|
403
|
+
await importAccount({ type: 'id', accountId });
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
return (
|
|
407
|
+
<button onClick={handleImport} disabled={isImporting}>
|
|
408
|
+
{isImporting ? 'Importing...' : 'Import Account'}
|
|
409
|
+
</button>
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Note Hooks
|
|
415
|
+
|
|
416
|
+
#### `useNotes(options?)`
|
|
417
|
+
|
|
418
|
+
List and filter notes (incoming transactions). Includes consumable notes and
|
|
419
|
+
optional summaries that bundle asset metadata so you can render balances and
|
|
420
|
+
labels without extra lookups. It syncs notes after successful syncs so you
|
|
421
|
+
don't have to wire listeners. Summaries reduce boilerplate around asset
|
|
422
|
+
metadata and formatting.
|
|
423
|
+
|
|
424
|
+
```tsx
|
|
425
|
+
import { useNotes } from '@miden-sdk/react';
|
|
426
|
+
|
|
427
|
+
function NotesList() {
|
|
428
|
+
const {
|
|
429
|
+
notes, // All notes matching filter
|
|
430
|
+
consumableNotes, // Notes ready to be consumed
|
|
431
|
+
noteSummaries, // Summary objects with asset metadata
|
|
432
|
+
isLoading,
|
|
433
|
+
error,
|
|
434
|
+
refetch,
|
|
435
|
+
} = useNotes();
|
|
436
|
+
|
|
437
|
+
// With filtering options
|
|
438
|
+
const { notes: committedNotes } = useNotes({
|
|
439
|
+
status: 'committed', // 'all' | 'consumed' | 'committed' | 'expected' | 'processing'
|
|
440
|
+
accountId: '0x...', // Filter by account
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
return (
|
|
444
|
+
<div>
|
|
445
|
+
<h2>Consumable Notes ({consumableNotes.length})</h2>
|
|
446
|
+
{consumableNotes.map(note => (
|
|
447
|
+
<div key={note.id().toString()}>
|
|
448
|
+
{note.id().toString()}
|
|
449
|
+
</div>
|
|
450
|
+
))}
|
|
451
|
+
|
|
452
|
+
<h2>Note Summaries</h2>
|
|
453
|
+
{noteSummaries.map(summary => (
|
|
454
|
+
<div key={summary.id}>
|
|
455
|
+
{summary.id} — {summary.assets.map(a => `${a.amount} ${a.symbol ?? a.assetId}`).join(', ')}
|
|
456
|
+
</div>
|
|
457
|
+
))}
|
|
458
|
+
</div>
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
#### `useAssetMetadata(assetIds)`
|
|
464
|
+
|
|
465
|
+
Fetch asset symbols/decimals for a list of asset IDs. This is the lightweight
|
|
466
|
+
way to enrich balances and note lists with human-friendly token info.
|
|
467
|
+
It batches lookups and caches results for reuse across components. That avoids
|
|
468
|
+
repeated RPC calls and inconsistent labels.
|
|
469
|
+
|
|
470
|
+
```tsx
|
|
471
|
+
import { useAssetMetadata } from '@miden-sdk/react';
|
|
472
|
+
|
|
473
|
+
function AssetLabels({ assetIds }: { assetIds: string[] }) {
|
|
474
|
+
const { assetMetadata } = useAssetMetadata(assetIds);
|
|
475
|
+
return (
|
|
476
|
+
<ul>
|
|
477
|
+
{assetIds.map((id) => {
|
|
478
|
+
const meta = assetMetadata.get(id);
|
|
479
|
+
return (
|
|
480
|
+
<li key={id}>
|
|
481
|
+
{id} — {meta?.symbol ?? 'UNKNOWN'} ({meta?.decimals ?? 0})
|
|
482
|
+
</li>
|
|
483
|
+
);
|
|
484
|
+
})}
|
|
485
|
+
</ul>
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Transaction Hooks
|
|
491
|
+
|
|
492
|
+
All transaction hooks follow a consistent pattern with `stage` tracking:
|
|
493
|
+
|
|
494
|
+
| Stage | Description |
|
|
495
|
+
|-------|-------------|
|
|
496
|
+
| `'idle'` | Not started |
|
|
497
|
+
| `'executing'` | Building/executing request |
|
|
498
|
+
| `'proving'` | Generating ZK proof |
|
|
499
|
+
| `'submitting'` | Submitting to network |
|
|
500
|
+
| `'complete'` | Transaction confirmed |
|
|
501
|
+
|
|
502
|
+
#### `useSend()`
|
|
503
|
+
|
|
504
|
+
Send tokens from one account to another. Handles the full lifecycle (execute,
|
|
505
|
+
prove, submit, apply) and delivers private notes automatically when needed.
|
|
506
|
+
It collapses the multi-step transaction pipeline into a single call with stage
|
|
507
|
+
tracking. You get private note delivery without having to remember the extra
|
|
508
|
+
send step.
|
|
509
|
+
|
|
510
|
+
```tsx
|
|
511
|
+
import { useSend } from '@miden-sdk/react';
|
|
512
|
+
|
|
513
|
+
function SendForm() {
|
|
514
|
+
const {
|
|
515
|
+
send, // Function to execute send
|
|
516
|
+
result, // { transactionId } after success
|
|
517
|
+
isLoading, // true during transaction
|
|
518
|
+
stage, // Current stage
|
|
519
|
+
error,
|
|
520
|
+
reset,
|
|
521
|
+
} = useSend();
|
|
522
|
+
|
|
523
|
+
const handleSend = async () => {
|
|
524
|
+
try {
|
|
525
|
+
const { transactionId } = await send({
|
|
526
|
+
from: '0xsender...', // Sender account ID
|
|
527
|
+
to: '0xrecipient...', // Recipient account ID
|
|
528
|
+
assetId: '0xtoken...', // Asset ID (token id)
|
|
529
|
+
amount: 100n, // Amount in smallest units
|
|
530
|
+
|
|
531
|
+
// Optional parameters
|
|
532
|
+
noteType: 'private', // 'private' | 'public' | 'encrypted' (default: 'private')
|
|
533
|
+
recallHeight: 1000, // Sender can reclaim after this block
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
console.log('Sent! TX:', transactionId);
|
|
537
|
+
} catch (err) {
|
|
538
|
+
console.error('Send failed:', err);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
return (
|
|
543
|
+
<div>
|
|
544
|
+
{error && <div>Error: {error.message}</div>}
|
|
545
|
+
|
|
546
|
+
<button onClick={handleSend} disabled={isLoading}>
|
|
547
|
+
{isLoading ? `Sending (${stage})...` : 'Send Tokens'}
|
|
548
|
+
</button>
|
|
549
|
+
|
|
550
|
+
{result && <div>Success! TX: {result.transactionId}</div>}
|
|
551
|
+
</div>
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
#### `useMultiSend()`
|
|
557
|
+
|
|
558
|
+
Create multiple P2ID output notes in a single transaction. This is ideal for
|
|
559
|
+
batched payouts or airdrops; with `noteType: 'private'`, the hook also delivers
|
|
560
|
+
each note to recipients via `sendPrivateNote`.
|
|
561
|
+
It builds the request and executes the full pipeline in one go. That means
|
|
562
|
+
fewer chances to handle batching incorrectly or forget private note delivery.
|
|
563
|
+
|
|
564
|
+
```tsx
|
|
565
|
+
import { useMultiSend } from '@miden-sdk/react';
|
|
566
|
+
|
|
567
|
+
function MultiSendButton() {
|
|
568
|
+
const { sendMany, isLoading, stage } = useMultiSend();
|
|
569
|
+
|
|
570
|
+
const handleSend = async () => {
|
|
571
|
+
await sendMany({
|
|
572
|
+
from: '0xsender...',
|
|
573
|
+
assetId: '0xtoken...',
|
|
574
|
+
recipients: [
|
|
575
|
+
{ to: '0xrec1...', amount: 100n },
|
|
576
|
+
{ to: '0xrec2...', amount: 250n },
|
|
577
|
+
],
|
|
578
|
+
noteType: 'public',
|
|
579
|
+
});
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
return (
|
|
583
|
+
<button onClick={handleSend} disabled={isLoading}>
|
|
584
|
+
{isLoading ? `Sending (${stage})...` : 'Multi-Send'}
|
|
585
|
+
</button>
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
#### `useInternalTransfer()`
|
|
591
|
+
|
|
592
|
+
Create a P2ID note and immediately consume it. This is useful for transfers
|
|
593
|
+
between accounts you control (e.g., public → private), since the receiver must
|
|
594
|
+
be available in the local client to consume the note.
|
|
595
|
+
It abstracts the two-transaction flow and keeps both steps tied to a single UI
|
|
596
|
+
action. You don't have to juggle temporary note IDs or manual syncs.
|
|
597
|
+
|
|
598
|
+
```tsx
|
|
599
|
+
import { useInternalTransfer } from '@miden-sdk/react';
|
|
600
|
+
|
|
601
|
+
function InternalTransferButton() {
|
|
602
|
+
const { transfer, isLoading, stage } = useInternalTransfer();
|
|
603
|
+
|
|
604
|
+
const handleTransfer = async () => {
|
|
605
|
+
await transfer({
|
|
606
|
+
from: '0xsender...',
|
|
607
|
+
to: '0xrecipient...',
|
|
608
|
+
assetId: '0xtoken...',
|
|
609
|
+
amount: 50n,
|
|
610
|
+
noteType: 'public',
|
|
611
|
+
});
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
return (
|
|
615
|
+
<button onClick={handleTransfer} disabled={isLoading}>
|
|
616
|
+
{isLoading ? `Transferring (${stage})...` : 'Transfer'}
|
|
617
|
+
</button>
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
#### `useWaitForCommit()`
|
|
623
|
+
|
|
624
|
+
Wait for a transaction to be committed. Useful for gating UI transitions,
|
|
625
|
+
follow-up actions, or polling-driven workflows that depend on finality.
|
|
626
|
+
It handles sync + polling loops and the discarded/timeout edge cases. So you
|
|
627
|
+
can keep UI logic simple and avoid bespoke timers.
|
|
628
|
+
|
|
629
|
+
```tsx
|
|
630
|
+
import { useWaitForCommit } from '@miden-sdk/react';
|
|
631
|
+
|
|
632
|
+
function WaitForTx({ txId }: { txId: string }) {
|
|
633
|
+
const { waitForCommit } = useWaitForCommit();
|
|
634
|
+
|
|
635
|
+
const handleWait = async () => {
|
|
636
|
+
await waitForCommit(txId, { timeoutMs: 10_000, intervalMs: 1_000 });
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
return <button onClick={handleWait}>Wait for Commit</button>;
|
|
640
|
+
}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
#### `useTransactionHistory()`
|
|
644
|
+
|
|
645
|
+
Query transaction history and get live state for a single transaction. You can
|
|
646
|
+
pass a single ID, multiple IDs, or a custom `TransactionFilter`. Results refresh
|
|
647
|
+
after each successful sync by default.
|
|
648
|
+
It hides the filter plumbing and provides a single-record convenience view.
|
|
649
|
+
The hook keeps lists fresh on sync so you don't wire manual refreshes.
|
|
650
|
+
|
|
651
|
+
```tsx
|
|
652
|
+
import { useTransactionHistory } from '@miden-sdk/react';
|
|
653
|
+
|
|
654
|
+
function TxHistory() {
|
|
655
|
+
const { records, isLoading, refetch } = useTransactionHistory();
|
|
656
|
+
|
|
657
|
+
if (isLoading) return <div>Loading...</div>;
|
|
658
|
+
|
|
659
|
+
return (
|
|
660
|
+
<div>
|
|
661
|
+
<button onClick={refetch}>Refresh</button>
|
|
662
|
+
<ul>
|
|
663
|
+
{records.map((record) => (
|
|
664
|
+
<li key={record.id().toHex()}>{record.id().toHex()}</li>
|
|
665
|
+
))}
|
|
666
|
+
</ul>
|
|
667
|
+
</div>
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
```tsx
|
|
673
|
+
import { useTransactionHistory } from '@miden-sdk/react';
|
|
674
|
+
|
|
675
|
+
function TxStatus({ txId }: { txId: string }) {
|
|
676
|
+
const { record, status } = useTransactionHistory({ id: txId });
|
|
677
|
+
|
|
678
|
+
if (!record) return <div>Not found</div>;
|
|
679
|
+
|
|
680
|
+
return (
|
|
681
|
+
<div>
|
|
682
|
+
{record.id().toHex()} → {status}
|
|
683
|
+
</div>
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
#### `useWaitForNotes()`
|
|
689
|
+
|
|
690
|
+
Wait until an account has consumable notes. Great for mint → consume pipelines
|
|
691
|
+
and other flows where you want to proceed only when notes are ready.
|
|
692
|
+
It wraps the poll/sync loop and returns notes once the threshold is met. That
|
|
693
|
+
keeps workflows linear without ad-hoc sleeps.
|
|
694
|
+
|
|
695
|
+
```tsx
|
|
696
|
+
import { useWaitForNotes } from '@miden-sdk/react';
|
|
697
|
+
|
|
698
|
+
function WaitForNotes({ accountId }: { accountId: string }) {
|
|
699
|
+
const { waitForConsumableNotes } = useWaitForNotes();
|
|
700
|
+
|
|
701
|
+
const handleWait = async () => {
|
|
702
|
+
const notes = await waitForConsumableNotes({
|
|
703
|
+
accountId,
|
|
704
|
+
minCount: 1,
|
|
705
|
+
timeoutMs: 10_000,
|
|
706
|
+
intervalMs: 1_000,
|
|
707
|
+
});
|
|
708
|
+
console.log('Notes ready:', notes.length);
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
return <button onClick={handleWait}>Wait for Notes</button>;
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
#### `useMint()`
|
|
716
|
+
|
|
717
|
+
Mint new tokens from a faucet you control. The hook handles the full tx pipeline
|
|
718
|
+
and is perfect for quickly funding accounts in dev/test environments.
|
|
719
|
+
Defaults and ID parsing keep the call small while still letting you tune note
|
|
720
|
+
type. It also tracks stages so you can surface progress without extra state.
|
|
721
|
+
|
|
722
|
+
```tsx
|
|
723
|
+
import { useMint } from '@miden-sdk/react';
|
|
724
|
+
|
|
725
|
+
function MintForm() {
|
|
726
|
+
const { mint, result, isLoading, stage, error, reset } = useMint();
|
|
727
|
+
|
|
728
|
+
const handleMint = async () => {
|
|
729
|
+
try {
|
|
730
|
+
const { transactionId } = await mint({
|
|
731
|
+
faucetId: '0xmyfaucet...', // Your faucet ID
|
|
732
|
+
targetAccountId: '0xwallet...', // Recipient wallet
|
|
733
|
+
amount: 1000n * 10n**8n, // Amount to mint
|
|
734
|
+
noteType: 'private', // Optional: 'private' | 'public' | 'encrypted'
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
console.log('Minted! TX:', transactionId);
|
|
738
|
+
} catch (err) {
|
|
739
|
+
console.error('Mint failed:', err);
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
return (
|
|
744
|
+
<button onClick={handleMint} disabled={isLoading}>
|
|
745
|
+
{isLoading ? `Minting (${stage})...` : 'Mint 1000 Tokens'}
|
|
746
|
+
</button>
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### `useConsume()`
|
|
752
|
+
|
|
753
|
+
Consume notes to claim tokens sent to your account. Supports multiple note IDs
|
|
754
|
+
and handles proof generation and submission automatically.
|
|
755
|
+
It wraps the multi-step consume pipeline so you don't have to string calls
|
|
756
|
+
together. That prevents common snags like forgetting to sync or submit.
|
|
757
|
+
|
|
758
|
+
```tsx
|
|
759
|
+
import { useConsume } from '@miden-sdk/react';
|
|
760
|
+
|
|
761
|
+
function ConsumeNotes() {
|
|
762
|
+
const { consume, result, isLoading, stage, error, reset } = useConsume();
|
|
763
|
+
|
|
764
|
+
const handleConsume = async (noteIds: string[]) => {
|
|
765
|
+
try {
|
|
766
|
+
const { transactionId } = await consume({
|
|
767
|
+
accountId: '0xmywallet...', // Your wallet ID
|
|
768
|
+
noteIds: noteIds, // Array of note IDs to consume
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
console.log('Consumed! TX:', transactionId);
|
|
772
|
+
} catch (err) {
|
|
773
|
+
console.error('Consume failed:', err);
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
return (
|
|
778
|
+
<button
|
|
779
|
+
onClick={() => handleConsume(['0xnote1...', '0xnote2...'])}
|
|
780
|
+
disabled={isLoading}
|
|
781
|
+
>
|
|
782
|
+
{isLoading ? `Consuming (${stage})...` : 'Claim Tokens'}
|
|
783
|
+
</button>
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
#### `useSwap()`
|
|
789
|
+
|
|
790
|
+
Create atomic swap offers. Use it to build escrow-style swaps with configurable
|
|
791
|
+
note types for both the swap note and the payback note.
|
|
792
|
+
The hook hides request construction and lets you focus on trade parameters.
|
|
793
|
+
Stage tracking helps you provide clear UX during proof/submission.
|
|
794
|
+
|
|
795
|
+
```tsx
|
|
796
|
+
import { useSwap } from '@miden-sdk/react';
|
|
797
|
+
|
|
798
|
+
function SwapForm() {
|
|
799
|
+
const { swap, result, isLoading, stage, error, reset } = useSwap();
|
|
800
|
+
|
|
801
|
+
const handleSwap = async () => {
|
|
802
|
+
try {
|
|
803
|
+
const { transactionId } = await swap({
|
|
804
|
+
accountId: '0xmywallet...',
|
|
805
|
+
|
|
806
|
+
// What you're offering
|
|
807
|
+
offeredFaucetId: '0xtokenA...',
|
|
808
|
+
offeredAmount: 100n,
|
|
809
|
+
|
|
810
|
+
// What you want in return
|
|
811
|
+
requestedFaucetId: '0xtokenB...',
|
|
812
|
+
requestedAmount: 50n,
|
|
813
|
+
|
|
814
|
+
// Optional
|
|
815
|
+
noteType: 'private', // Note type for swap note
|
|
816
|
+
paybackNoteType: 'private', // Note type for payback note
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
console.log('Swap created! TX:', transactionId);
|
|
820
|
+
} catch (err) {
|
|
821
|
+
console.error('Swap failed:', err);
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
return (
|
|
826
|
+
<button onClick={handleSwap} disabled={isLoading}>
|
|
827
|
+
{isLoading ? `Creating Swap (${stage})...` : 'Create Swap Offer'}
|
|
828
|
+
</button>
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
#### `useTransaction()`
|
|
834
|
+
|
|
835
|
+
Execute a custom `TransactionRequest` or build one with the client. This is the
|
|
836
|
+
escape hatch for advanced flows not covered by higher-level hooks.
|
|
837
|
+
It standardizes the execute/prove/submit flow while still letting you craft the
|
|
838
|
+
request. You get progress and error handling without wrapping every call
|
|
839
|
+
yourself.
|
|
840
|
+
|
|
841
|
+
```tsx
|
|
842
|
+
import { useTransaction } from '@miden-sdk/react';
|
|
843
|
+
import { AccountId, NoteType } from '@miden-sdk/miden-sdk';
|
|
844
|
+
|
|
845
|
+
function CustomTransactionButton({ accountId }: { accountId: string }) {
|
|
846
|
+
const { execute, isLoading, stage } = useTransaction();
|
|
847
|
+
|
|
848
|
+
const handleRun = async () => {
|
|
849
|
+
await execute({
|
|
850
|
+
accountId,
|
|
851
|
+
request: (client) =>
|
|
852
|
+
client.newSwapTransactionRequest(
|
|
853
|
+
AccountId.fromHex(accountId),
|
|
854
|
+
AccountId.fromHex('0xassetA'),
|
|
855
|
+
10n,
|
|
856
|
+
AccountId.fromHex('0xassetB'),
|
|
857
|
+
5n,
|
|
858
|
+
NoteType.Private,
|
|
859
|
+
NoteType.Private
|
|
860
|
+
),
|
|
861
|
+
});
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
return (
|
|
865
|
+
<button onClick={handleRun} disabled={isLoading}>
|
|
866
|
+
{isLoading ? stage : 'Run Transaction'}
|
|
867
|
+
</button>
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
## Common Patterns
|
|
873
|
+
|
|
874
|
+
### Error Handling
|
|
875
|
+
|
|
876
|
+
All hooks that can fail provide an `error` state and `reset` function:
|
|
877
|
+
|
|
878
|
+
```tsx
|
|
879
|
+
function MyComponent() {
|
|
880
|
+
const { createWallet, error, reset } = useCreateWallet();
|
|
881
|
+
|
|
882
|
+
if (error) {
|
|
883
|
+
return (
|
|
884
|
+
<div>
|
|
885
|
+
<p>Error: {error.message}</p>
|
|
886
|
+
<button onClick={reset}>Try Again</button>
|
|
887
|
+
</div>
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// ...
|
|
892
|
+
}
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
### Loading States
|
|
896
|
+
|
|
897
|
+
Query hooks provide `isLoading`, mutation hooks provide both `isLoading` and `stage`:
|
|
898
|
+
|
|
899
|
+
```tsx
|
|
900
|
+
function TransactionButton() {
|
|
901
|
+
const { send, isLoading, stage } = useSend();
|
|
902
|
+
|
|
903
|
+
// Show detailed progress
|
|
904
|
+
const buttonText = isLoading
|
|
905
|
+
? `${stage === 'proving' ? 'Generating proof' : 'Submitting'}...`
|
|
906
|
+
: 'Send';
|
|
907
|
+
|
|
908
|
+
return <button disabled={isLoading}>{buttonText}</button>;
|
|
909
|
+
}
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
### Refreshing Data
|
|
913
|
+
|
|
914
|
+
All query hooks provide a `refetch` function:
|
|
915
|
+
|
|
916
|
+
```tsx
|
|
917
|
+
function AccountBalance({ accountId }) {
|
|
918
|
+
const { assets, refetch } = useAccount(accountId);
|
|
919
|
+
|
|
920
|
+
// Refresh after a transaction
|
|
921
|
+
const handleSendComplete = async () => {
|
|
922
|
+
await refetch();
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
return (
|
|
926
|
+
<div>
|
|
927
|
+
{/* ... */}
|
|
928
|
+
<button onClick={refetch}>Refresh Balance</button>
|
|
929
|
+
</div>
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
### Waiting for Client Ready
|
|
935
|
+
|
|
936
|
+
Always check `isReady` before using hooks that require the client:
|
|
937
|
+
|
|
938
|
+
```tsx
|
|
939
|
+
function MyFeature() {
|
|
940
|
+
const { isReady } = useMiden();
|
|
941
|
+
const { createWallet } = useCreateWallet();
|
|
942
|
+
|
|
943
|
+
if (!isReady) {
|
|
944
|
+
return <div>Please wait...</div>;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
return <button onClick={() => createWallet()}>Create Wallet</button>;
|
|
948
|
+
}
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
## Default Values
|
|
952
|
+
|
|
953
|
+
The SDK uses privacy-first defaults:
|
|
954
|
+
|
|
955
|
+
| Setting | Default | Description |
|
|
956
|
+
|---------|---------|-------------|
|
|
957
|
+
| `storageMode` | `'private'` | Account data stored off-chain |
|
|
958
|
+
| `mutable` | `true` | Wallet code can be updated |
|
|
959
|
+
| `authScheme` | `0` (Falcon) | Post-quantum secure signatures |
|
|
960
|
+
| `noteType` | `'private'` | Note contents encrypted |
|
|
961
|
+
| `decimals` | `8` | Token decimal places |
|
|
962
|
+
| `autoSyncInterval` | `15000` | Sync every 15 seconds |
|
|
963
|
+
|
|
964
|
+
## TypeScript
|
|
965
|
+
|
|
966
|
+
Full TypeScript support with exported types:
|
|
967
|
+
|
|
968
|
+
```tsx
|
|
969
|
+
import type {
|
|
970
|
+
// Configuration
|
|
971
|
+
MidenConfig,
|
|
972
|
+
|
|
973
|
+
// Hook options
|
|
974
|
+
CreateWalletOptions,
|
|
975
|
+
CreateFaucetOptions,
|
|
976
|
+
ImportAccountOptions,
|
|
977
|
+
SendOptions,
|
|
978
|
+
MultiSendRecipient,
|
|
979
|
+
MultiSendOptions,
|
|
980
|
+
InternalTransferOptions,
|
|
981
|
+
InternalTransferChainOptions,
|
|
982
|
+
InternalTransferResult,
|
|
983
|
+
WaitForCommitOptions,
|
|
984
|
+
WaitForNotesOptions,
|
|
985
|
+
MintOptions,
|
|
986
|
+
ConsumeOptions,
|
|
987
|
+
SwapOptions,
|
|
988
|
+
ExecuteTransactionOptions,
|
|
989
|
+
NotesFilter,
|
|
990
|
+
|
|
991
|
+
// Hook results
|
|
992
|
+
AccountResult,
|
|
993
|
+
AccountsResult,
|
|
994
|
+
NotesResult,
|
|
995
|
+
TransactionResult,
|
|
996
|
+
|
|
997
|
+
// State types
|
|
998
|
+
TransactionStage,
|
|
999
|
+
AssetBalance,
|
|
1000
|
+
SyncState,
|
|
1001
|
+
} from '@miden-sdk/react';
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
## Examples
|
|
1005
|
+
|
|
1006
|
+
One runnable Vite example lives in `examples/`:
|
|
1007
|
+
|
|
1008
|
+
- `examples/wallet` - Minimal wallet: create account, view balances, claim notes, send tokens.
|
|
1009
|
+
|
|
1010
|
+
```bash
|
|
1011
|
+
cd examples/wallet
|
|
1012
|
+
yarn install
|
|
1013
|
+
yarn dev
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
## Requirements
|
|
1017
|
+
|
|
1018
|
+
- React 18.0 or higher
|
|
1019
|
+
- `@miden-sdk/miden-sdk` ^0.13.0-0
|
|
1020
|
+
|
|
1021
|
+
## Browser Support
|
|
1022
|
+
|
|
1023
|
+
Requires browsers with WebAssembly support:
|
|
1024
|
+
- Chrome 57+
|
|
1025
|
+
- Firefox 52+
|
|
1026
|
+
- Safari 11+
|
|
1027
|
+
- Edge 16+
|
|
1028
|
+
|
|
1029
|
+
## License
|
|
1030
|
+
|
|
1031
|
+
MIT
|