@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/src/index.ts
CHANGED
|
@@ -13,6 +13,12 @@ import {
|
|
|
13
13
|
SendTransactionRequest,
|
|
14
14
|
StatusChangeCallback,
|
|
15
15
|
PlatformAdapter,
|
|
16
|
+
Network,
|
|
17
|
+
TonConnectEventType,
|
|
18
|
+
TonConnectEventListener,
|
|
19
|
+
TransactionStatus,
|
|
20
|
+
TransactionStatusResponse,
|
|
21
|
+
BalanceResponse,
|
|
16
22
|
} from './types';
|
|
17
23
|
import {
|
|
18
24
|
buildConnectionRequest,
|
|
@@ -81,14 +87,22 @@ export class UserRejectedError extends TonConnectError {
|
|
|
81
87
|
|
|
82
88
|
export class ConnectionInProgressError extends TonConnectError {
|
|
83
89
|
constructor() {
|
|
84
|
-
super(
|
|
90
|
+
super(
|
|
91
|
+
'Connection request already in progress',
|
|
92
|
+
'CONNECTION_IN_PROGRESS',
|
|
93
|
+
'Please wait for the current connection attempt to complete before trying again.'
|
|
94
|
+
);
|
|
85
95
|
this.name = 'ConnectionInProgressError';
|
|
86
96
|
}
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
export class TransactionInProgressError extends TonConnectError {
|
|
90
100
|
constructor() {
|
|
91
|
-
super(
|
|
101
|
+
super(
|
|
102
|
+
'Transaction request already in progress',
|
|
103
|
+
'TRANSACTION_IN_PROGRESS',
|
|
104
|
+
'Please wait for the current transaction to complete before sending another one.'
|
|
105
|
+
);
|
|
92
106
|
this.name = 'TransactionInProgressError';
|
|
93
107
|
}
|
|
94
108
|
}
|
|
@@ -98,8 +112,13 @@ export class TransactionInProgressError extends TonConnectError {
|
|
|
98
112
|
*/
|
|
99
113
|
export class TonConnectMobile {
|
|
100
114
|
private adapter: PlatformAdapter;
|
|
101
|
-
private config: Required<Omit<TonConnectMobileConfig, 'preferredWallet'>> & {
|
|
115
|
+
private config: Required<Omit<TonConnectMobileConfig, 'preferredWallet' | 'network' | 'tonApiEndpoint'>> & {
|
|
116
|
+
preferredWallet?: string;
|
|
117
|
+
network: Network;
|
|
118
|
+
tonApiEndpoint?: string;
|
|
119
|
+
};
|
|
102
120
|
private statusChangeCallbacks: Set<StatusChangeCallback> = new Set();
|
|
121
|
+
private eventListeners: Map<TonConnectEventType, Set<TonConnectEventListener>> = new Map();
|
|
103
122
|
private currentStatus: ConnectionStatus = { connected: false, wallet: null };
|
|
104
123
|
private urlUnsubscribe: (() => void) | null = null;
|
|
105
124
|
private currentWallet!: WalletDefinition;
|
|
@@ -128,14 +147,32 @@ export class TonConnectMobile {
|
|
|
128
147
|
throw new TonConnectError('scheme is required');
|
|
129
148
|
}
|
|
130
149
|
|
|
150
|
+
// Validate network
|
|
151
|
+
const network = config.network || 'mainnet';
|
|
152
|
+
if (network !== 'mainnet' && network !== 'testnet') {
|
|
153
|
+
throw new TonConnectError('Network must be either "mainnet" or "testnet"');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Set default TON API endpoint based on network
|
|
157
|
+
const defaultTonApiEndpoint =
|
|
158
|
+
network === 'testnet'
|
|
159
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
160
|
+
: 'https://toncenter.com/api/v2';
|
|
161
|
+
|
|
131
162
|
this.config = {
|
|
132
163
|
storageKeyPrefix: 'tonconnect_',
|
|
133
164
|
connectionTimeout: 300000, // 5 minutes
|
|
134
165
|
transactionTimeout: 300000, // 5 minutes
|
|
135
166
|
skipCanOpenURLCheck: true, // Skip canOpenURL check by default (Android issue)
|
|
136
167
|
preferredWallet: config.preferredWallet,
|
|
168
|
+
network,
|
|
169
|
+
tonApiEndpoint: config.tonApiEndpoint || defaultTonApiEndpoint,
|
|
137
170
|
...config,
|
|
138
|
-
} as Required<Omit<TonConnectMobileConfig, 'preferredWallet'>> & {
|
|
171
|
+
} as Required<Omit<TonConnectMobileConfig, 'preferredWallet' | 'network' | 'tonApiEndpoint'>> & {
|
|
172
|
+
preferredWallet?: string;
|
|
173
|
+
network: Network;
|
|
174
|
+
tonApiEndpoint?: string;
|
|
175
|
+
};
|
|
139
176
|
|
|
140
177
|
// Determine which wallet to use
|
|
141
178
|
if (this.config.preferredWallet) {
|
|
@@ -154,6 +191,7 @@ export class TonConnectMobile {
|
|
|
154
191
|
console.log('[TON Connect] Initializing SDK with config:', {
|
|
155
192
|
manifestUrl: this.config.manifestUrl,
|
|
156
193
|
scheme: this.config.scheme,
|
|
194
|
+
network: this.config.network,
|
|
157
195
|
wallet: this.currentWallet.name,
|
|
158
196
|
universalLink: this.currentWallet.universalLink,
|
|
159
197
|
});
|
|
@@ -247,8 +285,9 @@ export class TonConnectMobile {
|
|
|
247
285
|
console.log('[TON Connect] Parsed callback:', parsed.type, parsed.data ? 'has data' : 'no data');
|
|
248
286
|
|
|
249
287
|
// CRITICAL FIX: Check for sign data response first (before other handlers)
|
|
250
|
-
if (
|
|
251
|
-
|
|
288
|
+
// Note: We check if promise exists and hasn't timed out (timeout !== null means not timed out yet)
|
|
289
|
+
if (this.signDataPromise && this.signDataPromise.timeout !== null) {
|
|
290
|
+
// Sign data request is pending and hasn't timed out
|
|
252
291
|
if (parsed.type === 'error' && parsed.data) {
|
|
253
292
|
const errorData = parsed.data as ErrorResponse;
|
|
254
293
|
if (errorData?.error) {
|
|
@@ -338,13 +377,22 @@ export class TonConnectMobile {
|
|
|
338
377
|
this.currentStatus = { connected: true, wallet };
|
|
339
378
|
this.notifyStatusChange();
|
|
340
379
|
|
|
380
|
+
// Emit connect event
|
|
381
|
+
this.emit('connect', wallet);
|
|
382
|
+
|
|
341
383
|
// Resolve connection promise
|
|
384
|
+
// CRITICAL: Only resolve if promise still exists and hasn't timed out
|
|
342
385
|
if (this.connectionPromise) {
|
|
386
|
+
// Clear timeout if it exists
|
|
343
387
|
if (this.connectionPromise.timeout !== null) {
|
|
344
388
|
clearTimeout(this.connectionPromise.timeout);
|
|
345
389
|
}
|
|
346
|
-
|
|
390
|
+
// Store reference before clearing to prevent race conditions
|
|
391
|
+
const promise = this.connectionPromise;
|
|
392
|
+
// Clear promise first
|
|
347
393
|
this.connectionPromise = null;
|
|
394
|
+
// Then resolve
|
|
395
|
+
promise.resolve(wallet);
|
|
348
396
|
}
|
|
349
397
|
}
|
|
350
398
|
|
|
@@ -357,16 +405,27 @@ export class TonConnectMobile {
|
|
|
357
405
|
return;
|
|
358
406
|
}
|
|
359
407
|
|
|
408
|
+
const transactionResult = {
|
|
409
|
+
boc: response.boc,
|
|
410
|
+
signature: response.signature,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// Emit transaction event
|
|
414
|
+
this.emit('transaction', transactionResult);
|
|
415
|
+
|
|
360
416
|
// Resolve transaction promise
|
|
417
|
+
// CRITICAL: Only resolve if promise still exists and hasn't timed out
|
|
361
418
|
if (this.transactionPromise) {
|
|
419
|
+
// Clear timeout if it exists
|
|
362
420
|
if (this.transactionPromise.timeout !== null) {
|
|
363
421
|
clearTimeout(this.transactionPromise.timeout);
|
|
364
422
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
});
|
|
423
|
+
// Store reference before clearing
|
|
424
|
+
const promise = this.transactionPromise;
|
|
425
|
+
// Clear promise first to prevent race conditions
|
|
369
426
|
this.transactionPromise = null;
|
|
427
|
+
// Then resolve
|
|
428
|
+
promise.resolve(transactionResult);
|
|
370
429
|
}
|
|
371
430
|
}
|
|
372
431
|
|
|
@@ -374,6 +433,9 @@ export class TonConnectMobile {
|
|
|
374
433
|
* Reject current promise with error
|
|
375
434
|
*/
|
|
376
435
|
private rejectWithError(error: Error): void {
|
|
436
|
+
// Emit error event
|
|
437
|
+
this.emit('error', error);
|
|
438
|
+
|
|
377
439
|
if (this.connectionPromise) {
|
|
378
440
|
if (this.connectionPromise.timeout !== null) {
|
|
379
441
|
clearTimeout(this.connectionPromise.timeout);
|
|
@@ -388,6 +450,14 @@ export class TonConnectMobile {
|
|
|
388
450
|
this.transactionPromise.reject(error);
|
|
389
451
|
this.transactionPromise = null;
|
|
390
452
|
}
|
|
453
|
+
// CRITICAL FIX: Also clear signDataPromise to prevent memory leaks
|
|
454
|
+
if (this.signDataPromise) {
|
|
455
|
+
if (this.signDataPromise.timeout !== null) {
|
|
456
|
+
clearTimeout(this.signDataPromise.timeout);
|
|
457
|
+
}
|
|
458
|
+
this.signDataPromise.reject(error);
|
|
459
|
+
this.signDataPromise = null;
|
|
460
|
+
}
|
|
391
461
|
}
|
|
392
462
|
|
|
393
463
|
/**
|
|
@@ -743,6 +813,9 @@ export class TonConnectMobile {
|
|
|
743
813
|
// Update status
|
|
744
814
|
this.currentStatus = { connected: false, wallet: null };
|
|
745
815
|
this.notifyStatusChange();
|
|
816
|
+
|
|
817
|
+
// Emit disconnect event
|
|
818
|
+
this.emit('disconnect', null);
|
|
746
819
|
}
|
|
747
820
|
|
|
748
821
|
/**
|
|
@@ -769,6 +842,8 @@ export class TonConnectMobile {
|
|
|
769
842
|
/**
|
|
770
843
|
* Check if a wallet is available on the current platform
|
|
771
844
|
* Note: This is a best-effort check and may not be 100% accurate
|
|
845
|
+
* CRITICAL FIX: On web, if wallet has universalLink, it's considered available
|
|
846
|
+
* because universal links can open in new tabs/windows
|
|
772
847
|
*/
|
|
773
848
|
async isWalletAvailable(walletName?: string): Promise<boolean> {
|
|
774
849
|
const wallet = walletName ? getWalletByName(walletName) : this.currentWallet;
|
|
@@ -776,10 +851,14 @@ export class TonConnectMobile {
|
|
|
776
851
|
return false;
|
|
777
852
|
}
|
|
778
853
|
|
|
779
|
-
//
|
|
780
|
-
//
|
|
781
|
-
|
|
782
|
-
|
|
854
|
+
// CRITICAL FIX: Check adapter type to reliably detect web platform
|
|
855
|
+
// WebAdapter is only used on web, so this is the most reliable check
|
|
856
|
+
const isWeb = this.adapter.constructor.name === 'WebAdapter';
|
|
857
|
+
|
|
858
|
+
if (isWeb) {
|
|
859
|
+
// On web, if wallet has universalLink or supports web platform, it's available
|
|
860
|
+
// Universal links can open in a new tab on web
|
|
861
|
+
return wallet.platforms.includes('web') || !!wallet.universalLink;
|
|
783
862
|
}
|
|
784
863
|
|
|
785
864
|
// On mobile, we can't reliably check if wallet is installed
|
|
@@ -841,6 +920,63 @@ export class TonConnectMobile {
|
|
|
841
920
|
// Ignore errors in callbacks
|
|
842
921
|
}
|
|
843
922
|
});
|
|
923
|
+
// Emit statusChange event
|
|
924
|
+
this.emit('statusChange', status);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Emit event to all listeners
|
|
929
|
+
*/
|
|
930
|
+
private emit<T>(event: TonConnectEventType, data: T): void {
|
|
931
|
+
const listeners = this.eventListeners.get(event);
|
|
932
|
+
if (listeners) {
|
|
933
|
+
listeners.forEach((listener) => {
|
|
934
|
+
try {
|
|
935
|
+
listener(data);
|
|
936
|
+
} catch (error) {
|
|
937
|
+
console.error(`[TON Connect] Error in event listener for ${event}:`, error);
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Add event listener
|
|
945
|
+
*/
|
|
946
|
+
on<T = any>(event: TonConnectEventType, listener: TonConnectEventListener<T>): () => void {
|
|
947
|
+
if (!this.eventListeners.has(event)) {
|
|
948
|
+
this.eventListeners.set(event, new Set());
|
|
949
|
+
}
|
|
950
|
+
this.eventListeners.get(event)!.add(listener);
|
|
951
|
+
|
|
952
|
+
// Return unsubscribe function
|
|
953
|
+
return () => {
|
|
954
|
+
const listeners = this.eventListeners.get(event);
|
|
955
|
+
if (listeners) {
|
|
956
|
+
listeners.delete(listener);
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Remove event listener
|
|
963
|
+
*/
|
|
964
|
+
off<T = any>(event: TonConnectEventType, listener: TonConnectEventListener<T>): void {
|
|
965
|
+
const listeners = this.eventListeners.get(event);
|
|
966
|
+
if (listeners) {
|
|
967
|
+
listeners.delete(listener);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Remove all listeners for an event
|
|
973
|
+
*/
|
|
974
|
+
removeAllListeners(event?: TonConnectEventType): void {
|
|
975
|
+
if (event) {
|
|
976
|
+
this.eventListeners.delete(event);
|
|
977
|
+
} else {
|
|
978
|
+
this.eventListeners.clear();
|
|
979
|
+
}
|
|
844
980
|
}
|
|
845
981
|
|
|
846
982
|
/**
|
|
@@ -955,10 +1091,240 @@ export class TonConnectMobile {
|
|
|
955
1091
|
}
|
|
956
1092
|
|
|
957
1093
|
this.statusChangeCallbacks.clear();
|
|
1094
|
+
this.eventListeners.clear();
|
|
958
1095
|
this.connectionPromise = null;
|
|
959
1096
|
this.transactionPromise = null;
|
|
960
1097
|
this.signDataPromise = null;
|
|
961
1098
|
}
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* Get current network
|
|
1102
|
+
*/
|
|
1103
|
+
getNetwork(): Network {
|
|
1104
|
+
return this.config.network;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Set network (mainnet/testnet)
|
|
1109
|
+
*/
|
|
1110
|
+
setNetwork(network: Network): void {
|
|
1111
|
+
if (network !== 'mainnet' && network !== 'testnet') {
|
|
1112
|
+
throw new TonConnectError('Network must be either "mainnet" or "testnet"');
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
const oldNetwork = this.config.network;
|
|
1116
|
+
|
|
1117
|
+
// Warn if switching network while connected (wallet connection is network-specific)
|
|
1118
|
+
if (this.currentStatus.connected && oldNetwork !== network) {
|
|
1119
|
+
console.warn(
|
|
1120
|
+
'[TON Connect] Network changed while wallet is connected. ' +
|
|
1121
|
+
'The wallet connection may be invalid for the new network. ' +
|
|
1122
|
+
'Consider disconnecting and reconnecting after network change.'
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
this.config.network = network;
|
|
1127
|
+
|
|
1128
|
+
// Update TON API endpoint if not explicitly set
|
|
1129
|
+
if (!this.config.tonApiEndpoint || this.config.tonApiEndpoint.includes(oldNetwork)) {
|
|
1130
|
+
this.config.tonApiEndpoint =
|
|
1131
|
+
network === 'testnet'
|
|
1132
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
1133
|
+
: 'https://toncenter.com/api/v2';
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
console.log('[TON Connect] Network changed to:', network);
|
|
1137
|
+
|
|
1138
|
+
// Notify status change to update chain ID in React components
|
|
1139
|
+
this.notifyStatusChange();
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* Get wallet balance
|
|
1144
|
+
*/
|
|
1145
|
+
async getBalance(address?: string): Promise<BalanceResponse> {
|
|
1146
|
+
const targetAddress = address || this.currentStatus.wallet?.address;
|
|
1147
|
+
if (!targetAddress) {
|
|
1148
|
+
throw new TonConnectError('Address is required. Either connect a wallet or provide an address.');
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// Validate address format
|
|
1152
|
+
if (!/^[0-9A-Za-z_-]{48}$/.test(targetAddress)) {
|
|
1153
|
+
throw new TonConnectError('Invalid TON address format');
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
try {
|
|
1157
|
+
const apiEndpoint = this.config.tonApiEndpoint ||
|
|
1158
|
+
(this.config.network === 'testnet'
|
|
1159
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
1160
|
+
: 'https://toncenter.com/api/v2');
|
|
1161
|
+
|
|
1162
|
+
const url = `${apiEndpoint}/getAddressInformation?address=${encodeURIComponent(targetAddress)}`;
|
|
1163
|
+
|
|
1164
|
+
const response = await fetch(url, {
|
|
1165
|
+
method: 'GET',
|
|
1166
|
+
headers: {
|
|
1167
|
+
'Accept': 'application/json',
|
|
1168
|
+
},
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
if (!response.ok) {
|
|
1172
|
+
throw new TonConnectError(`Failed to fetch balance: ${response.status} ${response.statusText}`);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
const data = await response.json();
|
|
1176
|
+
|
|
1177
|
+
if (data.ok === false) {
|
|
1178
|
+
throw new TonConnectError(data.error || 'Failed to fetch balance');
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// TON Center API returns balance in nanotons
|
|
1182
|
+
const balance = data.result?.balance || '0';
|
|
1183
|
+
const balanceTon = (BigInt(balance) / BigInt(1000000000)).toString() + '.' +
|
|
1184
|
+
(BigInt(balance) % BigInt(1000000000)).toString().padStart(9, '0').replace(/0+$/, '');
|
|
1185
|
+
|
|
1186
|
+
return {
|
|
1187
|
+
balance,
|
|
1188
|
+
balanceTon: balanceTon === '0.' ? '0' : balanceTon,
|
|
1189
|
+
network: this.config.network,
|
|
1190
|
+
};
|
|
1191
|
+
} catch (error: any) {
|
|
1192
|
+
if (error instanceof TonConnectError) {
|
|
1193
|
+
throw error;
|
|
1194
|
+
}
|
|
1195
|
+
throw new TonConnectError(`Failed to get balance: ${error?.message || String(error)}`);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* Get transaction status
|
|
1201
|
+
*/
|
|
1202
|
+
async getTransactionStatus(boc: string, maxAttempts: number = 10, intervalMs: number = 2000): Promise<TransactionStatusResponse> {
|
|
1203
|
+
if (!boc || typeof boc !== 'string' || boc.length === 0) {
|
|
1204
|
+
throw new TonConnectError('Transaction BOC is required');
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// Extract transaction hash from BOC (simplified - in production, you'd parse the BOC properly)
|
|
1208
|
+
// For now, we'll use a polling approach with TON Center API
|
|
1209
|
+
try {
|
|
1210
|
+
const apiEndpoint = this.config.tonApiEndpoint ||
|
|
1211
|
+
(this.config.network === 'testnet'
|
|
1212
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
1213
|
+
: 'https://toncenter.com/api/v2');
|
|
1214
|
+
|
|
1215
|
+
// Try to get transaction info
|
|
1216
|
+
// Note: This is a simplified implementation. In production, you'd need to:
|
|
1217
|
+
// 1. Parse the BOC to extract transaction hash
|
|
1218
|
+
// 2. Query the blockchain for transaction status
|
|
1219
|
+
// 3. Handle different confirmation states
|
|
1220
|
+
|
|
1221
|
+
// For now, we'll return a basic status
|
|
1222
|
+
// In a real implementation, you'd query the blockchain API
|
|
1223
|
+
let attempts = 0;
|
|
1224
|
+
let lastError: Error | null = null;
|
|
1225
|
+
|
|
1226
|
+
while (attempts < maxAttempts) {
|
|
1227
|
+
try {
|
|
1228
|
+
// This is a placeholder - you'd need to implement actual transaction lookup
|
|
1229
|
+
// For now, we'll simulate checking
|
|
1230
|
+
await new Promise<void>((resolve) => setTimeout(() => resolve(), intervalMs));
|
|
1231
|
+
|
|
1232
|
+
// In production, you would:
|
|
1233
|
+
// 1. Parse BOC to get transaction hash
|
|
1234
|
+
// 2. Query TON API: GET /getTransactions?address=...&limit=1
|
|
1235
|
+
// 3. Check if transaction exists and is confirmed
|
|
1236
|
+
|
|
1237
|
+
// For now, return unknown status (as we can't parse BOC without additional libraries)
|
|
1238
|
+
return {
|
|
1239
|
+
status: 'unknown',
|
|
1240
|
+
error: 'Transaction status checking requires BOC parsing. Please use a TON library to parse the BOC and extract the transaction hash.',
|
|
1241
|
+
};
|
|
1242
|
+
} catch (error: any) {
|
|
1243
|
+
lastError = error;
|
|
1244
|
+
attempts++;
|
|
1245
|
+
if (attempts < maxAttempts) {
|
|
1246
|
+
await new Promise<void>((resolve) => setTimeout(() => resolve(), intervalMs));
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
return {
|
|
1252
|
+
status: 'failed',
|
|
1253
|
+
error: lastError?.message || 'Failed to check transaction status',
|
|
1254
|
+
};
|
|
1255
|
+
} catch (error: any) {
|
|
1256
|
+
throw new TonConnectError(`Failed to get transaction status: ${error?.message || String(error)}`);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* Get transaction status by hash (more reliable than BOC)
|
|
1262
|
+
*/
|
|
1263
|
+
async getTransactionStatusByHash(txHash: string, address: string): Promise<TransactionStatusResponse> {
|
|
1264
|
+
if (!txHash || typeof txHash !== 'string' || txHash.length === 0) {
|
|
1265
|
+
throw new TonConnectError('Transaction hash is required');
|
|
1266
|
+
}
|
|
1267
|
+
if (!address || typeof address !== 'string' || address.length === 0) {
|
|
1268
|
+
throw new TonConnectError('Address is required');
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
try {
|
|
1272
|
+
const apiEndpoint = this.config.tonApiEndpoint ||
|
|
1273
|
+
(this.config.network === 'testnet'
|
|
1274
|
+
? 'https://testnet.toncenter.com/api/v2'
|
|
1275
|
+
: 'https://toncenter.com/api/v2');
|
|
1276
|
+
|
|
1277
|
+
// Query transactions for the address
|
|
1278
|
+
const url = `${apiEndpoint}/getTransactions?address=${encodeURIComponent(address)}&limit=100`;
|
|
1279
|
+
|
|
1280
|
+
const response = await fetch(url, {
|
|
1281
|
+
method: 'GET',
|
|
1282
|
+
headers: {
|
|
1283
|
+
'Accept': 'application/json',
|
|
1284
|
+
},
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
if (!response.ok) {
|
|
1288
|
+
throw new TonConnectError(`Failed to fetch transactions: ${response.status} ${response.statusText}`);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const data = await response.json();
|
|
1292
|
+
|
|
1293
|
+
if (data.ok === false) {
|
|
1294
|
+
throw new TonConnectError(data.error || 'Failed to fetch transactions');
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// Search for transaction with matching hash
|
|
1298
|
+
const transactions = data.result || [];
|
|
1299
|
+
const transaction = transactions.find((tx: any) =>
|
|
1300
|
+
tx.transaction_id?.hash === txHash ||
|
|
1301
|
+
tx.transaction_id?.lt === txHash ||
|
|
1302
|
+
JSON.stringify(tx.transaction_id).includes(txHash)
|
|
1303
|
+
);
|
|
1304
|
+
|
|
1305
|
+
if (transaction) {
|
|
1306
|
+
return {
|
|
1307
|
+
status: 'confirmed',
|
|
1308
|
+
hash: transaction.transaction_id?.hash || txHash,
|
|
1309
|
+
blockNumber: transaction.transaction_id?.lt,
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// Transaction not found - could be pending or failed
|
|
1314
|
+
return {
|
|
1315
|
+
status: 'pending',
|
|
1316
|
+
hash: txHash,
|
|
1317
|
+
};
|
|
1318
|
+
} catch (error: any) {
|
|
1319
|
+
if (error instanceof TonConnectError) {
|
|
1320
|
+
throw error;
|
|
1321
|
+
}
|
|
1322
|
+
return {
|
|
1323
|
+
status: 'failed',
|
|
1324
|
+
error: error?.message || 'Failed to check transaction status',
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
962
1328
|
}
|
|
963
1329
|
|
|
964
1330
|
// Export types
|