@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 +316 -18
- package/dist/adapters/expo.js +3 -3
- package/dist/adapters/react-native.js +3 -3
- package/dist/core/protocol.d.ts +1 -0
- package/dist/core/protocol.js +69 -9
- package/dist/core/wallets.js +5 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +309 -13
- package/dist/react/TonConnectUIProvider.d.ts +21 -2
- package/dist/react/TonConnectUIProvider.js +118 -14
- package/dist/react/WalletSelectionModal.d.ts +1 -0
- package/dist/react/WalletSelectionModal.js +153 -80
- package/dist/react/index.d.ts +1 -0
- package/dist/types/index.d.ts +46 -0
- package/dist/utils/transactionBuilder.js +50 -7
- package/package.json +1 -1
- package/src/adapters/expo.ts +3 -3
- package/src/adapters/react-native.ts +3 -3
- package/src/core/protocol.ts +76 -9
- package/src/core/wallets.ts +5 -1
- package/src/index.ts +381 -15
- package/src/react/TonConnectUIProvider.tsx +154 -15
- package/src/react/WalletSelectionModal.tsx +180 -90
- package/src/react/index.ts +1 -0
- package/src/types/index.ts +52 -0
- package/src/utils/transactionBuilder.ts +56 -7
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* TON Connect Mobile SDK
|
|
3
3
|
* Production-ready implementation for React Native and Expo
|
|
4
4
|
*/
|
|
5
|
-
import { TonConnectMobileConfig, ConnectionStatus, WalletInfo, SendTransactionRequest, StatusChangeCallback } from './types';
|
|
5
|
+
import { TonConnectMobileConfig, ConnectionStatus, WalletInfo, SendTransactionRequest, StatusChangeCallback, Network, TonConnectEventType, TonConnectEventListener, TransactionStatusResponse, BalanceResponse } from './types';
|
|
6
6
|
import { type WalletDefinition } from './core/wallets';
|
|
7
7
|
/**
|
|
8
8
|
* Custom error classes
|
|
@@ -34,6 +34,7 @@ export declare class TonConnectMobile {
|
|
|
34
34
|
private adapter;
|
|
35
35
|
private config;
|
|
36
36
|
private statusChangeCallbacks;
|
|
37
|
+
private eventListeners;
|
|
37
38
|
private currentStatus;
|
|
38
39
|
private urlUnsubscribe;
|
|
39
40
|
private currentWallet;
|
|
@@ -103,6 +104,8 @@ export declare class TonConnectMobile {
|
|
|
103
104
|
/**
|
|
104
105
|
* Check if a wallet is available on the current platform
|
|
105
106
|
* Note: This is a best-effort check and may not be 100% accurate
|
|
107
|
+
* CRITICAL FIX: On web, if wallet has universalLink, it's considered available
|
|
108
|
+
* because universal links can open in new tabs/windows
|
|
106
109
|
*/
|
|
107
110
|
isWalletAvailable(walletName?: string): Promise<boolean>;
|
|
108
111
|
/**
|
|
@@ -117,6 +120,22 @@ export declare class TonConnectMobile {
|
|
|
117
120
|
* Notify all status change callbacks
|
|
118
121
|
*/
|
|
119
122
|
private notifyStatusChange;
|
|
123
|
+
/**
|
|
124
|
+
* Emit event to all listeners
|
|
125
|
+
*/
|
|
126
|
+
private emit;
|
|
127
|
+
/**
|
|
128
|
+
* Add event listener
|
|
129
|
+
*/
|
|
130
|
+
on<T = any>(event: TonConnectEventType, listener: TonConnectEventListener<T>): () => void;
|
|
131
|
+
/**
|
|
132
|
+
* Remove event listener
|
|
133
|
+
*/
|
|
134
|
+
off<T = any>(event: TonConnectEventType, listener: TonConnectEventListener<T>): void;
|
|
135
|
+
/**
|
|
136
|
+
* Remove all listeners for an event
|
|
137
|
+
*/
|
|
138
|
+
removeAllListeners(event?: TonConnectEventType): void;
|
|
120
139
|
/**
|
|
121
140
|
* Validate session ID format
|
|
122
141
|
*/
|
|
@@ -137,6 +156,26 @@ export declare class TonConnectMobile {
|
|
|
137
156
|
* Cleanup resources
|
|
138
157
|
*/
|
|
139
158
|
destroy(): void;
|
|
159
|
+
/**
|
|
160
|
+
* Get current network
|
|
161
|
+
*/
|
|
162
|
+
getNetwork(): Network;
|
|
163
|
+
/**
|
|
164
|
+
* Set network (mainnet/testnet)
|
|
165
|
+
*/
|
|
166
|
+
setNetwork(network: Network): void;
|
|
167
|
+
/**
|
|
168
|
+
* Get wallet balance
|
|
169
|
+
*/
|
|
170
|
+
getBalance(address?: string): Promise<BalanceResponse>;
|
|
171
|
+
/**
|
|
172
|
+
* Get transaction status
|
|
173
|
+
*/
|
|
174
|
+
getTransactionStatus(boc: string, maxAttempts?: number, intervalMs?: number): Promise<TransactionStatusResponse>;
|
|
175
|
+
/**
|
|
176
|
+
* Get transaction status by hash (more reliable than BOC)
|
|
177
|
+
*/
|
|
178
|
+
getTransactionStatusByHash(txHash: string, address: string): Promise<TransactionStatusResponse>;
|
|
140
179
|
}
|
|
141
180
|
export * from './types';
|
|
142
181
|
export type { WalletDefinition } from './core/wallets';
|
package/dist/index.js
CHANGED
|
@@ -60,14 +60,14 @@ class UserRejectedError extends TonConnectError {
|
|
|
60
60
|
exports.UserRejectedError = UserRejectedError;
|
|
61
61
|
class ConnectionInProgressError extends TonConnectError {
|
|
62
62
|
constructor() {
|
|
63
|
-
super('Connection request already in progress', 'CONNECTION_IN_PROGRESS');
|
|
63
|
+
super('Connection request already in progress', 'CONNECTION_IN_PROGRESS', 'Please wait for the current connection attempt to complete before trying again.');
|
|
64
64
|
this.name = 'ConnectionInProgressError';
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
exports.ConnectionInProgressError = ConnectionInProgressError;
|
|
68
68
|
class TransactionInProgressError extends TonConnectError {
|
|
69
69
|
constructor() {
|
|
70
|
-
super('Transaction request already in progress', 'TRANSACTION_IN_PROGRESS');
|
|
70
|
+
super('Transaction request already in progress', 'TRANSACTION_IN_PROGRESS', 'Please wait for the current transaction to complete before sending another one.');
|
|
71
71
|
this.name = 'TransactionInProgressError';
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -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
|
});
|
|
@@ -198,8 +211,9 @@ class TonConnectMobile {
|
|
|
198
211
|
const parsed = (0, protocol_1.parseCallbackURL)(url, this.config.scheme);
|
|
199
212
|
console.log('[TON Connect] Parsed callback:', parsed.type, parsed.data ? 'has data' : 'no data');
|
|
200
213
|
// CRITICAL FIX: Check for sign data response first (before other handlers)
|
|
201
|
-
if (
|
|
202
|
-
|
|
214
|
+
// Note: We check if promise exists and hasn't timed out (timeout !== null means not timed out yet)
|
|
215
|
+
if (this.signDataPromise && this.signDataPromise.timeout !== null) {
|
|
216
|
+
// Sign data request is pending and hasn't timed out
|
|
203
217
|
if (parsed.type === 'error' && parsed.data) {
|
|
204
218
|
const errorData = parsed.data;
|
|
205
219
|
if (errorData?.error) {
|
|
@@ -283,13 +297,21 @@ class TonConnectMobile {
|
|
|
283
297
|
// Update status
|
|
284
298
|
this.currentStatus = { connected: true, wallet };
|
|
285
299
|
this.notifyStatusChange();
|
|
300
|
+
// Emit connect event
|
|
301
|
+
this.emit('connect', wallet);
|
|
286
302
|
// Resolve connection promise
|
|
303
|
+
// CRITICAL: Only resolve if promise still exists and hasn't timed out
|
|
287
304
|
if (this.connectionPromise) {
|
|
305
|
+
// Clear timeout if it exists
|
|
288
306
|
if (this.connectionPromise.timeout !== null) {
|
|
289
307
|
clearTimeout(this.connectionPromise.timeout);
|
|
290
308
|
}
|
|
291
|
-
|
|
309
|
+
// Store reference before clearing to prevent race conditions
|
|
310
|
+
const promise = this.connectionPromise;
|
|
311
|
+
// Clear promise first
|
|
292
312
|
this.connectionPromise = null;
|
|
313
|
+
// Then resolve
|
|
314
|
+
promise.resolve(wallet);
|
|
293
315
|
}
|
|
294
316
|
}
|
|
295
317
|
/**
|
|
@@ -300,22 +322,33 @@ class TonConnectMobile {
|
|
|
300
322
|
this.rejectWithError(new TonConnectError('Invalid transaction response'));
|
|
301
323
|
return;
|
|
302
324
|
}
|
|
325
|
+
const transactionResult = {
|
|
326
|
+
boc: response.boc,
|
|
327
|
+
signature: response.signature,
|
|
328
|
+
};
|
|
329
|
+
// Emit transaction event
|
|
330
|
+
this.emit('transaction', transactionResult);
|
|
303
331
|
// Resolve transaction promise
|
|
332
|
+
// CRITICAL: Only resolve if promise still exists and hasn't timed out
|
|
304
333
|
if (this.transactionPromise) {
|
|
334
|
+
// Clear timeout if it exists
|
|
305
335
|
if (this.transactionPromise.timeout !== null) {
|
|
306
336
|
clearTimeout(this.transactionPromise.timeout);
|
|
307
337
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
});
|
|
338
|
+
// Store reference before clearing
|
|
339
|
+
const promise = this.transactionPromise;
|
|
340
|
+
// Clear promise first to prevent race conditions
|
|
312
341
|
this.transactionPromise = null;
|
|
342
|
+
// Then resolve
|
|
343
|
+
promise.resolve(transactionResult);
|
|
313
344
|
}
|
|
314
345
|
}
|
|
315
346
|
/**
|
|
316
347
|
* Reject current promise with error
|
|
317
348
|
*/
|
|
318
349
|
rejectWithError(error) {
|
|
350
|
+
// Emit error event
|
|
351
|
+
this.emit('error', error);
|
|
319
352
|
if (this.connectionPromise) {
|
|
320
353
|
if (this.connectionPromise.timeout !== null) {
|
|
321
354
|
clearTimeout(this.connectionPromise.timeout);
|
|
@@ -330,6 +363,14 @@ class TonConnectMobile {
|
|
|
330
363
|
this.transactionPromise.reject(error);
|
|
331
364
|
this.transactionPromise = null;
|
|
332
365
|
}
|
|
366
|
+
// CRITICAL FIX: Also clear signDataPromise to prevent memory leaks
|
|
367
|
+
if (this.signDataPromise) {
|
|
368
|
+
if (this.signDataPromise.timeout !== null) {
|
|
369
|
+
clearTimeout(this.signDataPromise.timeout);
|
|
370
|
+
}
|
|
371
|
+
this.signDataPromise.reject(error);
|
|
372
|
+
this.signDataPromise = null;
|
|
373
|
+
}
|
|
333
374
|
}
|
|
334
375
|
/**
|
|
335
376
|
* Connect to wallet
|
|
@@ -627,6 +668,8 @@ class TonConnectMobile {
|
|
|
627
668
|
// Update status
|
|
628
669
|
this.currentStatus = { connected: false, wallet: null };
|
|
629
670
|
this.notifyStatusChange();
|
|
671
|
+
// Emit disconnect event
|
|
672
|
+
this.emit('disconnect', null);
|
|
630
673
|
}
|
|
631
674
|
/**
|
|
632
675
|
* Get current connection status
|
|
@@ -649,16 +692,21 @@ class TonConnectMobile {
|
|
|
649
692
|
/**
|
|
650
693
|
* Check if a wallet is available on the current platform
|
|
651
694
|
* Note: This is a best-effort check and may not be 100% accurate
|
|
695
|
+
* CRITICAL FIX: On web, if wallet has universalLink, it's considered available
|
|
696
|
+
* because universal links can open in new tabs/windows
|
|
652
697
|
*/
|
|
653
698
|
async isWalletAvailable(walletName) {
|
|
654
699
|
const wallet = walletName ? (0, wallets_1.getWalletByName)(walletName) : this.currentWallet;
|
|
655
700
|
if (!wallet) {
|
|
656
701
|
return false;
|
|
657
702
|
}
|
|
658
|
-
//
|
|
659
|
-
//
|
|
660
|
-
|
|
661
|
-
|
|
703
|
+
// CRITICAL FIX: Check adapter type to reliably detect web platform
|
|
704
|
+
// WebAdapter is only used on web, so this is the most reliable check
|
|
705
|
+
const isWeb = this.adapter.constructor.name === 'WebAdapter';
|
|
706
|
+
if (isWeb) {
|
|
707
|
+
// On web, if wallet has universalLink or supports web platform, it's available
|
|
708
|
+
// Universal links can open in a new tab on web
|
|
709
|
+
return wallet.platforms.includes('web') || !!wallet.universalLink;
|
|
662
710
|
}
|
|
663
711
|
// On mobile, we can't reliably check if wallet is installed
|
|
664
712
|
// Return true if wallet supports the current platform
|
|
@@ -712,6 +760,60 @@ class TonConnectMobile {
|
|
|
712
760
|
// Ignore errors in callbacks
|
|
713
761
|
}
|
|
714
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
|
+
}
|
|
715
817
|
}
|
|
716
818
|
/**
|
|
717
819
|
* Validate session ID format
|
|
@@ -815,10 +917,204 @@ class TonConnectMobile {
|
|
|
815
917
|
this.adapter.destroy();
|
|
816
918
|
}
|
|
817
919
|
this.statusChangeCallbacks.clear();
|
|
920
|
+
this.eventListeners.clear();
|
|
818
921
|
this.connectionPromise = null;
|
|
819
922
|
this.transactionPromise = null;
|
|
820
923
|
this.signDataPromise = null;
|
|
821
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
|
+
}
|
|
822
1118
|
}
|
|
823
1119
|
exports.TonConnectMobile = TonConnectMobile;
|
|
824
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 */
|