@blazium/ton-connect-mobile 1.0.3 → 1.0.5
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 +3 -0
- package/dist/adapters/expo.d.ts +1 -1
- package/dist/adapters/expo.js +20 -12
- package/dist/adapters/react-native.d.ts +1 -1
- package/dist/adapters/react-native.js +20 -12
- package/dist/adapters/web.d.ts +1 -1
- package/dist/adapters/web.js +1 -1
- package/dist/core/protocol.d.ts +4 -2
- package/dist/core/protocol.js +14 -2
- package/dist/index.js +7 -6
- package/dist/types/index.d.ts +6 -1
- package/package.json +1 -1
- package/src/adapters/expo.ts +25 -12
- package/src/adapters/react-native.ts +25 -12
- package/src/adapters/web.ts +1 -1
- package/src/core/protocol.ts +20 -2
- package/src/index.ts +7 -6
- package/src/types/index.ts +6 -1
package/README.md
CHANGED
|
@@ -223,6 +223,9 @@ new TonConnectMobile(config: TonConnectMobileConfig)
|
|
|
223
223
|
- `storageKeyPrefix` (optional): Prefix for storage keys (default: `'tonconnect_'`)
|
|
224
224
|
- `connectionTimeout` (optional): Connection timeout in ms (default: `300000`)
|
|
225
225
|
- `transactionTimeout` (optional): Transaction timeout in ms (default: `300000`)
|
|
226
|
+
- `skipCanOpenURLCheck` (optional): Skip canOpenURL check before opening URL (default: `true`)
|
|
227
|
+
- Set to `false` if you want to check if URL can be opened before attempting to open it
|
|
228
|
+
- Note: On Android, `canOpenURL` may return `false` for `tonconnect://` even if wallet is installed
|
|
226
229
|
|
|
227
230
|
#### Methods
|
|
228
231
|
|
package/dist/adapters/expo.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare class ExpoAdapter implements PlatformAdapter {
|
|
|
11
11
|
private subscription;
|
|
12
12
|
constructor();
|
|
13
13
|
private setupURLListener;
|
|
14
|
-
openURL(url: string): Promise<boolean>;
|
|
14
|
+
openURL(url: string, skipCanOpenURLCheck?: boolean): Promise<boolean>;
|
|
15
15
|
getInitialURL(): Promise<string | null>;
|
|
16
16
|
addURLListener(callback: (url: string) => void): () => void;
|
|
17
17
|
setItem(key: string, value: string): Promise<void>;
|
package/dist/adapters/expo.js
CHANGED
|
@@ -39,30 +39,38 @@ class ExpoAdapter {
|
|
|
39
39
|
this.urlListeners.forEach((listener) => listener(event.url));
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
|
-
async openURL(url) {
|
|
42
|
+
async openURL(url, skipCanOpenURLCheck = true) {
|
|
43
43
|
if (!Linking) {
|
|
44
44
|
throw new Error('expo-linking is not available');
|
|
45
45
|
}
|
|
46
46
|
try {
|
|
47
|
-
console.log('[ExpoAdapter]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
console.log('[ExpoAdapter] Opening URL:', url);
|
|
48
|
+
// Optional canOpenURL check (can be disabled via config)
|
|
49
|
+
if (!skipCanOpenURLCheck) {
|
|
50
|
+
console.log('[ExpoAdapter] Checking if URL can be opened...');
|
|
51
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
52
|
+
console.log('[ExpoAdapter] canOpenURL result:', canOpen);
|
|
53
|
+
if (!canOpen) {
|
|
54
|
+
throw new Error('Cannot open URL. Make sure a wallet app is installed that supports tonconnect:// protocol.');
|
|
55
|
+
}
|
|
53
56
|
}
|
|
54
|
-
|
|
57
|
+
else {
|
|
58
|
+
console.log('[ExpoAdapter] Skipping canOpenURL check (Android compatibility)');
|
|
59
|
+
}
|
|
60
|
+
// CRITICAL FIX: Android'de canOpenURL() tonconnect:// protokolünü tanımayabilir
|
|
61
|
+
// Bu yüzden direkt openURL() çağırıyoruz. Eğer açılamazsa hata fırlatır.
|
|
55
62
|
await Linking.openURL(url);
|
|
56
63
|
console.log('[ExpoAdapter] URL opened successfully');
|
|
57
64
|
return true;
|
|
58
65
|
}
|
|
59
66
|
catch (error) {
|
|
60
67
|
console.error('[ExpoAdapter] Error in openURL:', error);
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
// Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
|
|
69
|
+
const errorMessage = error?.message || String(error);
|
|
70
|
+
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
|
|
71
|
+
throw new Error('No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.');
|
|
64
72
|
}
|
|
65
|
-
throw new Error(`Failed to open
|
|
73
|
+
throw new Error(`Failed to open wallet app: ${errorMessage}`);
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
76
|
async getInitialURL() {
|
|
@@ -11,7 +11,7 @@ export declare class ReactNativeAdapter implements PlatformAdapter {
|
|
|
11
11
|
private subscription;
|
|
12
12
|
constructor();
|
|
13
13
|
private setupURLListener;
|
|
14
|
-
openURL(url: string): Promise<boolean>;
|
|
14
|
+
openURL(url: string, skipCanOpenURLCheck?: boolean): Promise<boolean>;
|
|
15
15
|
getInitialURL(): Promise<string | null>;
|
|
16
16
|
addURLListener(callback: (url: string) => void): () => void;
|
|
17
17
|
setItem(key: string, value: string): Promise<void>;
|
|
@@ -37,30 +37,38 @@ class ReactNativeAdapter {
|
|
|
37
37
|
this.urlListeners.forEach((listener) => listener(event.url));
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
-
async openURL(url) {
|
|
40
|
+
async openURL(url, skipCanOpenURLCheck = true) {
|
|
41
41
|
if (!Linking) {
|
|
42
42
|
throw new Error('react-native Linking is not available');
|
|
43
43
|
}
|
|
44
44
|
try {
|
|
45
|
-
console.log('[ReactNativeAdapter]
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
console.log('[ReactNativeAdapter] Opening URL:', url);
|
|
46
|
+
// Optional canOpenURL check (can be disabled via config)
|
|
47
|
+
if (!skipCanOpenURLCheck) {
|
|
48
|
+
console.log('[ReactNativeAdapter] Checking if URL can be opened...');
|
|
49
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
50
|
+
console.log('[ReactNativeAdapter] canOpenURL result:', canOpen);
|
|
51
|
+
if (!canOpen) {
|
|
52
|
+
throw new Error('Cannot open URL. Make sure a wallet app is installed that supports tonconnect:// protocol.');
|
|
53
|
+
}
|
|
51
54
|
}
|
|
52
|
-
|
|
55
|
+
else {
|
|
56
|
+
console.log('[ReactNativeAdapter] Skipping canOpenURL check (Android compatibility)');
|
|
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.
|
|
53
60
|
await Linking.openURL(url);
|
|
54
61
|
console.log('[ReactNativeAdapter] URL opened successfully');
|
|
55
62
|
return true;
|
|
56
63
|
}
|
|
57
64
|
catch (error) {
|
|
58
65
|
console.error('[ReactNativeAdapter] Error in openURL:', error);
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
// Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
|
|
67
|
+
const errorMessage = error?.message || String(error);
|
|
68
|
+
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
|
|
69
|
+
throw new Error('No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.');
|
|
62
70
|
}
|
|
63
|
-
throw new Error(`Failed to open
|
|
71
|
+
throw new Error(`Failed to open wallet app: ${errorMessage}`);
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
async getInitialURL() {
|
package/dist/adapters/web.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare class WebAdapter implements PlatformAdapter {
|
|
|
13
13
|
constructor();
|
|
14
14
|
private setupURLListener;
|
|
15
15
|
private handleURLChange;
|
|
16
|
-
openURL(url: string): Promise<boolean>;
|
|
16
|
+
openURL(url: string, skipCanOpenURLCheck?: boolean): Promise<boolean>;
|
|
17
17
|
getInitialURL(): Promise<string | null>;
|
|
18
18
|
addURLListener(callback: (url: string) => void): () => void;
|
|
19
19
|
setItem(key: string, value: string): Promise<void>;
|
package/dist/adapters/web.js
CHANGED
package/dist/core/protocol.d.ts
CHANGED
|
@@ -14,13 +14,15 @@ export declare function decodeBase64URL<T>(encoded: string): T;
|
|
|
14
14
|
/**
|
|
15
15
|
* Build connection request URL
|
|
16
16
|
* Format: tonconnect://connect?<base64_encoded_payload>
|
|
17
|
+
* Or universal link: https://app.tonkeeper.com/ton-connect?<base64_encoded_payload>
|
|
17
18
|
*/
|
|
18
|
-
export declare function buildConnectionRequest(manifestUrl: string, returnScheme: string): string;
|
|
19
|
+
export declare function buildConnectionRequest(manifestUrl: string, returnScheme: string, useUniversalLink?: boolean): string;
|
|
19
20
|
/**
|
|
20
21
|
* Build transaction request URL
|
|
21
22
|
* Format: tonconnect://send-transaction?<base64_encoded_payload>
|
|
23
|
+
* Or universal link: https://app.tonkeeper.com/ton-connect/send-transaction?<base64_encoded_payload>
|
|
22
24
|
*/
|
|
23
|
-
export declare function buildTransactionRequest(manifestUrl: string, request: SendTransactionRequest, returnScheme: string): string;
|
|
25
|
+
export declare function buildTransactionRequest(manifestUrl: string, request: SendTransactionRequest, returnScheme: string, useUniversalLink?: boolean): string;
|
|
24
26
|
/**
|
|
25
27
|
* Parse callback URL
|
|
26
28
|
* Format: <scheme>://tonconnect?<base64_encoded_response>
|
package/dist/core/protocol.js
CHANGED
|
@@ -18,7 +18,9 @@ exports.validateTransactionRequest = validateTransactionRequest;
|
|
|
18
18
|
*/
|
|
19
19
|
const PROTOCOL_VERSION = '2';
|
|
20
20
|
const CONNECT_PREFIX = 'tonconnect://connect';
|
|
21
|
+
const CONNECT_UNIVERSAL_PREFIX = 'https://app.tonkeeper.com/ton-connect';
|
|
21
22
|
const SEND_TRANSACTION_PREFIX = 'tonconnect://send-transaction';
|
|
23
|
+
const SEND_TRANSACTION_UNIVERSAL_PREFIX = 'https://app.tonkeeper.com/ton-connect/send-transaction';
|
|
22
24
|
const CALLBACK_PREFIX = 'tonconnect';
|
|
23
25
|
/**
|
|
24
26
|
* Get TextEncoder with availability check
|
|
@@ -100,21 +102,27 @@ function decodeBase64URL(encoded) {
|
|
|
100
102
|
/**
|
|
101
103
|
* Build connection request URL
|
|
102
104
|
* Format: tonconnect://connect?<base64_encoded_payload>
|
|
105
|
+
* Or universal link: https://app.tonkeeper.com/ton-connect?<base64_encoded_payload>
|
|
103
106
|
*/
|
|
104
|
-
function buildConnectionRequest(manifestUrl, returnScheme) {
|
|
107
|
+
function buildConnectionRequest(manifestUrl, returnScheme, useUniversalLink = true) {
|
|
105
108
|
const payload = {
|
|
106
109
|
manifestUrl,
|
|
107
110
|
items: [{ name: 'ton_addr' }],
|
|
108
111
|
returnStrategy: 'back',
|
|
109
112
|
};
|
|
110
113
|
const encoded = encodeBase64URL(payload);
|
|
114
|
+
// Use universal link for better Android compatibility
|
|
115
|
+
if (useUniversalLink) {
|
|
116
|
+
return `${CONNECT_UNIVERSAL_PREFIX}?${encoded}`;
|
|
117
|
+
}
|
|
111
118
|
return `${CONNECT_PREFIX}?${encoded}`;
|
|
112
119
|
}
|
|
113
120
|
/**
|
|
114
121
|
* Build transaction request URL
|
|
115
122
|
* Format: tonconnect://send-transaction?<base64_encoded_payload>
|
|
123
|
+
* Or universal link: https://app.tonkeeper.com/ton-connect/send-transaction?<base64_encoded_payload>
|
|
116
124
|
*/
|
|
117
|
-
function buildTransactionRequest(manifestUrl, request, returnScheme) {
|
|
125
|
+
function buildTransactionRequest(manifestUrl, request, returnScheme, useUniversalLink = true) {
|
|
118
126
|
const payload = {
|
|
119
127
|
manifestUrl,
|
|
120
128
|
request: {
|
|
@@ -131,6 +139,10 @@ function buildTransactionRequest(manifestUrl, request, returnScheme) {
|
|
|
131
139
|
returnStrategy: 'back',
|
|
132
140
|
};
|
|
133
141
|
const encoded = encodeBase64URL(payload);
|
|
142
|
+
// Use universal link for better Android compatibility
|
|
143
|
+
if (useUniversalLink) {
|
|
144
|
+
return `${SEND_TRANSACTION_UNIVERSAL_PREFIX}?${encoded}`;
|
|
145
|
+
}
|
|
134
146
|
return `${SEND_TRANSACTION_PREFIX}?${encoded}`;
|
|
135
147
|
}
|
|
136
148
|
/**
|
package/dist/index.js
CHANGED
|
@@ -91,6 +91,7 @@ class TonConnectMobile {
|
|
|
91
91
|
storageKeyPrefix: 'tonconnect_',
|
|
92
92
|
connectionTimeout: 300000, // 5 minutes
|
|
93
93
|
transactionTimeout: 300000, // 5 minutes
|
|
94
|
+
skipCanOpenURLCheck: true, // Skip canOpenURL check by default (Android issue)
|
|
94
95
|
...config,
|
|
95
96
|
};
|
|
96
97
|
console.log('[TON Connect] Initializing SDK with config:', {
|
|
@@ -278,9 +279,9 @@ class TonConnectMobile {
|
|
|
278
279
|
console.log('[TON Connect] Connection already in progress');
|
|
279
280
|
throw new ConnectionInProgressError();
|
|
280
281
|
}
|
|
281
|
-
// Build connection request URL
|
|
282
|
+
// Build connection request URL (use universal link for Android compatibility)
|
|
282
283
|
console.log('[TON Connect] Building connection request URL...');
|
|
283
|
-
const url = (0, protocol_1.buildConnectionRequest)(this.config.manifestUrl, this.config.scheme);
|
|
284
|
+
const url = (0, protocol_1.buildConnectionRequest)(this.config.manifestUrl, this.config.scheme, true);
|
|
284
285
|
// DEBUG: Log the URL being opened
|
|
285
286
|
console.log('[TON Connect] Opening URL:', url);
|
|
286
287
|
console.log('[TON Connect] Manifest URL:', this.config.manifestUrl);
|
|
@@ -316,7 +317,7 @@ class TonConnectMobile {
|
|
|
316
317
|
this.connectionPromise.timeout = timeout;
|
|
317
318
|
// Open wallet app
|
|
318
319
|
console.log('[TON Connect] Attempting to open wallet app...');
|
|
319
|
-
this.adapter.openURL(url).then((success) => {
|
|
320
|
+
this.adapter.openURL(url, this.config.skipCanOpenURLCheck).then((success) => {
|
|
320
321
|
console.log('[TON Connect] openURL result:', success);
|
|
321
322
|
// URL opened successfully, wait for callback
|
|
322
323
|
// If success is false, it should have thrown an error
|
|
@@ -353,8 +354,8 @@ class TonConnectMobile {
|
|
|
353
354
|
if (this.transactionPromise) {
|
|
354
355
|
throw new TransactionInProgressError();
|
|
355
356
|
}
|
|
356
|
-
// Build transaction request URL
|
|
357
|
-
const url = (0, protocol_1.buildTransactionRequest)(this.config.manifestUrl, request, this.config.scheme);
|
|
357
|
+
// Build transaction request URL (use universal link for Android compatibility)
|
|
358
|
+
const url = (0, protocol_1.buildTransactionRequest)(this.config.manifestUrl, request, this.config.scheme, true);
|
|
358
359
|
// Create promise for transaction
|
|
359
360
|
return new Promise((resolve, reject) => {
|
|
360
361
|
let timeout = null;
|
|
@@ -383,7 +384,7 @@ class TonConnectMobile {
|
|
|
383
384
|
}, this.config.transactionTimeout);
|
|
384
385
|
this.transactionPromise.timeout = timeout;
|
|
385
386
|
// Open wallet app
|
|
386
|
-
this.adapter.openURL(url).then((success) => {
|
|
387
|
+
this.adapter.openURL(url, this.config.skipCanOpenURLCheck).then((success) => {
|
|
387
388
|
// URL opened successfully, wait for callback
|
|
388
389
|
// If success is false, it should have thrown an error
|
|
389
390
|
if (!success && this.transactionPromise) {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -157,7 +157,7 @@ export interface ErrorResponse {
|
|
|
157
157
|
*/
|
|
158
158
|
export interface PlatformAdapter {
|
|
159
159
|
/** Open a deep link URL */
|
|
160
|
-
openURL(url: string): Promise<boolean>;
|
|
160
|
+
openURL(url: string, skipCanOpenURLCheck?: boolean): Promise<boolean>;
|
|
161
161
|
/** Get initial URL when app was opened via deep link */
|
|
162
162
|
getInitialURL(): Promise<string | null>;
|
|
163
163
|
/** Add listener for URL changes */
|
|
@@ -185,6 +185,11 @@ export interface TonConnectMobileConfig {
|
|
|
185
185
|
connectionTimeout?: number;
|
|
186
186
|
/** Optional transaction timeout in ms (default: 300000 = 5 minutes) */
|
|
187
187
|
transactionTimeout?: number;
|
|
188
|
+
/** Skip canOpenURL check before opening URL (optional, default: true)
|
|
189
|
+
* Set to false if you want to check if URL can be opened before attempting to open it.
|
|
190
|
+
* Note: On Android, canOpenURL may return false for tonconnect:// even if wallet is installed.
|
|
191
|
+
*/
|
|
192
|
+
skipCanOpenURLCheck?: boolean;
|
|
188
193
|
}
|
|
189
194
|
/**
|
|
190
195
|
* Event listener callback type
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blazium/ton-connect-mobile",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Production-ready TON Connect Mobile SDK for React Native and Expo. Implements the real TonConnect protocol for mobile applications using deep links and callbacks.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/src/adapters/expo.ts
CHANGED
|
@@ -48,29 +48,42 @@ export class ExpoAdapter implements PlatformAdapter {
|
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
async openURL(url: string): Promise<boolean> {
|
|
51
|
+
async openURL(url: string, skipCanOpenURLCheck: boolean = true): Promise<boolean> {
|
|
52
52
|
if (!Linking) {
|
|
53
53
|
throw new Error('expo-linking is not available');
|
|
54
54
|
}
|
|
55
55
|
try {
|
|
56
|
-
console.log('[ExpoAdapter]
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (!
|
|
60
|
-
console.
|
|
61
|
-
|
|
56
|
+
console.log('[ExpoAdapter] Opening URL:', url);
|
|
57
|
+
|
|
58
|
+
// Optional canOpenURL check (can be disabled via config)
|
|
59
|
+
if (!skipCanOpenURLCheck) {
|
|
60
|
+
console.log('[ExpoAdapter] Checking if URL can be opened...');
|
|
61
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
62
|
+
console.log('[ExpoAdapter] canOpenURL result:', canOpen);
|
|
63
|
+
if (!canOpen) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'Cannot open URL. Make sure a wallet app is installed that supports tonconnect:// protocol.'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
console.log('[ExpoAdapter] Skipping canOpenURL check (Android compatibility)');
|
|
62
70
|
}
|
|
63
|
-
|
|
71
|
+
|
|
72
|
+
// CRITICAL FIX: Android'de canOpenURL() tonconnect:// protokolünü tanımayabilir
|
|
73
|
+
// Bu yüzden direkt openURL() çağırıyoruz. Eğer açılamazsa hata fırlatır.
|
|
64
74
|
await Linking.openURL(url);
|
|
65
75
|
console.log('[ExpoAdapter] URL opened successfully');
|
|
66
76
|
return true;
|
|
67
77
|
} catch (error: any) {
|
|
68
78
|
console.error('[ExpoAdapter] Error in openURL:', error);
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
// Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
|
|
80
|
+
const errorMessage = error?.message || String(error);
|
|
81
|
+
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
'No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.'
|
|
84
|
+
);
|
|
72
85
|
}
|
|
73
|
-
throw new Error(`Failed to open
|
|
86
|
+
throw new Error(`Failed to open wallet app: ${errorMessage}`);
|
|
74
87
|
}
|
|
75
88
|
}
|
|
76
89
|
|
|
@@ -46,29 +46,42 @@ export class ReactNativeAdapter implements PlatformAdapter {
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
async openURL(url: string): Promise<boolean> {
|
|
49
|
+
async openURL(url: string, skipCanOpenURLCheck: boolean = true): Promise<boolean> {
|
|
50
50
|
if (!Linking) {
|
|
51
51
|
throw new Error('react-native Linking is not available');
|
|
52
52
|
}
|
|
53
53
|
try {
|
|
54
|
-
console.log('[ReactNativeAdapter]
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (!
|
|
58
|
-
console.
|
|
59
|
-
|
|
54
|
+
console.log('[ReactNativeAdapter] Opening URL:', url);
|
|
55
|
+
|
|
56
|
+
// Optional canOpenURL check (can be disabled via config)
|
|
57
|
+
if (!skipCanOpenURLCheck) {
|
|
58
|
+
console.log('[ReactNativeAdapter] Checking if URL can be opened...');
|
|
59
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
60
|
+
console.log('[ReactNativeAdapter] canOpenURL result:', canOpen);
|
|
61
|
+
if (!canOpen) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
'Cannot open URL. Make sure a wallet app is installed that supports tonconnect:// protocol.'
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
console.log('[ReactNativeAdapter] Skipping canOpenURL check (Android compatibility)');
|
|
60
68
|
}
|
|
61
|
-
|
|
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.
|
|
62
72
|
await Linking.openURL(url);
|
|
63
73
|
console.log('[ReactNativeAdapter] URL opened successfully');
|
|
64
74
|
return true;
|
|
65
75
|
} catch (error: any) {
|
|
66
76
|
console.error('[ReactNativeAdapter] Error in openURL:', error);
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
// Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
|
|
78
|
+
const errorMessage = error?.message || String(error);
|
|
79
|
+
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
'No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.'
|
|
82
|
+
);
|
|
70
83
|
}
|
|
71
|
-
throw new Error(`Failed to open
|
|
84
|
+
throw new Error(`Failed to open wallet app: ${errorMessage}`);
|
|
72
85
|
}
|
|
73
86
|
}
|
|
74
87
|
|
package/src/adapters/web.ts
CHANGED
|
@@ -66,7 +66,7 @@ export class WebAdapter implements PlatformAdapter {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
async openURL(url: string): Promise<boolean> {
|
|
69
|
+
async openURL(url: string, skipCanOpenURLCheck?: boolean): Promise<boolean> {
|
|
70
70
|
try {
|
|
71
71
|
if (typeof window !== 'undefined') {
|
|
72
72
|
// For web, tonconnect:// deep links don't work
|
package/src/core/protocol.ts
CHANGED
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
*/
|
|
27
27
|
const PROTOCOL_VERSION = '2';
|
|
28
28
|
const CONNECT_PREFIX = 'tonconnect://connect';
|
|
29
|
+
const CONNECT_UNIVERSAL_PREFIX = 'https://app.tonkeeper.com/ton-connect';
|
|
29
30
|
const SEND_TRANSACTION_PREFIX = 'tonconnect://send-transaction';
|
|
31
|
+
const SEND_TRANSACTION_UNIVERSAL_PREFIX = 'https://app.tonkeeper.com/ton-connect/send-transaction';
|
|
30
32
|
const CALLBACK_PREFIX = 'tonconnect';
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -122,10 +124,12 @@ export function decodeBase64URL<T>(encoded: string): T {
|
|
|
122
124
|
/**
|
|
123
125
|
* Build connection request URL
|
|
124
126
|
* Format: tonconnect://connect?<base64_encoded_payload>
|
|
127
|
+
* Or universal link: https://app.tonkeeper.com/ton-connect?<base64_encoded_payload>
|
|
125
128
|
*/
|
|
126
129
|
export function buildConnectionRequest(
|
|
127
130
|
manifestUrl: string,
|
|
128
|
-
returnScheme: string
|
|
131
|
+
returnScheme: string,
|
|
132
|
+
useUniversalLink: boolean = true
|
|
129
133
|
): string {
|
|
130
134
|
const payload: ConnectionRequestPayload = {
|
|
131
135
|
manifestUrl,
|
|
@@ -134,17 +138,25 @@ export function buildConnectionRequest(
|
|
|
134
138
|
};
|
|
135
139
|
|
|
136
140
|
const encoded = encodeBase64URL(payload);
|
|
141
|
+
|
|
142
|
+
// Use universal link for better Android compatibility
|
|
143
|
+
if (useUniversalLink) {
|
|
144
|
+
return `${CONNECT_UNIVERSAL_PREFIX}?${encoded}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
137
147
|
return `${CONNECT_PREFIX}?${encoded}`;
|
|
138
148
|
}
|
|
139
149
|
|
|
140
150
|
/**
|
|
141
151
|
* Build transaction request URL
|
|
142
152
|
* Format: tonconnect://send-transaction?<base64_encoded_payload>
|
|
153
|
+
* Or universal link: https://app.tonkeeper.com/ton-connect/send-transaction?<base64_encoded_payload>
|
|
143
154
|
*/
|
|
144
155
|
export function buildTransactionRequest(
|
|
145
156
|
manifestUrl: string,
|
|
146
157
|
request: SendTransactionRequest,
|
|
147
|
-
returnScheme: string
|
|
158
|
+
returnScheme: string,
|
|
159
|
+
useUniversalLink: boolean = true
|
|
148
160
|
): string {
|
|
149
161
|
const payload: TransactionRequestPayload = {
|
|
150
162
|
manifestUrl,
|
|
@@ -163,6 +175,12 @@ export function buildTransactionRequest(
|
|
|
163
175
|
};
|
|
164
176
|
|
|
165
177
|
const encoded = encodeBase64URL(payload);
|
|
178
|
+
|
|
179
|
+
// Use universal link for better Android compatibility
|
|
180
|
+
if (useUniversalLink) {
|
|
181
|
+
return `${SEND_TRANSACTION_UNIVERSAL_PREFIX}?${encoded}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
166
184
|
return `${SEND_TRANSACTION_PREFIX}?${encoded}`;
|
|
167
185
|
}
|
|
168
186
|
|
package/src/index.ts
CHANGED
|
@@ -111,6 +111,7 @@ export class TonConnectMobile {
|
|
|
111
111
|
storageKeyPrefix: 'tonconnect_',
|
|
112
112
|
connectionTimeout: 300000, // 5 minutes
|
|
113
113
|
transactionTimeout: 300000, // 5 minutes
|
|
114
|
+
skipCanOpenURLCheck: true, // Skip canOpenURL check by default (Android issue)
|
|
114
115
|
...config,
|
|
115
116
|
};
|
|
116
117
|
|
|
@@ -319,9 +320,9 @@ export class TonConnectMobile {
|
|
|
319
320
|
throw new ConnectionInProgressError();
|
|
320
321
|
}
|
|
321
322
|
|
|
322
|
-
// Build connection request URL
|
|
323
|
+
// Build connection request URL (use universal link for Android compatibility)
|
|
323
324
|
console.log('[TON Connect] Building connection request URL...');
|
|
324
|
-
const url = buildConnectionRequest(this.config.manifestUrl, this.config.scheme);
|
|
325
|
+
const url = buildConnectionRequest(this.config.manifestUrl, this.config.scheme, true);
|
|
325
326
|
|
|
326
327
|
// DEBUG: Log the URL being opened
|
|
327
328
|
console.log('[TON Connect] Opening URL:', url);
|
|
@@ -363,7 +364,7 @@ export class TonConnectMobile {
|
|
|
363
364
|
|
|
364
365
|
// Open wallet app
|
|
365
366
|
console.log('[TON Connect] Attempting to open wallet app...');
|
|
366
|
-
this.adapter.openURL(url).then((success) => {
|
|
367
|
+
this.adapter.openURL(url, this.config.skipCanOpenURLCheck).then((success) => {
|
|
367
368
|
console.log('[TON Connect] openURL result:', success);
|
|
368
369
|
// URL opened successfully, wait for callback
|
|
369
370
|
// If success is false, it should have thrown an error
|
|
@@ -407,8 +408,8 @@ export class TonConnectMobile {
|
|
|
407
408
|
throw new TransactionInProgressError();
|
|
408
409
|
}
|
|
409
410
|
|
|
410
|
-
// Build transaction request URL
|
|
411
|
-
const url = buildTransactionRequest(this.config.manifestUrl, request, this.config.scheme);
|
|
411
|
+
// Build transaction request URL (use universal link for Android compatibility)
|
|
412
|
+
const url = buildTransactionRequest(this.config.manifestUrl, request, this.config.scheme, true);
|
|
412
413
|
|
|
413
414
|
// Create promise for transaction
|
|
414
415
|
return new Promise<{ boc: string; signature: string }>((resolve, reject) => {
|
|
@@ -442,7 +443,7 @@ export class TonConnectMobile {
|
|
|
442
443
|
this.transactionPromise.timeout = timeout;
|
|
443
444
|
|
|
444
445
|
// Open wallet app
|
|
445
|
-
this.adapter.openURL(url).then((success) => {
|
|
446
|
+
this.adapter.openURL(url, this.config.skipCanOpenURLCheck).then((success) => {
|
|
446
447
|
// URL opened successfully, wait for callback
|
|
447
448
|
// If success is false, it should have thrown an error
|
|
448
449
|
if (!success && this.transactionPromise) {
|
package/src/types/index.ts
CHANGED
|
@@ -168,7 +168,7 @@ export interface ErrorResponse {
|
|
|
168
168
|
*/
|
|
169
169
|
export interface PlatformAdapter {
|
|
170
170
|
/** Open a deep link URL */
|
|
171
|
-
openURL(url: string): Promise<boolean>;
|
|
171
|
+
openURL(url: string, skipCanOpenURLCheck?: boolean): Promise<boolean>;
|
|
172
172
|
/** Get initial URL when app was opened via deep link */
|
|
173
173
|
getInitialURL(): Promise<string | null>;
|
|
174
174
|
/** Add listener for URL changes */
|
|
@@ -197,6 +197,11 @@ export interface TonConnectMobileConfig {
|
|
|
197
197
|
connectionTimeout?: number;
|
|
198
198
|
/** Optional transaction timeout in ms (default: 300000 = 5 minutes) */
|
|
199
199
|
transactionTimeout?: number;
|
|
200
|
+
/** Skip canOpenURL check before opening URL (optional, default: true)
|
|
201
|
+
* Set to false if you want to check if URL can be opened before attempting to open it.
|
|
202
|
+
* Note: On Android, canOpenURL may return false for tonconnect:// even if wallet is installed.
|
|
203
|
+
*/
|
|
204
|
+
skipCanOpenURLCheck?: boolean;
|
|
200
205
|
}
|
|
201
206
|
|
|
202
207
|
/**
|