@blazium/ton-connect-mobile 1.2.0 → 1.2.3

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 CHANGED
@@ -9,8 +9,8 @@ Production-ready TON Connect Mobile SDK for React Native and Expo. Implements th
9
9
  - ✅ **Full `@tonconnect/ui-react` Compatibility** - Drop-in replacement
10
10
  - ✅ **React Native & Expo Support** - Works with both Expo and React Native CLI
11
11
  - ✅ **Android & iOS Support** - Full deep linking support
12
- - ✅ **Multiple Wallet Support** - Tonkeeper, Tonhub, MyTonWallet, Telegram Wallet
13
- - ✅ **Beautiful Wallet Selection Modal** - Built-in wallet picker UI
12
+ - ✅ **Multiple Wallet Support** - Tonkeeper (including Web), Tonhub, MyTonWallet, Telegram Wallet
13
+ - ✅ **Beautiful Wallet Selection Modal** - Grid layout matching @tonconnect/ui-react design
14
14
  - ✅ **Transaction Signing** - Send transactions with wallet approval
15
15
  - ✅ **Data Signing** - Sign arbitrary data for authentication
16
16
  - ✅ **Transaction Builder Utilities** - Helper functions for building transactions
@@ -18,6 +18,10 @@ Production-ready TON Connect Mobile SDK for React Native and Expo. Implements th
18
18
  - ✅ **Enhanced Error Messages** - Clear error messages with recovery suggestions
19
19
  - ✅ **Wallet Availability Checking** - Check if wallets are available
20
20
  - ✅ **Session Persistence** - Maintains connection across app restarts
21
+ - ✅ **Network Switching** - Switch between mainnet and testnet dynamically
22
+ - ✅ **Event Emitters** - Listen to connect, disconnect, transaction, and error events
23
+ - ✅ **Wallet Balance Checking** - Get wallet balance via TON Center API
24
+ - ✅ **Transaction Status Tracking** - Track transaction status with polling
21
25
  - ✅ **TypeScript** - Full type safety
22
26
  - ✅ **Production Ready** - Battle-tested implementation
23
27
 
@@ -135,18 +139,50 @@ Pre-built button component for connecting/disconnecting wallets.
135
139
 
136
140
  #### `useTonConnectUI()`
137
141
 
138
- Access the TonConnectUI instance with all methods.
142
+ Access the TonConnectUI instance with all methods and features.
139
143
 
140
144
  ```typescript
141
145
  const tonConnectUI = useTonConnectUI();
142
146
 
143
- // Methods:
147
+ // Connection methods:
144
148
  await tonConnectUI.connectWallet();
145
149
  await tonConnectUI.disconnect();
150
+ await tonConnectUI.restoreConnection(); // Restore from stored session
151
+
152
+ // Transaction methods:
146
153
  await tonConnectUI.sendTransaction({ ... });
147
154
  await tonConnectUI.signData({ data: '...', version: '1.0' });
155
+
156
+ // Modal methods:
148
157
  await tonConnectUI.openModal();
149
158
  tonConnectUI.closeModal();
159
+
160
+ // Wallet customization:
161
+ tonConnectUI.setWalletList([...]); // Customize available wallets
162
+
163
+ // Network management:
164
+ const network = tonConnectUI.getNetwork(); // Get current network
165
+ tonConnectUI.setNetwork('testnet'); // Switch to testnet
166
+
167
+ // Balance checking:
168
+ const balance = await tonConnectUI.getBalance(); // Get connected wallet balance
169
+ const balance2 = await tonConnectUI.getBalance(address); // Get specific address balance
170
+
171
+ // Transaction status:
172
+ const status = await tonConnectUI.getTransactionStatusByHash(txHash, address);
173
+
174
+ // Event listeners:
175
+ const unsubscribe = tonConnectUI.on('connect', (wallet) => {
176
+ console.log('Connected:', wallet);
177
+ });
178
+ tonConnectUI.on('disconnect', () => console.log('Disconnected'));
179
+ tonConnectUI.on('transaction', (tx) => console.log('Transaction:', tx));
180
+ tonConnectUI.on('error', (error) => console.error('Error:', error));
181
+
182
+ // State access:
183
+ tonConnectUI.wallet; // Current wallet state
184
+ tonConnectUI.modalState.open; // Modal open state
185
+ tonConnectUI.uiVersion; // UI kit version
150
186
  ```
151
187
 
152
188
  #### `useTonWallet()`
@@ -204,10 +240,12 @@ new TonConnectMobile(config: TonConnectMobileConfig)
204
240
  - `manifestUrl` (required): URL to your TonConnect manifest file
205
241
  - `scheme` (required): Your app's deep link scheme
206
242
  - `storageKeyPrefix` (optional): Prefix for storage keys (default: `'tonconnect_'`)
207
- - `connectionTimeout` (optional): Connection timeout in ms (default: `30000`)
208
- - `transactionTimeout` (optional): Transaction timeout in ms (default: `300000`)
243
+ - `connectionTimeout` (optional): Connection timeout in ms (default: `300000` = 5 minutes)
244
+ - `transactionTimeout` (optional): Transaction timeout in ms (default: `300000` = 5 minutes)
209
245
  - `skipCanOpenURLCheck` (optional): Skip canOpenURL check (default: `true` for Android compatibility)
210
246
  - `preferredWallet` (optional): Default wallet name
247
+ - `network` (optional): Network to use - `'mainnet'` or `'testnet'` (default: `'mainnet'`)
248
+ - `tonApiEndpoint` (optional): Custom TON API endpoint (default: auto-selected based on network)
211
249
 
212
250
  #### Methods
213
251
 
@@ -286,20 +324,124 @@ const unsubscribe = ton.onStatusChange((status) => {
286
324
  });
287
325
  ```
288
326
 
289
- ## Platform Support
327
+ ##### `getNetwork(): Network`
328
+
329
+ Get current network (mainnet or testnet).
330
+
331
+ ```typescript
332
+ const network = ton.getNetwork(); // 'mainnet' or 'testnet'
333
+ ```
334
+
335
+ ##### `setNetwork(network: Network): void`
336
+
337
+ Switch between mainnet and testnet.
338
+
339
+ ```typescript
340
+ ton.setNetwork('testnet'); // Switch to testnet
341
+ // Note: Warning is logged if switching while wallet is connected
342
+ ```
343
+
344
+ ##### `getBalance(address?: string): Promise<BalanceResponse>`
345
+
346
+ Get wallet balance from TON Center API.
347
+
348
+ ```typescript
349
+ // Get balance of connected wallet
350
+ const balance = await ton.getBalance();
351
+
352
+ // Get balance of specific address
353
+ const balance = await ton.getBalance('EQD0vdSA_NedR9uvbgN9EikRX-suesDxGeFg69XQMavfLqIo');
354
+
355
+ // Response:
356
+ // {
357
+ // balance: "1000000000", // in nanotons
358
+ // balanceTon: "1.0", // formatted TON
359
+ // network: "mainnet"
360
+ // }
361
+ ```
362
+
363
+ ##### `getTransactionStatusByHash(txHash: string, address: string): Promise<TransactionStatusResponse>`
364
+
365
+ Get transaction status by hash (recommended method).
366
+
367
+ ```typescript
368
+ const status = await ton.getTransactionStatusByHash(txHash, address);
369
+
370
+ // Response:
371
+ // {
372
+ // status: "confirmed" | "pending" | "failed" | "unknown",
373
+ // hash?: string,
374
+ // blockNumber?: number,
375
+ // error?: string
376
+ // }
377
+ ```
378
+
379
+ ##### `getTransactionStatus(boc: string, maxAttempts?: number, intervalMs?: number): Promise<TransactionStatusResponse>`
380
+
381
+ Get transaction status from BOC (requires BOC parsing library).
382
+
383
+ ```typescript
384
+ // Note: This method requires BOC parsing. Use getTransactionStatusByHash() instead.
385
+ const status = await ton.getTransactionStatus(boc, 10, 2000);
386
+ ```
387
+
388
+ ##### `on<T>(event: TonConnectEventType, listener: TonConnectEventListener<T>): () => void`
389
+
390
+ Add event listener.
391
+
392
+ ```typescript
393
+ // Listen to connection events
394
+ const unsubscribe = ton.on('connect', (wallet) => {
395
+ console.log('Connected to:', wallet.name);
396
+ });
397
+
398
+ // Listen to transaction events
399
+ ton.on('transaction', (tx) => {
400
+ console.log('Transaction sent:', tx.boc);
401
+ });
402
+
403
+ // Listen to errors
404
+ ton.on('error', (error) => {
405
+ console.error('SDK error:', error);
406
+ });
407
+
408
+ // Cleanup
409
+ unsubscribe();
410
+ ```
411
+
412
+ ##### `off<T>(event: TonConnectEventType, listener: TonConnectEventListener<T>): void`
413
+
414
+ Remove event listener.
415
+
416
+ ```typescript
417
+ ton.off('connect', listener);
418
+ ```
419
+
420
+ ##### `removeAllListeners(event?: TonConnectEventType): void`
290
421
 
291
- **⚠️ Important**: TON Connect deep links (`tonconnect://`) only work on **mobile devices** (Android/iOS). They do not work in web browsers.
422
+ Remove all listeners for an event, or all events.
423
+
424
+ ```typescript
425
+ ton.removeAllListeners('connect'); // Remove all connect listeners
426
+ ton.removeAllListeners(); // Remove all listeners
427
+ ```
428
+
429
+ ## Platform Support
292
430
 
293
431
  - ✅ **Android**: Full support via Expo or React Native CLI
294
432
  - ✅ **iOS**: Full support via Expo or React Native CLI
295
- - ⚠️ **Web**: Deep links are not supported. The SDK will throw a clear error message if you try to use it in a web browser.
433
+ - **Web**: Universal links supported (opens wallet in new tab/window)
296
434
 
297
- **Why?** The `tonconnect://` protocol is a custom URI scheme that requires a mobile app handler. Web browsers cannot handle these custom protocols.
435
+ **Web Platform Notes:**
436
+ - On web, wallets with universal links (like Tonkeeper Web, MyTonWallet) can be opened in a new browser tab
437
+ - The SDK automatically detects web platform and shows all available wallets
438
+ - Wallet availability is checked based on universal link support
439
+ - Deep links (`tonconnect://`) are not supported in web browsers, but universal links work perfectly
298
440
 
299
- **Testing**: To test wallet connections, use:
441
+ **Testing**:
300
442
  - Android device or emulator
301
443
  - iOS device or simulator
302
- - Not web browsers
444
+ - Web browsers (for wallets with web support like Tonkeeper Web)
303
445
 
304
446
  ## Configuration
305
447
 
@@ -368,10 +510,12 @@ The manifest URL must be accessible via HTTPS.
368
510
 
369
511
  ## Supported Wallets
370
512
 
371
- - **Tonkeeper** - Full support
372
- - **Tonhub** - Full support
373
- - **MyTonWallet** - Full support
374
- - **Wallet in Telegram** - Full support
513
+ - **Tonkeeper** - Full support (iOS, Android, Web)
514
+ - **MyTonWallet** - Full support (iOS, Android, Web)
515
+ - **Tonhub** - Full support (iOS, Android)
516
+ - **Wallet in Telegram** - Full support (iOS, Android)
517
+
518
+ **Note**: Wallet icons are automatically loaded from official sources. If an icon fails to load, a placeholder with the wallet's initial is shown.
375
519
 
376
520
  ## Migration from @tonconnect/ui-react
377
521
 
@@ -453,10 +597,133 @@ MIT
453
597
  For issues and questions:
454
598
  - GitHub Issues: [https://github.com/blaziumdev/ton-connect-mobile/issues](https://github.com/blaziumdev/ton-connect-mobile/issues)
455
599
 
456
- ## New Features in v1.2.0
600
+ ## New Features in v1.2.3
601
+
602
+ ### 🌐 Network Switching
603
+ Switch between mainnet and testnet dynamically:
604
+
605
+ ```typescript
606
+ // Initialize with network
607
+ const ton = new TonConnectMobile({
608
+ network: 'testnet', // or 'mainnet' (default)
609
+ // ... other config
610
+ });
611
+
612
+ // Or switch at runtime
613
+ ton.setNetwork('testnet');
614
+ tonConnectUI.setNetwork('testnet');
615
+
616
+ // Get current network
617
+ const network = ton.getNetwork(); // 'mainnet' or 'testnet'
618
+ ```
619
+
620
+ **Features:**
621
+ - Chain ID automatically updates (-239 for mainnet, -3 for testnet)
622
+ - TON API endpoint automatically switches based on network
623
+ - Warning logged if switching network while wallet is connected
624
+ - React components automatically update chain ID
625
+
626
+ ### 📡 Event Emitters
627
+ Listen to SDK events for reactive programming:
628
+
629
+ ```typescript
630
+ // Add event listeners
631
+ tonConnectUI.on('connect', (wallet) => {
632
+ console.log('Connected to:', wallet.name);
633
+ });
634
+
635
+ tonConnectUI.on('disconnect', () => {
636
+ console.log('Disconnected');
637
+ });
638
+
639
+ tonConnectUI.on('transaction', (tx) => {
640
+ console.log('Transaction sent:', tx.boc);
641
+ });
642
+
643
+ tonConnectUI.on('error', (error) => {
644
+ console.error('SDK error:', error);
645
+ });
646
+
647
+ // Remove listener
648
+ const unsubscribe = tonConnectUI.on('connect', listener);
649
+ unsubscribe(); // or
650
+ tonConnectUI.off('connect', listener);
651
+ ```
652
+
653
+ **Available Events:**
654
+ - `connect` - Fired when wallet connects
655
+ - `disconnect` - Fired when wallet disconnects
656
+ - `transaction` - Fired when transaction is sent
657
+ - `error` - Fired when an error occurs
658
+ - `statusChange` - Fired when connection status changes
659
+
660
+ ### 💰 Wallet Balance Checking
661
+ Get wallet balance from TON Center API:
662
+
663
+ ```typescript
664
+ // Get balance of connected wallet
665
+ const balance = await tonConnectUI.getBalance();
666
+
667
+ // Get balance of specific address
668
+ const balance = await tonConnectUI.getBalance('EQD0vdSA_NedR9uvbgN9EikRX-suesDxGeFg69XQMavfLqIo');
669
+
670
+ // Response:
671
+ // {
672
+ // balance: "1000000000", // in nanotons
673
+ // balanceTon: "1.0", // formatted TON
674
+ // network: "mainnet"
675
+ // }
676
+ ```
677
+
678
+ **Features:**
679
+ - Automatically uses correct API endpoint based on network
680
+ - Returns balance in both nanotons and formatted TON
681
+ - Validates address format before API call
682
+ - Handles API errors gracefully
683
+
684
+ ### 📊 Transaction Status Tracking
685
+ Track transaction status after sending:
686
+
687
+ ```typescript
688
+ // Using transaction hash (recommended)
689
+ const status = await tonConnectUI.getTransactionStatusByHash(txHash, address);
690
+
691
+ // Response:
692
+ // {
693
+ // status: "confirmed" | "pending" | "failed" | "unknown",
694
+ // hash: "transaction_hash",
695
+ // blockNumber: 12345,
696
+ // error?: "error message"
697
+ // }
698
+
699
+ // Using BOC (requires BOC parsing library)
700
+ const status = await tonConnectUI.getTransactionStatus(boc, maxAttempts, intervalMs);
701
+ ```
702
+
703
+ **Features:**
704
+ - Polling mechanism with configurable attempts and intervals
705
+ - Network-specific API endpoint selection
706
+ - Returns detailed status information
707
+ - Handles API errors gracefully
708
+
709
+ ### 🎯 Complete TonConnectUI API
710
+ All features from `@tonconnect/ui-react` are now available:
711
+
712
+ ```typescript
713
+ const tonConnectUI = useTonConnectUI();
714
+
715
+ // Restore connection from stored session
716
+ await tonConnectUI.restoreConnection();
717
+
718
+ // Customize available wallets
719
+ tonConnectUI.setWalletList([
720
+ { name: 'Tonkeeper', universalLink: '...', platforms: ['ios', 'android', 'web'] },
721
+ { name: 'MyTonWallet', universalLink: '...', platforms: ['ios', 'android', 'web'] },
722
+ ]);
723
+ ```
457
724
 
458
725
  ### 🎨 Wallet Selection Modal
459
- Beautiful, built-in wallet selection modal that automatically appears when you call `openModal()`:
726
+ Beautiful, built-in wallet selection modal with grid layout matching @tonconnect/ui-react design. Automatically appears when you call `openModal()`:
460
727
 
461
728
  ```typescript
462
729
  import { WalletSelectionModal } from '@blazium/ton-connect-mobile/react';
@@ -469,6 +736,14 @@ import { WalletSelectionModal } from '@blazium/ton-connect-mobile/react';
469
736
  />
470
737
  ```
471
738
 
739
+ **Features:**
740
+ - Grid layout (4 columns) matching @tonconnect/ui-react design
741
+ - Real wallet icons loaded from official sources
742
+ - Availability status for each wallet
743
+ - Automatic wallet filtering by platform
744
+ - Smooth animations and loading states
745
+ - Custom wallet list support via `setWalletList()`
746
+
472
747
  ### 🛠️ Transaction Builder Utilities
473
748
  Helper functions for building transactions easily:
474
749
 
@@ -533,6 +808,12 @@ if (isAvailable) {
533
808
  }
534
809
  ```
535
810
 
811
+ **Platform Detection:**
812
+ - On web: Checks if wallet has universal link support (can open in new tab)
813
+ - On mobile: Checks if wallet supports the current platform (iOS/Android)
814
+ - Uses adapter type for reliable platform detection
815
+ - All wallets with universal links are considered available on web
816
+
536
817
  ### 💬 Enhanced Error Messages
537
818
  All errors now include helpful recovery suggestions:
538
819
 
@@ -549,6 +830,23 @@ try {
549
830
 
550
831
  ## Changelog
551
832
 
833
+ ### v1.2.3
834
+ - ✅ **NEW**: Network switching - Switch between mainnet and testnet dynamically
835
+ - ✅ **NEW**: Event emitters - Listen to connect, disconnect, transaction, and error events
836
+ - ✅ **NEW**: Wallet balance checking - Get wallet balance via TON Center API integration
837
+ - ✅ **NEW**: Transaction status tracking - Track transaction status with polling mechanism
838
+ - ✅ **NEW**: Complete TonConnectUI API implementation - all features from @tonconnect/ui-react
839
+ - ✅ **NEW**: `restoreConnection()` method - restore connection from stored session
840
+ - ✅ **NEW**: `setWalletList()` method - customize available wallets in modal
841
+ - ✅ **NEW**: Wallet selection modal with grid layout matching @tonconnect/ui-react design
842
+ - ✅ **NEW**: Real wallet icons loaded from official sources
843
+ - ✅ **NEW**: Improved web platform support (Tonkeeper Web, MyTonWallet Web)
844
+ - ✅ **IMPROVED**: Wallet availability detection using adapter type (more reliable)
845
+ - ✅ **IMPROVED**: All wallets shown on web platform (with availability status)
846
+ - ✅ **IMPROVED**: Chain ID automatically updates when network changes
847
+ - ✅ **FIXED**: Tonkeeper now correctly shows as available on web
848
+ - ✅ **FIXED**: All Turkish comments translated to English
849
+
552
850
  ### v1.2.0
553
851
  - ✅ **NEW**: Beautiful wallet selection modal component
554
852
  - ✅ **NEW**: Transaction builder utilities (`buildTransferTransaction`, `tonToNano`, etc.)
@@ -67,15 +67,15 @@ class ExpoAdapter {
67
67
  else {
68
68
  console.log('[ExpoAdapter] Skipping canOpenURL check (Android compatibility)');
69
69
  }
70
- // CRITICAL FIX: Android'de canOpenURL() tonconnect:// protokolünü tanımayabilir
71
- // Bu yüzden direkt openURL() çağırıyoruz. Eğer açılamazsa hata fırlatır.
70
+ // CRITICAL FIX: On Android, canOpenURL() may not recognize tonconnect:// protocol
71
+ // So we call openURL() directly. If it fails, it will throw an error.
72
72
  await Linking.openURL(url);
73
73
  console.log('[ExpoAdapter] URL opened successfully');
74
74
  return true;
75
75
  }
76
76
  catch (error) {
77
77
  console.error('[ExpoAdapter] Error in openURL:', error);
78
- // Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
78
+ // On Android, if tonconnect:// protocol is not recognized or wallet is not installed, it will throw an error
79
79
  const errorMessage = error?.message || String(error);
80
80
  if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
81
81
  throw new Error('No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.');
@@ -55,15 +55,15 @@ class ReactNativeAdapter {
55
55
  else {
56
56
  console.log('[ReactNativeAdapter] Skipping canOpenURL check (Android compatibility)');
57
57
  }
58
- // CRITICAL FIX: Android'de canOpenURL() tonconnect:// protokolünü tanımayabilir
59
- // Bu yüzden direkt openURL() çağırıyoruz. Eğer açılamazsa hata fırlatır.
58
+ // CRITICAL FIX: On Android, canOpenURL() may not recognize tonconnect:// protocol
59
+ // So we call openURL() directly. If it fails, it will throw an error.
60
60
  await Linking.openURL(url);
61
61
  console.log('[ReactNativeAdapter] URL opened successfully');
62
62
  return true;
63
63
  }
64
64
  catch (error) {
65
65
  console.error('[ReactNativeAdapter] Error in openURL:', error);
66
- // Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
66
+ // On Android, if tonconnect:// protocol is not recognized or wallet is not installed, it will throw an error
67
67
  const errorMessage = error?.message || String(error);
68
68
  if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
69
69
  throw new Error('No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.');
@@ -35,6 +35,7 @@ export declare function parseCallbackURL(url: string, scheme: string): {
35
35
  };
36
36
  /**
37
37
  * Extract wallet info from connection response
38
+ * CRITICAL: This function assumes response has been validated by validateConnectionResponse
38
39
  */
39
40
  export declare function extractWalletInfo(response: ConnectionResponsePayload): WalletInfo;
40
41
  /**
@@ -195,12 +195,21 @@ function parseCallbackURL(url, scheme) {
195
195
  return { type: 'unknown', data: null };
196
196
  }
197
197
  // Extract encoded payload
198
- const encoded = url.substring(expectedPrefix.length);
198
+ let encoded = url.substring(expectedPrefix.length);
199
+ // CRITICAL FIX: Decode URL encoding first (wallet may URL-encode the payload)
200
+ try {
201
+ encoded = decodeURIComponent(encoded);
202
+ }
203
+ catch (error) {
204
+ // If decodeURIComponent fails, try using the original encoded string
205
+ // Some wallets may not URL-encode the payload
206
+ console.log('[TON Connect] Payload not URL-encoded, using as-is');
207
+ }
199
208
  // CRITICAL FIX: Validate base64 payload size (prevent DoS)
200
209
  if (encoded.length === 0 || encoded.length > 5000) {
201
210
  return { type: 'unknown', data: null };
202
211
  }
203
- // CRITICAL FIX: Validate base64 characters only
212
+ // CRITICAL FIX: Validate base64 characters only (after URL decoding)
204
213
  if (!/^[A-Za-z0-9_-]+$/.test(encoded)) {
205
214
  return { type: 'unknown', data: null };
206
215
  }
@@ -241,12 +250,17 @@ function parseCallbackURL(url, scheme) {
241
250
  }
242
251
  /**
243
252
  * Extract wallet info from connection response
253
+ * CRITICAL: This function assumes response has been validated by validateConnectionResponse
244
254
  */
245
255
  function extractWalletInfo(response) {
256
+ // CRITICAL FIX: Add null checks to prevent runtime errors
257
+ if (!response || !response.name || !response.address || !response.publicKey) {
258
+ throw new Error('Invalid connection response: missing required fields');
259
+ }
246
260
  return {
247
261
  name: response.name,
248
- appName: response.appName,
249
- version: response.version,
262
+ appName: response.appName || response.name,
263
+ version: response.version || 'unknown',
250
264
  platform: response.platform || 'unknown',
251
265
  address: response.address,
252
266
  publicKey: response.publicKey,
@@ -280,13 +294,59 @@ function validateTransactionRequest(request) {
280
294
  if (!request.messages || request.messages.length === 0) {
281
295
  return { valid: false, error: 'Transaction must have at least one message' };
282
296
  }
283
- for (const msg of request.messages) {
284
- if (!msg.address) {
285
- return { valid: false, error: 'Message address is required' };
297
+ // CRITICAL: Validate each message
298
+ for (let i = 0; i < request.messages.length; i++) {
299
+ const msg = request.messages[i];
300
+ // Validate address
301
+ if (!msg.address || typeof msg.address !== 'string') {
302
+ return { valid: false, error: `Message ${i + 1}: Address is required and must be a string` };
303
+ }
304
+ // CRITICAL: Validate TON address format (EQ... or 0Q...)
305
+ if (!/^(EQ|0Q)[A-Za-z0-9_-]{46}$/.test(msg.address)) {
306
+ return { valid: false, error: `Message ${i + 1}: Invalid TON address format. Address must start with EQ or 0Q and be 48 characters long.` };
307
+ }
308
+ // Validate amount
309
+ if (!msg.amount || typeof msg.amount !== 'string') {
310
+ return { valid: false, error: `Message ${i + 1}: Amount is required and must be a string (nanotons)` };
311
+ }
312
+ // CRITICAL: Validate amount is a valid positive number (nanotons)
313
+ try {
314
+ const amount = BigInt(msg.amount);
315
+ if (amount <= 0n) {
316
+ return { valid: false, error: `Message ${i + 1}: Amount must be greater than 0` };
317
+ }
318
+ // Check for reasonable maximum (prevent overflow)
319
+ if (amount > BigInt('1000000000000000000')) { // 1 billion TON
320
+ return { valid: false, error: `Message ${i + 1}: Amount exceeds maximum allowed (1 billion TON)` };
321
+ }
286
322
  }
287
- if (!msg.amount || isNaN(Number(msg.amount))) {
288
- return { valid: false, error: 'Message amount must be a valid number' };
323
+ catch (error) {
324
+ return { valid: false, error: `Message ${i + 1}: Amount must be a valid number string (nanotons)` };
289
325
  }
326
+ // Validate payload if provided (must be base64)
327
+ if (msg.payload !== undefined && msg.payload !== null) {
328
+ if (typeof msg.payload !== 'string') {
329
+ return { valid: false, error: `Message ${i + 1}: Payload must be a base64 string` };
330
+ }
331
+ // Basic base64 validation
332
+ if (msg.payload.length > 0 && !/^[A-Za-z0-9+/=]+$/.test(msg.payload)) {
333
+ return { valid: false, error: `Message ${i + 1}: Payload must be valid base64 encoded` };
334
+ }
335
+ }
336
+ // Validate stateInit if provided (must be base64)
337
+ if (msg.stateInit !== undefined && msg.stateInit !== null) {
338
+ if (typeof msg.stateInit !== 'string') {
339
+ return { valid: false, error: `Message ${i + 1}: StateInit must be a base64 string` };
340
+ }
341
+ // Basic base64 validation
342
+ if (msg.stateInit.length > 0 && !/^[A-Za-z0-9+/=]+$/.test(msg.stateInit)) {
343
+ return { valid: false, error: `Message ${i + 1}: StateInit must be valid base64 encoded` };
344
+ }
345
+ }
346
+ }
347
+ // CRITICAL: Limit maximum number of messages (prevent DoS)
348
+ if (request.messages.length > 255) {
349
+ return { valid: false, error: 'Transaction cannot have more than 255 messages' };
290
350
  }
291
351
  return { valid: true };
292
352
  }
@@ -17,7 +17,8 @@ exports.SUPPORTED_WALLETS = [
17
17
  appName: 'Tonkeeper',
18
18
  universalLink: 'https://app.tonkeeper.com/ton-connect',
19
19
  deepLink: 'tonkeeper://',
20
- platforms: ['ios', 'android'],
20
+ iconUrl: 'https://tonkeeper.com/assets/tonconnect-icon.png',
21
+ platforms: ['ios', 'android', 'web'], // CRITICAL FIX: Tonkeeper Web is supported
21
22
  preferredReturnStrategy: 'post_redirect', // CRITICAL FIX: 'back' strategy may not send callback properly, use 'post_redirect'
22
23
  requiresReturnScheme: true, // CRITICAL FIX: Mobile apps need returnScheme for proper callback handling
23
24
  },
@@ -26,6 +27,7 @@ exports.SUPPORTED_WALLETS = [
26
27
  appName: 'MyTonWallet',
27
28
  universalLink: 'https://connect.mytonwallet.org',
28
29
  deepLink: 'mytonwallet://',
30
+ iconUrl: 'https://static.mytonwallet.io/icon-256.png',
29
31
  platforms: ['ios', 'android', 'web'],
30
32
  preferredReturnStrategy: 'post_redirect',
31
33
  requiresReturnScheme: true, // MyTonWallet requires explicit returnScheme
@@ -35,6 +37,7 @@ exports.SUPPORTED_WALLETS = [
35
37
  appName: 'Wallet',
36
38
  universalLink: 'https://wallet.tonapi.io/ton-connect',
37
39
  deepLink: 'tg://',
40
+ iconUrl: 'https://wallet.tonapi.io/icon.png',
38
41
  platforms: ['ios', 'android'],
39
42
  preferredReturnStrategy: 'post_redirect',
40
43
  requiresReturnScheme: true, // Telegram Wallet requires explicit returnScheme
@@ -44,6 +47,7 @@ exports.SUPPORTED_WALLETS = [
44
47
  appName: 'Tonhub',
45
48
  universalLink: 'https://tonhub.com/ton-connect',
46
49
  deepLink: 'tonhub://',
50
+ iconUrl: 'https://tonhub.com/tonconnect_logo.png',
47
51
  platforms: ['ios', 'android'],
48
52
  preferredReturnStrategy: 'post_redirect',
49
53
  requiresReturnScheme: true, // Tonhub requires explicit returnScheme for proper callback