@tari-project/tarijs 0.10.0 → 0.11.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.
Files changed (71) hide show
  1. package/README.md +127 -67
  2. package/TODO.md +91 -3
  3. package/docusaurus/tari-docs/README.md +200 -17
  4. package/docusaurus/tari-docs/docs/api-reference.md +665 -0
  5. package/docusaurus/tari-docs/docs/contributing.md +619 -0
  6. package/docusaurus/tari-docs/docs/guides/getting-started-tutorial.md +965 -0
  7. package/docusaurus/tari-docs/docs/guides/production-deployment.md +977 -0
  8. package/docusaurus/tari-docs/docs/index.md +114 -11
  9. package/docusaurus/tari-docs/docs/installation.md +142 -1
  10. package/docusaurus/tari-docs/docs/signers/metamask.md +529 -0
  11. package/docusaurus/tari-docs/docs/troubleshooting.md +661 -0
  12. package/docusaurus/tari-docs/package.json +1 -1
  13. package/examples/vite-typescript-react/README.md +9 -0
  14. package/examples/vite-typescript-react/eslint.config.js +23 -0
  15. package/examples/vite-typescript-react/index.html +13 -0
  16. package/examples/vite-typescript-react/package.json +35 -0
  17. package/examples/vite-typescript-react/public/vite.svg +1 -0
  18. package/examples/vite-typescript-react/src/App.css +42 -0
  19. package/examples/vite-typescript-react/src/App.tsx +50 -0
  20. package/examples/vite-typescript-react/src/assets/react.svg +1 -0
  21. package/examples/vite-typescript-react/src/index.css +68 -0
  22. package/examples/vite-typescript-react/src/main.tsx +10 -0
  23. package/examples/vite-typescript-react/src/vite-env.d.ts +1 -0
  24. package/examples/vite-typescript-react/tsconfig.app.json +27 -0
  25. package/examples/vite-typescript-react/tsconfig.json +7 -0
  26. package/examples/vite-typescript-react/tsconfig.node.json +25 -0
  27. package/examples/vite-typescript-react/vite.config.ts +7 -0
  28. package/package.json +2 -2
  29. package/packages/builders/package.json +2 -2
  30. package/packages/builders/src/transaction/TransactionBuilder.ts +4 -12
  31. package/packages/indexer_provider/package.json +2 -2
  32. package/packages/indexer_provider/src/provider.ts +5 -5
  33. package/packages/indexer_provider/tsconfig.json +4 -2
  34. package/packages/metamask_signer/package.json +2 -2
  35. package/packages/metamask_signer/src/index.ts +2 -14
  36. package/packages/{tari_permissions → permissions}/package.json +2 -2
  37. package/packages/{tari_permissions → permissions}/src/helpers.ts +1 -1
  38. package/packages/permissions/src/index.ts +2 -0
  39. package/packages/{tari_permissions/src/tari_permissions.ts → permissions/src/permissions.ts} +56 -6
  40. package/packages/react-mui-connect-button/moon.yml +71 -0
  41. package/packages/react-mui-connect-button/package.json +40 -0
  42. package/packages/react-mui-connect-button/src/Logos.tsx +60 -0
  43. package/packages/react-mui-connect-button/src/TariConnectButton.tsx +51 -0
  44. package/packages/react-mui-connect-button/src/TariWalletSelectionDialog.tsx +116 -0
  45. package/packages/react-mui-connect-button/src/content/tari-logo-white.svg +18 -0
  46. package/packages/react-mui-connect-button/src/content/tari-logo.svg +18 -0
  47. package/packages/react-mui-connect-button/src/content/walletconnect-logo.svg +13 -0
  48. package/packages/react-mui-connect-button/src/defaultPermissions.ts +0 -0
  49. package/packages/react-mui-connect-button/src/index.ts +24 -0
  50. package/packages/react-mui-connect-button/tsconfig.json +31 -0
  51. package/packages/tari_provider/package.json +2 -2
  52. package/packages/tari_signer/package.json +2 -2
  53. package/packages/tari_universe/package.json +2 -2
  54. package/packages/tari_universe/tsconfig.json +4 -2
  55. package/packages/tarijs/package.json +2 -2
  56. package/packages/tarijs/tsconfig.json +4 -2
  57. package/packages/tarijs_types/package.json +2 -2
  58. package/packages/wallet_daemon/package.json +2 -2
  59. package/packages/wallet_daemon/src/provider.ts +9 -12
  60. package/packages/wallet_daemon/src/signer.ts +11 -6
  61. package/packages/wallet_daemon/tsconfig.json +1 -1
  62. package/packages/walletconnect/package.json +3 -2
  63. package/packages/walletconnect/src/index.ts +52 -26
  64. package/packages/walletconnect/tsconfig.json +3 -0
  65. package/pnpm-workspace.yaml +15 -7
  66. package/scripts/check_versions.sh +4 -0
  67. package/scripts/clean_everything.sh +38 -0
  68. package/tsconfig.json +6 -0
  69. package/packages/tari_permissions/src/index.ts +0 -2
  70. /package/packages/{tari_permissions → permissions}/moon.yml +0 -0
  71. /package/packages/{tari_permissions → permissions}/tsconfig.json +0 -0
@@ -0,0 +1,965 @@
1
+ ---
2
+ sidebar_position: 2
3
+ title: Getting Started Tutorial
4
+ ---
5
+
6
+ # Getting Started Tutorial
7
+
8
+ This comprehensive tutorial will guide you through building your first tari.js application step by step. By the end, you'll have a working web app that connects to Tari wallets and performs transactions.
9
+
10
+ ## What You'll Build
11
+
12
+ A simple wallet interface that can:
13
+ - 🔌 Connect to different Tari wallet types
14
+ - 💰 Display wallet balance
15
+ - 📊 Query blockchain data
16
+ - 💸 Send transactions
17
+ - 📱 Handle wallet events
18
+
19
+ ## Prerequisites
20
+
21
+ Before starting, ensure you have:
22
+
23
+ - **Node.js 18+** and **npm/pnpm** installed
24
+ - Basic knowledge of **TypeScript/JavaScript**
25
+ - **React** familiarity (optional - adaptable to any framework)
26
+ - A **Tari wallet** for testing (Wallet Daemon or MetaMask with tari-snap)
27
+
28
+ ## Step 1: Project Setup
29
+
30
+ ### Create a New React Project
31
+
32
+ ```bash
33
+ npm create vite@latest tari-wallet-app -- --template react-ts
34
+ cd tari-wallet-app
35
+ npm install
36
+ ```
37
+
38
+ ### Install tari.js Dependencies
39
+
40
+ ```bash
41
+ # Install core tari.js packages
42
+ npm install @tari-project/tarijs
43
+
44
+ # Install specific wallet providers
45
+ npm install @tari-project/wallet-daemon @tari-project/indexer-provider
46
+
47
+ # Optional: Add MetaMask support
48
+ npm install @tari-project/metamask-signer
49
+ ```
50
+
51
+ ### Configure Build Tools
52
+
53
+ Update `vite.config.ts` for proper bundling:
54
+
55
+ ```typescript
56
+ import { defineConfig } from 'vite'
57
+ import react from '@vitejs/plugin-react'
58
+
59
+ export default defineConfig({
60
+ plugins: [react()],
61
+ define: {
62
+ global: 'globalThis',
63
+ },
64
+ optimizeDeps: {
65
+ include: ['@tari-project/tarijs']
66
+ }
67
+ })
68
+ ```
69
+
70
+ ## Step 2: Basic Wallet Connection
71
+
72
+ ### Create Wallet Service
73
+
74
+ Create `src/services/walletService.ts`:
75
+
76
+ ```typescript
77
+ import {
78
+ WalletDaemonTariSigner,
79
+ IndexerProvider,
80
+ TariSigner,
81
+ TariProvider,
82
+ TariPermissions
83
+ } from '@tari-project/tarijs';
84
+
85
+ export interface WalletConnection {
86
+ signer: TariSigner;
87
+ provider: TariProvider;
88
+ isConnected: boolean;
89
+ }
90
+
91
+ export class WalletService {
92
+ private connection: WalletConnection | null = null;
93
+
94
+ async connectWalletDaemon(endpoint: string = 'http://localhost:18103'): Promise<WalletConnection> {
95
+ try {
96
+ const signer = await WalletDaemonTariSigner.buildFetchSigner({
97
+ serverUrl: endpoint,
98
+ permissions: new TariPermissions()
99
+ });
100
+
101
+ const provider = new IndexerProvider({
102
+ endpoint: 'http://localhost:18300'
103
+ });
104
+
105
+ // Test connection
106
+ await signer.getAccount();
107
+
108
+ this.connection = {
109
+ signer,
110
+ provider,
111
+ isConnected: true
112
+ };
113
+
114
+ return this.connection;
115
+ } catch (error) {
116
+ console.error('Failed to connect to wallet daemon:', error);
117
+ throw new Error('Could not connect to Tari Wallet Daemon. Is it running?');
118
+ }
119
+ }
120
+
121
+ async connectMetaMask(): Promise<WalletConnection> {
122
+ if (typeof window.ethereum === 'undefined') {
123
+ throw new Error('MetaMask not installed. Please install MetaMask Flask.');
124
+ }
125
+
126
+ try {
127
+ // Request access to MetaMask
128
+ await window.ethereum.request({ method: 'eth_requestAccounts' });
129
+
130
+ // Install Tari snap if needed
131
+ await window.ethereum.request({
132
+ method: 'wallet_requestSnaps',
133
+ params: {
134
+ 'npm:@tari-project/wallet-snap': {}
135
+ }
136
+ });
137
+
138
+ const { MetaMaskSigner } = await import('@tari-project/metamask-signer');
139
+ const signer = new MetaMaskSigner();
140
+
141
+ const provider = new IndexerProvider({
142
+ endpoint: 'http://localhost:18300'
143
+ });
144
+
145
+ this.connection = {
146
+ signer,
147
+ provider,
148
+ isConnected: true
149
+ };
150
+
151
+ return this.connection;
152
+ } catch (error) {
153
+ console.error('Failed to connect to MetaMask:', error);
154
+ throw new Error('Could not connect to MetaMask. Check installation and permissions.');
155
+ }
156
+ }
157
+
158
+ getConnection(): WalletConnection | null {
159
+ return this.connection;
160
+ }
161
+
162
+ async disconnect(): Promise<void> {
163
+ if (this.connection) {
164
+ await this.connection.signer.disconnect?.();
165
+ this.connection = null;
166
+ }
167
+ }
168
+ }
169
+ ```
170
+
171
+ ### Create Wallet Context
172
+
173
+ Create `src/contexts/WalletContext.tsx`:
174
+
175
+ ```typescript
176
+ import React, { createContext, useContext, useState, useCallback } from 'react';
177
+ import { WalletService, WalletConnection } from '../services/walletService';
178
+
179
+ interface WalletContextType {
180
+ connection: WalletConnection | null;
181
+ isConnecting: boolean;
182
+ error: string | null;
183
+ connectWalletDaemon: () => Promise<void>;
184
+ connectMetaMask: () => Promise<void>;
185
+ disconnect: () => Promise<void>;
186
+ }
187
+
188
+ const WalletContext = createContext<WalletContextType | undefined>(undefined);
189
+
190
+ export const useWallet = () => {
191
+ const context = useContext(WalletContext);
192
+ if (!context) {
193
+ throw new Error('useWallet must be used within a WalletProvider');
194
+ }
195
+ return context;
196
+ };
197
+
198
+ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
199
+ const [connection, setConnection] = useState<WalletConnection | null>(null);
200
+ const [isConnecting, setIsConnecting] = useState(false);
201
+ const [error, setError] = useState<string | null>(null);
202
+ const [walletService] = useState(new WalletService());
203
+
204
+ const handleConnection = useCallback(async (connectFn: () => Promise<WalletConnection>) => {
205
+ setIsConnecting(true);
206
+ setError(null);
207
+
208
+ try {
209
+ const conn = await connectFn();
210
+ setConnection(conn);
211
+ } catch (err) {
212
+ setError(err instanceof Error ? err.message : 'Connection failed');
213
+ } finally {
214
+ setIsConnecting(false);
215
+ }
216
+ }, []);
217
+
218
+ const connectWalletDaemon = useCallback(() =>
219
+ handleConnection(() => walletService.connectWalletDaemon()),
220
+ [handleConnection, walletService]
221
+ );
222
+
223
+ const connectMetaMask = useCallback(() =>
224
+ handleConnection(() => walletService.connectMetaMask()),
225
+ [handleConnection, walletService]
226
+ );
227
+
228
+ const disconnect = useCallback(async () => {
229
+ await walletService.disconnect();
230
+ setConnection(null);
231
+ setError(null);
232
+ }, [walletService]);
233
+
234
+ return (
235
+ <WalletContext.Provider value={{
236
+ connection,
237
+ isConnecting,
238
+ error,
239
+ connectWalletDaemon,
240
+ connectMetaMask,
241
+ disconnect
242
+ }}>
243
+ {children}
244
+ </WalletContext.Provider>
245
+ );
246
+ };
247
+ ```
248
+
249
+ ## Step 3: Wallet Connection UI
250
+
251
+ ### Create Wallet Connect Component
252
+
253
+ Create `src/components/WalletConnect.tsx`:
254
+
255
+ ```typescript
256
+ import React from 'react';
257
+ import { useWallet } from '../contexts/WalletContext';
258
+
259
+ export const WalletConnect: React.FC = () => {
260
+ const {
261
+ connection,
262
+ isConnecting,
263
+ error,
264
+ connectWalletDaemon,
265
+ connectMetaMask,
266
+ disconnect
267
+ } = useWallet();
268
+
269
+ if (connection?.isConnected) {
270
+ return (
271
+ <div className="wallet-connected">
272
+ <div className="success-message">
273
+ ✅ Wallet Connected Successfully!
274
+ </div>
275
+ <button onClick={disconnect} className="disconnect-btn">
276
+ Disconnect Wallet
277
+ </button>
278
+ </div>
279
+ );
280
+ }
281
+
282
+ return (
283
+ <div className="wallet-connect">
284
+ <h2>Connect Your Tari Wallet</h2>
285
+
286
+ {error && (
287
+ <div className="error-message">
288
+ ❌ {error}
289
+ </div>
290
+ )}
291
+
292
+ <div className="wallet-options">
293
+ <button
294
+ onClick={connectWalletDaemon}
295
+ disabled={isConnecting}
296
+ className="wallet-btn wallet-daemon-btn"
297
+ >
298
+ {isConnecting ? '🔄 Connecting...' : '🖥️ Connect Wallet Daemon'}
299
+ </button>
300
+
301
+ <button
302
+ onClick={connectMetaMask}
303
+ disabled={isConnecting}
304
+ className="wallet-btn metamask-btn"
305
+ >
306
+ {isConnecting ? '🔄 Connecting...' : '🦊 Connect MetaMask'}
307
+ </button>
308
+ </div>
309
+
310
+ <div className="wallet-help">
311
+ <h3>Need Help?</h3>
312
+ <ul>
313
+ <li>
314
+ <strong>Wallet Daemon:</strong> Make sure your Tari Wallet Daemon is running on localhost:18103
315
+ </li>
316
+ <li>
317
+ <strong>MetaMask:</strong> Install MetaMask Flask and the Tari snap from our example site
318
+ </li>
319
+ </ul>
320
+ </div>
321
+ </div>
322
+ );
323
+ };
324
+ ```
325
+
326
+ ## Step 4: Display Wallet Information
327
+
328
+ ### Create Wallet Info Component
329
+
330
+ Create `src/components/WalletInfo.tsx`:
331
+
332
+ ```typescript
333
+ import React, { useState, useEffect } from 'react';
334
+ import { useWallet } from '../contexts/WalletContext';
335
+
336
+ interface AccountInfo {
337
+ address: string;
338
+ balance: number;
339
+ name?: string;
340
+ }
341
+
342
+ export const WalletInfo: React.FC = () => {
343
+ const { connection } = useWallet();
344
+ const [accounts, setAccounts] = useState<AccountInfo[]>([]);
345
+ const [loading, setLoading] = useState(false);
346
+ const [error, setError] = useState<string | null>(null);
347
+
348
+ useEffect(() => {
349
+ if (connection?.isConnected) {
350
+ loadAccountInfo();
351
+ }
352
+ }, [connection]);
353
+
354
+ const loadAccountInfo = async () => {
355
+ if (!connection) return;
356
+
357
+ setLoading(true);
358
+ setError(null);
359
+
360
+ try {
361
+ // Get default account
362
+ const account = await connection.signer.getAccount();
363
+
364
+ setAccounts([{
365
+ address: account.address,
366
+ balance: account.resources.reduce((total, resource) => total + resource.balance, 0),
367
+ name: 'Default Account'
368
+ }]);
369
+ } catch (err) {
370
+ setError(err instanceof Error ? err.message : 'Failed to load account information');
371
+ } finally {
372
+ setLoading(false);
373
+ }
374
+ };
375
+
376
+ if (!connection?.isConnected) {
377
+ return null;
378
+ }
379
+
380
+ if (loading) {
381
+ return (
382
+ <div className="wallet-info loading">
383
+ <h3>Loading wallet information...</h3>
384
+ </div>
385
+ );
386
+ }
387
+
388
+ if (error) {
389
+ return (
390
+ <div className="wallet-info error">
391
+ <h3>Error loading wallet info</h3>
392
+ <p>{error}</p>
393
+ <button onClick={loadAccountInfo}>Retry</button>
394
+ </div>
395
+ );
396
+ }
397
+
398
+ return (
399
+ <div className="wallet-info">
400
+ <div className="info-header">
401
+ <h3>💰 Wallet Information</h3>
402
+ <button onClick={loadAccountInfo} className="refresh-btn">
403
+ 🔄 Refresh
404
+ </button>
405
+ </div>
406
+
407
+ <div className="accounts-list">
408
+ {accounts.length === 0 ? (
409
+ <p>No accounts found</p>
410
+ ) : (
411
+ accounts.map((account, index) => (
412
+ <div key={index} className="account-card">
413
+ <div className="account-name">
414
+ <strong>{account.name}</strong>
415
+ </div>
416
+ <div className="account-address">
417
+ Address: <code>{account.address}</code>
418
+ </div>
419
+ <div className="account-balance">
420
+ Balance: <strong>{account.balance.toLocaleString()} Tari</strong>
421
+ </div>
422
+ </div>
423
+ ))
424
+ )}
425
+ </div>
426
+ </div>
427
+ );
428
+ };
429
+ ```
430
+
431
+ ## Step 5: Simple Transaction Interface
432
+
433
+ ### Create Transaction Component
434
+
435
+ Create `src/components/TransactionForm.tsx`:
436
+
437
+ ```typescript
438
+ import React, { useState } from 'react';
439
+ import { useWallet } from '../contexts/WalletContext';
440
+ import { TransactionBuilder } from '@tari-project/tarijs';
441
+
442
+ export const TransactionForm: React.FC = () => {
443
+ const { connection } = useWallet();
444
+ const [recipient, setRecipient] = useState('');
445
+ const [amount, setAmount] = useState('');
446
+ const [fee, setFee] = useState('100');
447
+ const [isSubmitting, setIsSubmitting] = useState(false);
448
+ const [result, setResult] = useState<string | null>(null);
449
+ const [error, setError] = useState<string | null>(null);
450
+
451
+ const handleSubmit = async (e: React.FormEvent) => {
452
+ e.preventDefault();
453
+
454
+ if (!connection?.isConnected) {
455
+ setError('Wallet not connected');
456
+ return;
457
+ }
458
+
459
+ if (!recipient || !amount) {
460
+ setError('Please fill in all fields');
461
+ return;
462
+ }
463
+
464
+ setIsSubmitting(true);
465
+ setError(null);
466
+ setResult(null);
467
+
468
+ try {
469
+ // Get default account
470
+ const account = await connection.signer.getAccount();
471
+
472
+ // Build transaction
473
+ const transaction = new TransactionBuilder()
474
+ .feeTransactionPayFromComponent(account.address, fee)
475
+ .callMethod({
476
+ componentAddress: account.address,
477
+ methodName: 'withdraw',
478
+ }, [{ type: 'Amount', value: amount }])
479
+ .build();
480
+
481
+ // Submit transaction
482
+ const txResult = await connection.signer.submitTransaction({ transaction });
483
+
484
+ setResult(`Transaction submitted successfully! ID: ${txResult.transaction_id}`);
485
+
486
+ // Clear form
487
+ setRecipient('');
488
+ setAmount('');
489
+ } catch (err) {
490
+ setError(err instanceof Error ? err.message : 'Transaction failed');
491
+ } finally {
492
+ setIsSubmitting(false);
493
+ }
494
+ };
495
+
496
+ if (!connection?.isConnected) {
497
+ return (
498
+ <div className="transaction-form disabled">
499
+ <h3>💸 Send Transaction</h3>
500
+ <p>Connect a wallet to send transactions</p>
501
+ </div>
502
+ );
503
+ }
504
+
505
+ return (
506
+ <div className="transaction-form">
507
+ <h3>💸 Send Transaction</h3>
508
+
509
+ <form onSubmit={handleSubmit}>
510
+ <div className="form-group">
511
+ <label htmlFor="recipient">Recipient Address:</label>
512
+ <input
513
+ id="recipient"
514
+ type="text"
515
+ value={recipient}
516
+ onChange={(e) => setRecipient(e.target.value)}
517
+ placeholder="Enter recipient address..."
518
+ disabled={isSubmitting}
519
+ />
520
+ </div>
521
+
522
+ <div className="form-group">
523
+ <label htmlFor="amount">Amount (Tari):</label>
524
+ <input
525
+ id="amount"
526
+ type="number"
527
+ value={amount}
528
+ onChange={(e) => setAmount(e.target.value)}
529
+ placeholder="Enter amount..."
530
+ min="1"
531
+ disabled={isSubmitting}
532
+ />
533
+ </div>
534
+
535
+ <div className="form-group">
536
+ <label htmlFor="fee">Fee (Tari):</label>
537
+ <input
538
+ id="fee"
539
+ type="number"
540
+ value={fee}
541
+ onChange={(e) => setFee(e.target.value)}
542
+ placeholder="Enter fee..."
543
+ min="1"
544
+ disabled={isSubmitting}
545
+ />
546
+ </div>
547
+
548
+ <button
549
+ type="submit"
550
+ disabled={isSubmitting || !recipient || !amount}
551
+ className="submit-btn"
552
+ >
553
+ {isSubmitting ? '🔄 Sending...' : '📤 Send Transaction'}
554
+ </button>
555
+ </form>
556
+
557
+ {error && (
558
+ <div className="error-message">
559
+ ❌ {error}
560
+ </div>
561
+ )}
562
+
563
+ {result && (
564
+ <div className="success-message">
565
+ ✅ {result}
566
+ </div>
567
+ )}
568
+ </div>
569
+ );
570
+ };
571
+ ```
572
+
573
+ ## Step 6: Bring It All Together
574
+
575
+ ### Update Main App
576
+
577
+ Update `src/App.tsx`:
578
+
579
+ ```typescript
580
+ import React from 'react';
581
+ import { WalletProvider } from './contexts/WalletContext';
582
+ import { WalletConnect } from './components/WalletConnect';
583
+ import { WalletInfo } from './components/WalletInfo';
584
+ import { TransactionForm } from './components/TransactionForm';
585
+ import './App.css';
586
+
587
+ function App() {
588
+ return (
589
+ <WalletProvider>
590
+ <div className="app">
591
+ <header className="app-header">
592
+ <h1>🏗️ Tari Wallet Demo</h1>
593
+ <p>Connect to your Tari wallet and perform transactions</p>
594
+ </header>
595
+
596
+ <main className="app-main">
597
+ <section className="connect-section">
598
+ <WalletConnect />
599
+ </section>
600
+
601
+ <section className="info-section">
602
+ <WalletInfo />
603
+ </section>
604
+
605
+ <section className="transaction-section">
606
+ <TransactionForm />
607
+ </section>
608
+ </main>
609
+
610
+ <footer className="app-footer">
611
+ <p>
612
+ Built with <a href="https://tari-project.github.io/tari.js/">tari.js</a> |
613
+ <a href="https://github.com/tari-project/tari.js">GitHub</a>
614
+ </p>
615
+ </footer>
616
+ </div>
617
+ </WalletProvider>
618
+ );
619
+ }
620
+
621
+ export default App;
622
+ ```
623
+
624
+ ### Add Basic Styling
625
+
626
+ Create/update `src/App.css`:
627
+
628
+ ```css
629
+ .app {
630
+ max-width: 800px;
631
+ margin: 0 auto;
632
+ padding: 20px;
633
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
634
+ }
635
+
636
+ .app-header {
637
+ text-align: center;
638
+ margin-bottom: 40px;
639
+ }
640
+
641
+ .app-header h1 {
642
+ color: #333;
643
+ margin-bottom: 10px;
644
+ }
645
+
646
+ .app-main {
647
+ display: flex;
648
+ flex-direction: column;
649
+ gap: 30px;
650
+ }
651
+
652
+ /* Wallet Connect Styles */
653
+ .wallet-connect {
654
+ background: #f8f9fa;
655
+ padding: 30px;
656
+ border-radius: 12px;
657
+ text-align: center;
658
+ }
659
+
660
+ .wallet-options {
661
+ display: flex;
662
+ gap: 15px;
663
+ justify-content: center;
664
+ margin: 20px 0;
665
+ }
666
+
667
+ .wallet-btn {
668
+ padding: 12px 24px;
669
+ border: none;
670
+ border-radius: 8px;
671
+ font-size: 16px;
672
+ cursor: pointer;
673
+ transition: all 0.2s;
674
+ }
675
+
676
+ .wallet-daemon-btn {
677
+ background: #007bff;
678
+ color: white;
679
+ }
680
+
681
+ .metamask-btn {
682
+ background: #f6851b;
683
+ color: white;
684
+ }
685
+
686
+ .wallet-btn:hover:not(:disabled) {
687
+ transform: translateY(-2px);
688
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
689
+ }
690
+
691
+ .wallet-btn:disabled {
692
+ opacity: 0.6;
693
+ cursor: not-allowed;
694
+ }
695
+
696
+ .wallet-connected {
697
+ background: #d4edda;
698
+ color: #155724;
699
+ padding: 20px;
700
+ border-radius: 8px;
701
+ text-align: center;
702
+ }
703
+
704
+ .disconnect-btn {
705
+ background: #dc3545;
706
+ color: white;
707
+ border: none;
708
+ padding: 8px 16px;
709
+ border-radius: 4px;
710
+ cursor: pointer;
711
+ margin-top: 10px;
712
+ }
713
+
714
+ /* Wallet Info Styles */
715
+ .wallet-info {
716
+ background: white;
717
+ border: 1px solid #e9ecef;
718
+ border-radius: 12px;
719
+ padding: 24px;
720
+ }
721
+
722
+ .info-header {
723
+ display: flex;
724
+ justify-content: space-between;
725
+ align-items: center;
726
+ margin-bottom: 20px;
727
+ }
728
+
729
+ .refresh-btn {
730
+ background: #28a745;
731
+ color: white;
732
+ border: none;
733
+ padding: 6px 12px;
734
+ border-radius: 4px;
735
+ cursor: pointer;
736
+ }
737
+
738
+ .account-card {
739
+ background: #f8f9fa;
740
+ padding: 16px;
741
+ border-radius: 8px;
742
+ margin-bottom: 12px;
743
+ }
744
+
745
+ .account-address code {
746
+ background: #e9ecef;
747
+ padding: 2px 6px;
748
+ border-radius: 4px;
749
+ font-size: 12px;
750
+ }
751
+
752
+ /* Transaction Form Styles */
753
+ .transaction-form {
754
+ background: white;
755
+ border: 1px solid #e9ecef;
756
+ border-radius: 12px;
757
+ padding: 24px;
758
+ }
759
+
760
+ .form-group {
761
+ margin-bottom: 16px;
762
+ }
763
+
764
+ .form-group label {
765
+ display: block;
766
+ margin-bottom: 4px;
767
+ font-weight: 500;
768
+ }
769
+
770
+ .form-group input {
771
+ width: 100%;
772
+ padding: 10px;
773
+ border: 1px solid #ced4da;
774
+ border-radius: 4px;
775
+ font-size: 14px;
776
+ }
777
+
778
+ .submit-btn {
779
+ background: #007bff;
780
+ color: white;
781
+ border: none;
782
+ padding: 12px 24px;
783
+ border-radius: 4px;
784
+ cursor: pointer;
785
+ font-size: 16px;
786
+ width: 100%;
787
+ }
788
+
789
+ .submit-btn:disabled {
790
+ opacity: 0.6;
791
+ cursor: not-allowed;
792
+ }
793
+
794
+ /* Message Styles */
795
+ .error-message {
796
+ background: #f8d7da;
797
+ color: #721c24;
798
+ padding: 12px;
799
+ border-radius: 4px;
800
+ margin-top: 16px;
801
+ }
802
+
803
+ .success-message {
804
+ background: #d4edda;
805
+ color: #155724;
806
+ padding: 12px;
807
+ border-radius: 4px;
808
+ margin-top: 16px;
809
+ }
810
+
811
+ /* Responsive Design */
812
+ @media (max-width: 768px) {
813
+ .wallet-options {
814
+ flex-direction: column;
815
+ align-items: center;
816
+ }
817
+
818
+ .wallet-btn {
819
+ width: 100%;
820
+ max-width: 300px;
821
+ }
822
+ }
823
+ ```
824
+
825
+ ## Step 7: Testing Your Application
826
+
827
+ ### Start the Development Server
828
+
829
+ ```bash
830
+ npm run dev
831
+ ```
832
+
833
+ Your app should now be running at `http://localhost:5173`
834
+
835
+ ### Test Wallet Connections
836
+
837
+ #### Testing with Wallet Daemon:
838
+
839
+ 1. Ensure your Tari Wallet Daemon is running:
840
+ ```bash
841
+ ./target/release/minotari_wallet_daemon --config-path ./config.toml
842
+ ```
843
+
844
+ 2. Click "Connect Wallet Daemon" in your app
845
+ 3. If successful, you should see wallet information displayed
846
+
847
+ #### Testing with MetaMask:
848
+
849
+ 1. Install MetaMask Flask (developer version)
850
+ 2. Visit the tari.js example site to install the Tari snap
851
+ 3. Click "Connect MetaMask" in your app
852
+ 4. Follow the MetaMask prompts to connect
853
+
854
+ ## Step 8: Advanced Features
855
+
856
+ ### Add Real-time Updates
857
+
858
+ ```typescript
859
+ // In WalletInfo component, add polling for balance updates
860
+ useEffect(() => {
861
+ if (!connection?.isConnected) return;
862
+
863
+ const interval = setInterval(() => {
864
+ loadAccountInfo();
865
+ }, 30000); // Update every 30 seconds
866
+
867
+ return () => clearInterval(interval);
868
+ }, [connection]);
869
+ ```
870
+
871
+ ### Add Transaction History
872
+
873
+ ```typescript
874
+ // Add to WalletInfo component
875
+ const [transactions, setTransactions] = useState([]);
876
+
877
+ const loadTransactionHistory = async () => {
878
+ if (!connection) return;
879
+
880
+ try {
881
+ const accounts = await connection.signer.getAccounts();
882
+ const txHistory = await connection.provider.getTransactionHistory(accounts[0]);
883
+ setTransactions(txHistory);
884
+ } catch (error) {
885
+ console.error('Failed to load transaction history:', error);
886
+ }
887
+ };
888
+ ```
889
+
890
+ ### Error Boundaries
891
+
892
+ ```typescript
893
+ // Create src/components/ErrorBoundary.tsx
894
+ import React from 'react';
895
+
896
+ interface Props {
897
+ children: React.ReactNode;
898
+ }
899
+
900
+ interface State {
901
+ hasError: boolean;
902
+ error?: Error;
903
+ }
904
+
905
+ export class ErrorBoundary extends React.Component<Props, State> {
906
+ constructor(props: Props) {
907
+ super(props);
908
+ this.state = { hasError: false };
909
+ }
910
+
911
+ static getDerivedStateFromError(error: Error): State {
912
+ return { hasError: true, error };
913
+ }
914
+
915
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
916
+ console.error('App Error:', error, errorInfo);
917
+ }
918
+
919
+ render() {
920
+ if (this.state.hasError) {
921
+ return (
922
+ <div className="error-boundary">
923
+ <h2>Something went wrong!</h2>
924
+ <p>{this.state.error?.message}</p>
925
+ <button onClick={() => window.location.reload()}>
926
+ Reload Page
927
+ </button>
928
+ </div>
929
+ );
930
+ }
931
+
932
+ return this.props.children;
933
+ }
934
+ }
935
+ ```
936
+
937
+ ## Next Steps
938
+
939
+ Congratulations! 🎉 You've built a functional Tari wallet application. Here's what you can explore next:
940
+
941
+ ### 📚 Learn More:
942
+ - **[Advanced Transaction Building](../wallet/submit-transaction/transaction-builder/)** - Complex smart contract interactions
943
+ - **[Template System](../wallet/template-definition.md)** - Working with smart contract templates
944
+ - **[Provider Types](../providers/indexer-provider.md)** - Different data access patterns
945
+
946
+ ### 🚀 Production Ready:
947
+ - **[Production Deployment Guide](./production-deployment.md)** - Security and performance best practices
948
+ - **[Error Handling](../troubleshooting.md)** - Comprehensive error management
949
+ - **[GitHub Testing Examples](https://github.com/tari-project/tari.js/tree/main/packages/tarijs/test)** - Unit and integration testing examples
950
+
951
+ ### 🛠️ Extend Your App:
952
+ - Add support for multiple wallet types
953
+ - Implement transaction history visualization
954
+ - Create reusable wallet components
955
+ - Add offline transaction queuing
956
+ - Implement advanced authentication
957
+
958
+ ### 💬 Get Help:
959
+ - **[Discord Community](https://discord.gg/tari)** - Join the conversation
960
+ - **[GitHub Discussions](https://github.com/tari-project/tari.js/discussions)** - Ask questions
961
+ - **[API Reference](../api-reference.md)** - Complete method documentation
962
+
963
+ ---
964
+
965
+ *Happy building with tari.js! 🏗️*