@blazium/ton-connect-mobile 1.2.1 → 1.2.4
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 +329 -14
- package/dist/core/wallets.js +5 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +274 -4
- package/dist/react/TonConnectUIProvider.d.ts +21 -2
- package/dist/react/TonConnectUIProvider.js +72 -3
- package/dist/react/WalletSelectionModal.d.ts +1 -0
- package/dist/react/WalletSelectionModal.js +143 -80
- package/dist/react/index.d.ts +1 -0
- package/dist/types/index.d.ts +46 -0
- package/package.json +1 -1
- package/src/core/wallets.ts +5 -1
- package/src/index.ts +337 -6
- package/src/react/TonConnectUIProvider.tsx +109 -4
- package/src/react/WalletSelectionModal.tsx +178 -91
- package/src/react/index.ts +1 -0
- package/src/types/index.ts +52 -0
package/dist/index.js
CHANGED
|
@@ -78,6 +78,7 @@ exports.TransactionInProgressError = TransactionInProgressError;
|
|
|
78
78
|
class TonConnectMobile {
|
|
79
79
|
constructor(config) {
|
|
80
80
|
this.statusChangeCallbacks = new Set();
|
|
81
|
+
this.eventListeners = new Map();
|
|
81
82
|
this.currentStatus = { connected: false, wallet: null };
|
|
82
83
|
this.urlUnsubscribe = null;
|
|
83
84
|
this.connectionPromise = null;
|
|
@@ -90,12 +91,23 @@ class TonConnectMobile {
|
|
|
90
91
|
if (!config.scheme) {
|
|
91
92
|
throw new TonConnectError('scheme is required');
|
|
92
93
|
}
|
|
94
|
+
// Validate network
|
|
95
|
+
const network = config.network || 'mainnet';
|
|
96
|
+
if (network !== 'mainnet' && network !== 'testnet') {
|
|
97
|
+
throw new TonConnectError('Network must be either "mainnet" or "testnet"');
|
|
98
|
+
}
|
|
99
|
+
// Set default TON API endpoint based on network
|
|
100
|
+
const defaultTonApiEndpoint = network === 'testnet'
|
|
101
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
102
|
+
: 'https://toncenter.com/api/v2';
|
|
93
103
|
this.config = {
|
|
94
104
|
storageKeyPrefix: 'tonconnect_',
|
|
95
105
|
connectionTimeout: 300000, // 5 minutes
|
|
96
106
|
transactionTimeout: 300000, // 5 minutes
|
|
97
107
|
skipCanOpenURLCheck: true, // Skip canOpenURL check by default (Android issue)
|
|
98
108
|
preferredWallet: config.preferredWallet,
|
|
109
|
+
network,
|
|
110
|
+
tonApiEndpoint: config.tonApiEndpoint || defaultTonApiEndpoint,
|
|
99
111
|
...config,
|
|
100
112
|
};
|
|
101
113
|
// Determine which wallet to use
|
|
@@ -116,6 +128,7 @@ class TonConnectMobile {
|
|
|
116
128
|
console.log('[TON Connect] Initializing SDK with config:', {
|
|
117
129
|
manifestUrl: this.config.manifestUrl,
|
|
118
130
|
scheme: this.config.scheme,
|
|
131
|
+
network: this.config.network,
|
|
119
132
|
wallet: this.currentWallet.name,
|
|
120
133
|
universalLink: this.currentWallet.universalLink,
|
|
121
134
|
});
|
|
@@ -284,6 +297,8 @@ class TonConnectMobile {
|
|
|
284
297
|
// Update status
|
|
285
298
|
this.currentStatus = { connected: true, wallet };
|
|
286
299
|
this.notifyStatusChange();
|
|
300
|
+
// Emit connect event
|
|
301
|
+
this.emit('connect', wallet);
|
|
287
302
|
// Resolve connection promise
|
|
288
303
|
// CRITICAL: Only resolve if promise still exists and hasn't timed out
|
|
289
304
|
if (this.connectionPromise) {
|
|
@@ -307,6 +322,12 @@ class TonConnectMobile {
|
|
|
307
322
|
this.rejectWithError(new TonConnectError('Invalid transaction response'));
|
|
308
323
|
return;
|
|
309
324
|
}
|
|
325
|
+
const transactionResult = {
|
|
326
|
+
boc: response.boc,
|
|
327
|
+
signature: response.signature,
|
|
328
|
+
};
|
|
329
|
+
// Emit transaction event
|
|
330
|
+
this.emit('transaction', transactionResult);
|
|
310
331
|
// Resolve transaction promise
|
|
311
332
|
// CRITICAL: Only resolve if promise still exists and hasn't timed out
|
|
312
333
|
if (this.transactionPromise) {
|
|
@@ -319,16 +340,15 @@ class TonConnectMobile {
|
|
|
319
340
|
// Clear promise first to prevent race conditions
|
|
320
341
|
this.transactionPromise = null;
|
|
321
342
|
// Then resolve
|
|
322
|
-
promise.resolve(
|
|
323
|
-
boc: response.boc,
|
|
324
|
-
signature: response.signature,
|
|
325
|
-
});
|
|
343
|
+
promise.resolve(transactionResult);
|
|
326
344
|
}
|
|
327
345
|
}
|
|
328
346
|
/**
|
|
329
347
|
* Reject current promise with error
|
|
330
348
|
*/
|
|
331
349
|
rejectWithError(error) {
|
|
350
|
+
// Emit error event
|
|
351
|
+
this.emit('error', error);
|
|
332
352
|
if (this.connectionPromise) {
|
|
333
353
|
if (this.connectionPromise.timeout !== null) {
|
|
334
354
|
clearTimeout(this.connectionPromise.timeout);
|
|
@@ -648,6 +668,8 @@ class TonConnectMobile {
|
|
|
648
668
|
// Update status
|
|
649
669
|
this.currentStatus = { connected: false, wallet: null };
|
|
650
670
|
this.notifyStatusChange();
|
|
671
|
+
// Emit disconnect event
|
|
672
|
+
this.emit('disconnect', null);
|
|
651
673
|
}
|
|
652
674
|
/**
|
|
653
675
|
* Get current connection status
|
|
@@ -738,6 +760,60 @@ class TonConnectMobile {
|
|
|
738
760
|
// Ignore errors in callbacks
|
|
739
761
|
}
|
|
740
762
|
});
|
|
763
|
+
// Emit statusChange event
|
|
764
|
+
this.emit('statusChange', status);
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Emit event to all listeners
|
|
768
|
+
*/
|
|
769
|
+
emit(event, data) {
|
|
770
|
+
const listeners = this.eventListeners.get(event);
|
|
771
|
+
if (listeners) {
|
|
772
|
+
listeners.forEach((listener) => {
|
|
773
|
+
try {
|
|
774
|
+
listener(data);
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
console.error(`[TON Connect] Error in event listener for ${event}:`, error);
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Add event listener
|
|
784
|
+
*/
|
|
785
|
+
on(event, listener) {
|
|
786
|
+
if (!this.eventListeners.has(event)) {
|
|
787
|
+
this.eventListeners.set(event, new Set());
|
|
788
|
+
}
|
|
789
|
+
this.eventListeners.get(event).add(listener);
|
|
790
|
+
// Return unsubscribe function
|
|
791
|
+
return () => {
|
|
792
|
+
const listeners = this.eventListeners.get(event);
|
|
793
|
+
if (listeners) {
|
|
794
|
+
listeners.delete(listener);
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Remove event listener
|
|
800
|
+
*/
|
|
801
|
+
off(event, listener) {
|
|
802
|
+
const listeners = this.eventListeners.get(event);
|
|
803
|
+
if (listeners) {
|
|
804
|
+
listeners.delete(listener);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Remove all listeners for an event
|
|
809
|
+
*/
|
|
810
|
+
removeAllListeners(event) {
|
|
811
|
+
if (event) {
|
|
812
|
+
this.eventListeners.delete(event);
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
this.eventListeners.clear();
|
|
816
|
+
}
|
|
741
817
|
}
|
|
742
818
|
/**
|
|
743
819
|
* Validate session ID format
|
|
@@ -841,10 +917,204 @@ class TonConnectMobile {
|
|
|
841
917
|
this.adapter.destroy();
|
|
842
918
|
}
|
|
843
919
|
this.statusChangeCallbacks.clear();
|
|
920
|
+
this.eventListeners.clear();
|
|
844
921
|
this.connectionPromise = null;
|
|
845
922
|
this.transactionPromise = null;
|
|
846
923
|
this.signDataPromise = null;
|
|
847
924
|
}
|
|
925
|
+
/**
|
|
926
|
+
* Get current network
|
|
927
|
+
*/
|
|
928
|
+
getNetwork() {
|
|
929
|
+
return this.config.network;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Set network (mainnet/testnet)
|
|
933
|
+
*/
|
|
934
|
+
setNetwork(network) {
|
|
935
|
+
if (network !== 'mainnet' && network !== 'testnet') {
|
|
936
|
+
throw new TonConnectError('Network must be either "mainnet" or "testnet"');
|
|
937
|
+
}
|
|
938
|
+
const oldNetwork = this.config.network;
|
|
939
|
+
// Warn if switching network while connected (wallet connection is network-specific)
|
|
940
|
+
if (this.currentStatus.connected && oldNetwork !== network) {
|
|
941
|
+
console.warn('[TON Connect] Network changed while wallet is connected. ' +
|
|
942
|
+
'The wallet connection may be invalid for the new network. ' +
|
|
943
|
+
'Consider disconnecting and reconnecting after network change.');
|
|
944
|
+
}
|
|
945
|
+
this.config.network = network;
|
|
946
|
+
// Update TON API endpoint if not explicitly set
|
|
947
|
+
if (!this.config.tonApiEndpoint || this.config.tonApiEndpoint.includes(oldNetwork)) {
|
|
948
|
+
this.config.tonApiEndpoint =
|
|
949
|
+
network === 'testnet'
|
|
950
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
951
|
+
: 'https://toncenter.com/api/v2';
|
|
952
|
+
}
|
|
953
|
+
console.log('[TON Connect] Network changed to:', network);
|
|
954
|
+
// Notify status change to update chain ID in React components
|
|
955
|
+
this.notifyStatusChange();
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Get wallet balance
|
|
959
|
+
*/
|
|
960
|
+
async getBalance(address) {
|
|
961
|
+
const targetAddress = address || this.currentStatus.wallet?.address;
|
|
962
|
+
if (!targetAddress) {
|
|
963
|
+
throw new TonConnectError('Address is required. Either connect a wallet or provide an address.');
|
|
964
|
+
}
|
|
965
|
+
// Validate address format
|
|
966
|
+
if (!/^[0-9A-Za-z_-]{48}$/.test(targetAddress)) {
|
|
967
|
+
throw new TonConnectError('Invalid TON address format');
|
|
968
|
+
}
|
|
969
|
+
try {
|
|
970
|
+
const apiEndpoint = this.config.tonApiEndpoint ||
|
|
971
|
+
(this.config.network === 'testnet'
|
|
972
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
973
|
+
: 'https://toncenter.com/api/v2');
|
|
974
|
+
const url = `${apiEndpoint}/getAddressInformation?address=${encodeURIComponent(targetAddress)}`;
|
|
975
|
+
const response = await fetch(url, {
|
|
976
|
+
method: 'GET',
|
|
977
|
+
headers: {
|
|
978
|
+
'Accept': 'application/json',
|
|
979
|
+
},
|
|
980
|
+
});
|
|
981
|
+
if (!response.ok) {
|
|
982
|
+
throw new TonConnectError(`Failed to fetch balance: ${response.status} ${response.statusText}`);
|
|
983
|
+
}
|
|
984
|
+
const data = await response.json();
|
|
985
|
+
if (data.ok === false) {
|
|
986
|
+
throw new TonConnectError(data.error || 'Failed to fetch balance');
|
|
987
|
+
}
|
|
988
|
+
// TON Center API returns balance in nanotons
|
|
989
|
+
const balance = data.result?.balance || '0';
|
|
990
|
+
const balanceTon = (BigInt(balance) / BigInt(1000000000)).toString() + '.' +
|
|
991
|
+
(BigInt(balance) % BigInt(1000000000)).toString().padStart(9, '0').replace(/0+$/, '');
|
|
992
|
+
return {
|
|
993
|
+
balance,
|
|
994
|
+
balanceTon: balanceTon === '0.' ? '0' : balanceTon,
|
|
995
|
+
network: this.config.network,
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
catch (error) {
|
|
999
|
+
if (error instanceof TonConnectError) {
|
|
1000
|
+
throw error;
|
|
1001
|
+
}
|
|
1002
|
+
throw new TonConnectError(`Failed to get balance: ${error?.message || String(error)}`);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Get transaction status
|
|
1007
|
+
*/
|
|
1008
|
+
async getTransactionStatus(boc, maxAttempts = 10, intervalMs = 2000) {
|
|
1009
|
+
if (!boc || typeof boc !== 'string' || boc.length === 0) {
|
|
1010
|
+
throw new TonConnectError('Transaction BOC is required');
|
|
1011
|
+
}
|
|
1012
|
+
// Extract transaction hash from BOC (simplified - in production, you'd parse the BOC properly)
|
|
1013
|
+
// For now, we'll use a polling approach with TON Center API
|
|
1014
|
+
try {
|
|
1015
|
+
const apiEndpoint = this.config.tonApiEndpoint ||
|
|
1016
|
+
(this.config.network === 'testnet'
|
|
1017
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
1018
|
+
: 'https://toncenter.com/api/v2');
|
|
1019
|
+
// Try to get transaction info
|
|
1020
|
+
// Note: This is a simplified implementation. In production, you'd need to:
|
|
1021
|
+
// 1. Parse the BOC to extract transaction hash
|
|
1022
|
+
// 2. Query the blockchain for transaction status
|
|
1023
|
+
// 3. Handle different confirmation states
|
|
1024
|
+
// For now, we'll return a basic status
|
|
1025
|
+
// In a real implementation, you'd query the blockchain API
|
|
1026
|
+
let attempts = 0;
|
|
1027
|
+
let lastError = null;
|
|
1028
|
+
while (attempts < maxAttempts) {
|
|
1029
|
+
try {
|
|
1030
|
+
// This is a placeholder - you'd need to implement actual transaction lookup
|
|
1031
|
+
// For now, we'll simulate checking
|
|
1032
|
+
await new Promise((resolve) => setTimeout(() => resolve(), intervalMs));
|
|
1033
|
+
// In production, you would:
|
|
1034
|
+
// 1. Parse BOC to get transaction hash
|
|
1035
|
+
// 2. Query TON API: GET /getTransactions?address=...&limit=1
|
|
1036
|
+
// 3. Check if transaction exists and is confirmed
|
|
1037
|
+
// For now, return unknown status (as we can't parse BOC without additional libraries)
|
|
1038
|
+
return {
|
|
1039
|
+
status: 'unknown',
|
|
1040
|
+
error: 'Transaction status checking requires BOC parsing. Please use a TON library to parse the BOC and extract the transaction hash.',
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
catch (error) {
|
|
1044
|
+
lastError = error;
|
|
1045
|
+
attempts++;
|
|
1046
|
+
if (attempts < maxAttempts) {
|
|
1047
|
+
await new Promise((resolve) => setTimeout(() => resolve(), intervalMs));
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return {
|
|
1052
|
+
status: 'failed',
|
|
1053
|
+
error: lastError?.message || 'Failed to check transaction status',
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
catch (error) {
|
|
1057
|
+
throw new TonConnectError(`Failed to get transaction status: ${error?.message || String(error)}`);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Get transaction status by hash (more reliable than BOC)
|
|
1062
|
+
*/
|
|
1063
|
+
async getTransactionStatusByHash(txHash, address) {
|
|
1064
|
+
if (!txHash || typeof txHash !== 'string' || txHash.length === 0) {
|
|
1065
|
+
throw new TonConnectError('Transaction hash is required');
|
|
1066
|
+
}
|
|
1067
|
+
if (!address || typeof address !== 'string' || address.length === 0) {
|
|
1068
|
+
throw new TonConnectError('Address is required');
|
|
1069
|
+
}
|
|
1070
|
+
try {
|
|
1071
|
+
const apiEndpoint = this.config.tonApiEndpoint ||
|
|
1072
|
+
(this.config.network === 'testnet'
|
|
1073
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
1074
|
+
: 'https://toncenter.com/api/v2');
|
|
1075
|
+
// Query transactions for the address
|
|
1076
|
+
const url = `${apiEndpoint}/getTransactions?address=${encodeURIComponent(address)}&limit=100`;
|
|
1077
|
+
const response = await fetch(url, {
|
|
1078
|
+
method: 'GET',
|
|
1079
|
+
headers: {
|
|
1080
|
+
'Accept': 'application/json',
|
|
1081
|
+
},
|
|
1082
|
+
});
|
|
1083
|
+
if (!response.ok) {
|
|
1084
|
+
throw new TonConnectError(`Failed to fetch transactions: ${response.status} ${response.statusText}`);
|
|
1085
|
+
}
|
|
1086
|
+
const data = await response.json();
|
|
1087
|
+
if (data.ok === false) {
|
|
1088
|
+
throw new TonConnectError(data.error || 'Failed to fetch transactions');
|
|
1089
|
+
}
|
|
1090
|
+
// Search for transaction with matching hash
|
|
1091
|
+
const transactions = data.result || [];
|
|
1092
|
+
const transaction = transactions.find((tx) => tx.transaction_id?.hash === txHash ||
|
|
1093
|
+
tx.transaction_id?.lt === txHash ||
|
|
1094
|
+
JSON.stringify(tx.transaction_id).includes(txHash));
|
|
1095
|
+
if (transaction) {
|
|
1096
|
+
return {
|
|
1097
|
+
status: 'confirmed',
|
|
1098
|
+
hash: transaction.transaction_id?.hash || txHash,
|
|
1099
|
+
blockNumber: transaction.transaction_id?.lt,
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
// Transaction not found - could be pending or failed
|
|
1103
|
+
return {
|
|
1104
|
+
status: 'pending',
|
|
1105
|
+
hash: txHash,
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
catch (error) {
|
|
1109
|
+
if (error instanceof TonConnectError) {
|
|
1110
|
+
throw error;
|
|
1111
|
+
}
|
|
1112
|
+
return {
|
|
1113
|
+
status: 'failed',
|
|
1114
|
+
error: error?.message || 'Failed to check transaction status',
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
848
1118
|
}
|
|
849
1119
|
exports.TonConnectMobile = TonConnectMobile;
|
|
850
1120
|
// Export types
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Provides TonConnectUIProvider, hooks, and components compatible with @tonconnect/ui-react API
|
|
4
4
|
*/
|
|
5
5
|
import { ReactNode } from 'react';
|
|
6
|
-
import { TonConnectMobile, WalletInfo, SendTransactionRequest } from '../index';
|
|
7
|
-
import type { TonConnectMobileConfig } from '../types';
|
|
6
|
+
import { TonConnectMobile, WalletInfo, SendTransactionRequest, WalletDefinition, Network, BalanceResponse, TransactionStatusResponse } from '../index';
|
|
7
|
+
import type { TonConnectMobileConfig, TonConnectEventType, TonConnectEventListener } from '../types';
|
|
8
8
|
/**
|
|
9
9
|
* Account information (compatible with @tonconnect/ui-react)
|
|
10
10
|
*/
|
|
@@ -46,6 +46,7 @@ export interface SignDataResponse {
|
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
48
|
* TonConnect UI instance interface (compatible with @tonconnect/ui-react)
|
|
49
|
+
* Includes all features from @tonconnect/ui-react for full compatibility
|
|
49
50
|
*/
|
|
50
51
|
export interface TonConnectUI {
|
|
51
52
|
/** Open connection modal */
|
|
@@ -60,6 +61,24 @@ export interface TonConnectUI {
|
|
|
60
61
|
sendTransaction: (transaction: SendTransactionRequest) => Promise<TransactionResponse>;
|
|
61
62
|
/** Sign data */
|
|
62
63
|
signData: (request: SignDataRequest) => Promise<SignDataResponse>;
|
|
64
|
+
/** Restore connection from stored session */
|
|
65
|
+
restoreConnection: () => Promise<void>;
|
|
66
|
+
/** Set wallet list (customize available wallets) */
|
|
67
|
+
setWalletList: (wallets: WalletDefinition[]) => void;
|
|
68
|
+
/** Get current network */
|
|
69
|
+
getNetwork: () => Network;
|
|
70
|
+
/** Set network (mainnet/testnet) */
|
|
71
|
+
setNetwork: (network: Network) => void;
|
|
72
|
+
/** Get wallet balance */
|
|
73
|
+
getBalance: (address?: string) => Promise<BalanceResponse>;
|
|
74
|
+
/** Get transaction status */
|
|
75
|
+
getTransactionStatus: (boc: string, maxAttempts?: number, intervalMs?: number) => Promise<TransactionStatusResponse>;
|
|
76
|
+
/** Get transaction status by hash */
|
|
77
|
+
getTransactionStatusByHash: (txHash: string, address: string) => Promise<TransactionStatusResponse>;
|
|
78
|
+
/** Add event listener */
|
|
79
|
+
on: <T = any>(event: TonConnectEventType, listener: TonConnectEventListener<T>) => () => void;
|
|
80
|
+
/** Remove event listener */
|
|
81
|
+
off: <T = any>(event: TonConnectEventType, listener: TonConnectEventListener<T>) => void;
|
|
63
82
|
/** Current wallet state */
|
|
64
83
|
wallet: WalletState | null;
|
|
65
84
|
/** Modal open state */
|
|
@@ -67,13 +67,21 @@ function TonConnectUIProvider({ config, children, sdkInstance, }) {
|
|
|
67
67
|
const [walletState, setWalletState] = (0, react_1.useState)(null);
|
|
68
68
|
const [modalOpen, setModalOpen] = (0, react_1.useState)(false);
|
|
69
69
|
const [isConnecting, setIsConnecting] = (0, react_1.useState)(false);
|
|
70
|
+
const [customWalletList, setCustomWalletList] = (0, react_1.useState)(null);
|
|
71
|
+
// Get chain ID based on network
|
|
72
|
+
const getChainId = (0, react_1.useCallback)((network) => {
|
|
73
|
+
// TON mainnet chain ID: -239
|
|
74
|
+
// TON testnet chain ID: -3
|
|
75
|
+
return network === 'testnet' ? -3 : -239;
|
|
76
|
+
}, []);
|
|
70
77
|
// Update wallet state from SDK status
|
|
71
78
|
const updateWalletState = (0, react_1.useCallback)((status) => {
|
|
72
79
|
if (status.connected && status.wallet) {
|
|
80
|
+
const network = sdk.getNetwork();
|
|
73
81
|
setWalletState({
|
|
74
82
|
account: {
|
|
75
83
|
address: status.wallet.address,
|
|
76
|
-
chain:
|
|
84
|
+
chain: getChainId(network),
|
|
77
85
|
publicKey: status.wallet.publicKey,
|
|
78
86
|
},
|
|
79
87
|
wallet: status.wallet,
|
|
@@ -87,7 +95,7 @@ function TonConnectUIProvider({ config, children, sdkInstance, }) {
|
|
|
87
95
|
connected: false,
|
|
88
96
|
});
|
|
89
97
|
}
|
|
90
|
-
}, []);
|
|
98
|
+
}, [sdk, getChainId]);
|
|
91
99
|
// Subscribe to SDK status changes
|
|
92
100
|
(0, react_1.useEffect)(() => {
|
|
93
101
|
// Set initial state
|
|
@@ -184,6 +192,58 @@ function TonConnectUIProvider({ config, children, sdkInstance, }) {
|
|
|
184
192
|
throw error;
|
|
185
193
|
}
|
|
186
194
|
}, [sdk]);
|
|
195
|
+
// Restore connection from stored session
|
|
196
|
+
const restoreConnection = (0, react_1.useCallback)(async () => {
|
|
197
|
+
try {
|
|
198
|
+
// SDK automatically loads session on initialization
|
|
199
|
+
// This method triggers a re-check of the stored session
|
|
200
|
+
const status = sdk.getStatus();
|
|
201
|
+
if (status.connected && status.wallet) {
|
|
202
|
+
updateWalletState(status);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error('[TonConnectUIProvider] Restore connection error:', error);
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
}, [sdk, updateWalletState]);
|
|
210
|
+
// Set wallet list (customize available wallets)
|
|
211
|
+
const setWalletList = (0, react_1.useCallback)((wallets) => {
|
|
212
|
+
if (!wallets || !Array.isArray(wallets)) {
|
|
213
|
+
throw new Error('Wallet list must be an array');
|
|
214
|
+
}
|
|
215
|
+
setCustomWalletList(wallets);
|
|
216
|
+
}, []);
|
|
217
|
+
// Get network
|
|
218
|
+
const getNetwork = (0, react_1.useCallback)(() => {
|
|
219
|
+
return sdk.getNetwork();
|
|
220
|
+
}, [sdk]);
|
|
221
|
+
// Set network
|
|
222
|
+
const setNetwork = (0, react_1.useCallback)((network) => {
|
|
223
|
+
sdk.setNetwork(network);
|
|
224
|
+
// Update wallet state to reflect new chain ID
|
|
225
|
+
const status = sdk.getStatus();
|
|
226
|
+
updateWalletState(status);
|
|
227
|
+
}, [sdk, updateWalletState]);
|
|
228
|
+
// Get balance
|
|
229
|
+
const getBalance = (0, react_1.useCallback)(async (address) => {
|
|
230
|
+
return await sdk.getBalance(address);
|
|
231
|
+
}, [sdk]);
|
|
232
|
+
// Get transaction status
|
|
233
|
+
const getTransactionStatus = (0, react_1.useCallback)(async (boc, maxAttempts = 10, intervalMs = 2000) => {
|
|
234
|
+
return await sdk.getTransactionStatus(boc, maxAttempts, intervalMs);
|
|
235
|
+
}, [sdk]);
|
|
236
|
+
// Get transaction status by hash
|
|
237
|
+
const getTransactionStatusByHash = (0, react_1.useCallback)(async (txHash, address) => {
|
|
238
|
+
return await sdk.getTransactionStatusByHash(txHash, address);
|
|
239
|
+
}, [sdk]);
|
|
240
|
+
// Event listeners
|
|
241
|
+
const on = (0, react_1.useCallback)((event, listener) => {
|
|
242
|
+
return sdk.on(event, listener);
|
|
243
|
+
}, [sdk]);
|
|
244
|
+
const off = (0, react_1.useCallback)((event, listener) => {
|
|
245
|
+
sdk.off(event, listener);
|
|
246
|
+
}, [sdk]);
|
|
187
247
|
// Create TonConnectUI instance
|
|
188
248
|
const tonConnectUI = {
|
|
189
249
|
openModal,
|
|
@@ -192,6 +252,15 @@ function TonConnectUIProvider({ config, children, sdkInstance, }) {
|
|
|
192
252
|
disconnect,
|
|
193
253
|
sendTransaction,
|
|
194
254
|
signData,
|
|
255
|
+
restoreConnection,
|
|
256
|
+
setWalletList,
|
|
257
|
+
getNetwork,
|
|
258
|
+
setNetwork,
|
|
259
|
+
getBalance,
|
|
260
|
+
getTransactionStatus,
|
|
261
|
+
getTransactionStatusByHash,
|
|
262
|
+
on,
|
|
263
|
+
off,
|
|
195
264
|
wallet: walletState,
|
|
196
265
|
modalState: {
|
|
197
266
|
open: modalOpen,
|
|
@@ -204,7 +273,7 @@ function TonConnectUIProvider({ config, children, sdkInstance, }) {
|
|
|
204
273
|
};
|
|
205
274
|
return (react_1.default.createElement(TonConnectUIContext.Provider, { value: contextValue },
|
|
206
275
|
children,
|
|
207
|
-
react_1.default.createElement(WalletSelectionModal_1.WalletSelectionModal, { visible: modalOpen && !walletState?.connected, onClose: closeModal })));
|
|
276
|
+
react_1.default.createElement(WalletSelectionModal_1.WalletSelectionModal, { visible: modalOpen && !walletState?.connected, onClose: closeModal, wallets: customWalletList || undefined })));
|
|
208
277
|
}
|
|
209
278
|
/**
|
|
210
279
|
* Hook to access TonConnectUI instance
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WalletSelectionModal component
|
|
3
3
|
* Provides a beautiful wallet selection UI compatible with @tonconnect/ui-react
|
|
4
|
+
* Matches the exact UI/UX of @tonconnect/ui-react wallet selection modal
|
|
4
5
|
*/
|
|
5
6
|
import type { WalletDefinition } from '../index';
|
|
6
7
|
export interface WalletSelectionModalProps {
|