@blazium/ton-connect-mobile 1.0.4 → 1.0.6
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 +14 -2
- package/dist/adapters/react-native.d.ts +1 -1
- package/dist/adapters/react-native.js +14 -2
- package/dist/adapters/web.d.ts +1 -1
- package/dist/adapters/web.js +1 -1
- package/dist/core/protocol.d.ts +6 -2
- package/dist/core/protocol.js +24 -4
- package/dist/core/wallets.d.ts +34 -0
- package/dist/core/wallets.js +61 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +58 -8
- package/dist/types/index.d.ts +11 -1
- package/package.json +1 -1
- package/src/adapters/expo.ts +17 -2
- package/src/adapters/react-native.ts +17 -2
- package/src/adapters/web.ts +1 -1
- package/src/core/protocol.ts +30 -4
- package/src/core/wallets.ts +77 -0
- package/src/index.ts +59 -9
- package/src/types/index.ts +11 -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,12 +39,24 @@ 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
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
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.log('[ExpoAdapter] Skipping canOpenURL check (Android compatibility)');
|
|
59
|
+
}
|
|
48
60
|
// CRITICAL FIX: Android'de canOpenURL() tonconnect:// protokolünü tanımayabilir
|
|
49
61
|
// Bu yüzden direkt openURL() çağırıyoruz. Eğer açılamazsa hata fırlatır.
|
|
50
62
|
await Linking.openURL(url);
|
|
@@ -55,7 +67,7 @@ class ExpoAdapter {
|
|
|
55
67
|
console.error('[ExpoAdapter] Error in openURL:', error);
|
|
56
68
|
// Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
|
|
57
69
|
const errorMessage = error?.message || String(error);
|
|
58
|
-
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found')) {
|
|
70
|
+
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
|
|
59
71
|
throw new Error('No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.');
|
|
60
72
|
}
|
|
61
73
|
throw new Error(`Failed to open wallet app: ${errorMessage}`);
|
|
@@ -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,12 +37,24 @@ 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
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
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.log('[ReactNativeAdapter] Skipping canOpenURL check (Android compatibility)');
|
|
57
|
+
}
|
|
46
58
|
// CRITICAL FIX: Android'de canOpenURL() tonconnect:// protokolünü tanımayabilir
|
|
47
59
|
// Bu yüzden direkt openURL() çağırıyoruz. Eğer açılamazsa hata fırlatır.
|
|
48
60
|
await Linking.openURL(url);
|
|
@@ -53,7 +65,7 @@ class ReactNativeAdapter {
|
|
|
53
65
|
console.error('[ReactNativeAdapter] Error in openURL:', error);
|
|
54
66
|
// Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
|
|
55
67
|
const errorMessage = error?.message || String(error);
|
|
56
|
-
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found')) {
|
|
68
|
+
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
|
|
57
69
|
throw new Error('No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.');
|
|
58
70
|
}
|
|
59
71
|
throw new Error(`Failed to open wallet app: ${errorMessage}`);
|
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,17 @@ 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>
|
|
18
|
+
* Or custom wallet universal link
|
|
17
19
|
*/
|
|
18
|
-
export declare function buildConnectionRequest(manifestUrl: string, returnScheme: string): string;
|
|
20
|
+
export declare function buildConnectionRequest(manifestUrl: string, returnScheme: string, walletUniversalLink?: string): string;
|
|
19
21
|
/**
|
|
20
22
|
* Build transaction request URL
|
|
21
23
|
* Format: tonconnect://send-transaction?<base64_encoded_payload>
|
|
24
|
+
* Or universal link: https://app.tonkeeper.com/ton-connect/send-transaction?<base64_encoded_payload>
|
|
25
|
+
* Or custom wallet universal link
|
|
22
26
|
*/
|
|
23
|
-
export declare function buildTransactionRequest(manifestUrl: string, request: SendTransactionRequest, returnScheme: string): string;
|
|
27
|
+
export declare function buildTransactionRequest(manifestUrl: string, request: SendTransactionRequest, returnScheme: string, walletUniversalLink?: string): string;
|
|
24
28
|
/**
|
|
25
29
|
* Parse callback URL
|
|
26
30
|
* 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,30 @@ 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>
|
|
106
|
+
* Or custom wallet universal link
|
|
103
107
|
*/
|
|
104
|
-
function buildConnectionRequest(manifestUrl, returnScheme) {
|
|
108
|
+
function buildConnectionRequest(manifestUrl, returnScheme, walletUniversalLink) {
|
|
105
109
|
const payload = {
|
|
106
110
|
manifestUrl,
|
|
107
111
|
items: [{ name: 'ton_addr' }],
|
|
108
112
|
returnStrategy: 'back',
|
|
109
113
|
};
|
|
110
114
|
const encoded = encodeBase64URL(payload);
|
|
111
|
-
|
|
115
|
+
// Use custom wallet universal link if provided
|
|
116
|
+
if (walletUniversalLink) {
|
|
117
|
+
return `${walletUniversalLink}?${encoded}`;
|
|
118
|
+
}
|
|
119
|
+
// Default to Tonkeeper universal link for Android compatibility
|
|
120
|
+
return `${CONNECT_UNIVERSAL_PREFIX}?${encoded}`;
|
|
112
121
|
}
|
|
113
122
|
/**
|
|
114
123
|
* Build transaction request URL
|
|
115
124
|
* Format: tonconnect://send-transaction?<base64_encoded_payload>
|
|
125
|
+
* Or universal link: https://app.tonkeeper.com/ton-connect/send-transaction?<base64_encoded_payload>
|
|
126
|
+
* Or custom wallet universal link
|
|
116
127
|
*/
|
|
117
|
-
function buildTransactionRequest(manifestUrl, request, returnScheme) {
|
|
128
|
+
function buildTransactionRequest(manifestUrl, request, returnScheme, walletUniversalLink) {
|
|
118
129
|
const payload = {
|
|
119
130
|
manifestUrl,
|
|
120
131
|
request: {
|
|
@@ -131,7 +142,16 @@ function buildTransactionRequest(manifestUrl, request, returnScheme) {
|
|
|
131
142
|
returnStrategy: 'back',
|
|
132
143
|
};
|
|
133
144
|
const encoded = encodeBase64URL(payload);
|
|
134
|
-
|
|
145
|
+
// Use custom wallet universal link if provided
|
|
146
|
+
if (walletUniversalLink) {
|
|
147
|
+
// For transaction, append /send-transaction to the base universal link
|
|
148
|
+
const baseUrl = walletUniversalLink.endsWith('/ton-connect')
|
|
149
|
+
? walletUniversalLink
|
|
150
|
+
: `${walletUniversalLink}/ton-connect`;
|
|
151
|
+
return `${baseUrl}/send-transaction?${encoded}`;
|
|
152
|
+
}
|
|
153
|
+
// Default to Tonkeeper universal link for Android compatibility
|
|
154
|
+
return `${SEND_TRANSACTION_UNIVERSAL_PREFIX}?${encoded}`;
|
|
135
155
|
}
|
|
136
156
|
/**
|
|
137
157
|
* Parse callback URL
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TON Connect compatible wallet definitions
|
|
3
|
+
* Each wallet has its own universal link format
|
|
4
|
+
*/
|
|
5
|
+
export interface WalletDefinition {
|
|
6
|
+
/** Wallet name for display */
|
|
7
|
+
name: string;
|
|
8
|
+
/** Wallet app name */
|
|
9
|
+
appName: string;
|
|
10
|
+
/** Universal link base URL for this wallet */
|
|
11
|
+
universalLink: string;
|
|
12
|
+
/** Deep link scheme (if supported) */
|
|
13
|
+
deepLink?: string;
|
|
14
|
+
/** Wallet icon URL (optional) */
|
|
15
|
+
iconUrl?: string;
|
|
16
|
+
/** Platform support */
|
|
17
|
+
platforms: ('ios' | 'android' | 'web')[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* List of supported TON Connect wallets
|
|
21
|
+
*/
|
|
22
|
+
export declare const SUPPORTED_WALLETS: WalletDefinition[];
|
|
23
|
+
/**
|
|
24
|
+
* Get wallet definition by name
|
|
25
|
+
*/
|
|
26
|
+
export declare function getWalletByName(name: string): WalletDefinition | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Get default wallet (Tonkeeper)
|
|
29
|
+
*/
|
|
30
|
+
export declare function getDefaultWallet(): WalletDefinition;
|
|
31
|
+
/**
|
|
32
|
+
* Get all wallets for a specific platform
|
|
33
|
+
*/
|
|
34
|
+
export declare function getWalletsForPlatform(platform: 'ios' | 'android' | 'web'): WalletDefinition[];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TON Connect compatible wallet definitions
|
|
4
|
+
* Each wallet has its own universal link format
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SUPPORTED_WALLETS = void 0;
|
|
8
|
+
exports.getWalletByName = getWalletByName;
|
|
9
|
+
exports.getDefaultWallet = getDefaultWallet;
|
|
10
|
+
exports.getWalletsForPlatform = getWalletsForPlatform;
|
|
11
|
+
/**
|
|
12
|
+
* List of supported TON Connect wallets
|
|
13
|
+
*/
|
|
14
|
+
exports.SUPPORTED_WALLETS = [
|
|
15
|
+
{
|
|
16
|
+
name: 'Tonkeeper',
|
|
17
|
+
appName: 'Tonkeeper',
|
|
18
|
+
universalLink: 'https://app.tonkeeper.com/ton-connect',
|
|
19
|
+
deepLink: 'tonkeeper://',
|
|
20
|
+
platforms: ['ios', 'android'],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'MyTonWallet',
|
|
24
|
+
appName: 'MyTonWallet',
|
|
25
|
+
universalLink: 'https://connect.mytonwallet.org',
|
|
26
|
+
deepLink: 'mytonwallet://',
|
|
27
|
+
platforms: ['ios', 'android', 'web'],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Wallet in Telegram',
|
|
31
|
+
appName: 'Wallet',
|
|
32
|
+
universalLink: 'https://wallet.tonapi.io/ton-connect',
|
|
33
|
+
deepLink: 'tg://',
|
|
34
|
+
platforms: ['ios', 'android'],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Tonhub',
|
|
38
|
+
appName: 'Tonhub',
|
|
39
|
+
universalLink: 'https://tonhub.com/ton-connect',
|
|
40
|
+
deepLink: 'tonhub://',
|
|
41
|
+
platforms: ['ios', 'android'],
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* Get wallet definition by name
|
|
46
|
+
*/
|
|
47
|
+
function getWalletByName(name) {
|
|
48
|
+
return exports.SUPPORTED_WALLETS.find((wallet) => wallet.name.toLowerCase() === name.toLowerCase() || wallet.appName.toLowerCase() === name.toLowerCase());
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get default wallet (Tonkeeper)
|
|
52
|
+
*/
|
|
53
|
+
function getDefaultWallet() {
|
|
54
|
+
return exports.SUPPORTED_WALLETS[0]; // Tonkeeper
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get all wallets for a specific platform
|
|
58
|
+
*/
|
|
59
|
+
function getWalletsForPlatform(platform) {
|
|
60
|
+
return exports.SUPPORTED_WALLETS.filter((wallet) => wallet.platforms.includes(platform));
|
|
61
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Production-ready implementation for React Native and Expo
|
|
4
4
|
*/
|
|
5
5
|
import { TonConnectMobileConfig, ConnectionStatus, WalletInfo, SendTransactionRequest, StatusChangeCallback } from './types';
|
|
6
|
+
import { type WalletDefinition } from './core/wallets';
|
|
6
7
|
/**
|
|
7
8
|
* Custom error classes
|
|
8
9
|
*/
|
|
@@ -34,6 +35,7 @@ export declare class TonConnectMobile {
|
|
|
34
35
|
private statusChangeCallbacks;
|
|
35
36
|
private currentStatus;
|
|
36
37
|
private urlUnsubscribe;
|
|
38
|
+
private currentWallet;
|
|
37
39
|
private connectionPromise;
|
|
38
40
|
private transactionPromise;
|
|
39
41
|
constructor(config: TonConnectMobileConfig);
|
|
@@ -80,6 +82,18 @@ export declare class TonConnectMobile {
|
|
|
80
82
|
* Get current connection status
|
|
81
83
|
*/
|
|
82
84
|
getStatus(): ConnectionStatus;
|
|
85
|
+
/**
|
|
86
|
+
* Get list of supported wallets
|
|
87
|
+
*/
|
|
88
|
+
getSupportedWallets(): WalletDefinition[];
|
|
89
|
+
/**
|
|
90
|
+
* Get current wallet being used
|
|
91
|
+
*/
|
|
92
|
+
getCurrentWallet(): WalletDefinition;
|
|
93
|
+
/**
|
|
94
|
+
* Set preferred wallet for connections
|
|
95
|
+
*/
|
|
96
|
+
setPreferredWallet(walletName: string): void;
|
|
83
97
|
/**
|
|
84
98
|
* Subscribe to status changes
|
|
85
99
|
*/
|
|
@@ -110,3 +124,5 @@ export declare class TonConnectMobile {
|
|
|
110
124
|
destroy(): void;
|
|
111
125
|
}
|
|
112
126
|
export * from './types';
|
|
127
|
+
export type { WalletDefinition } from './core/wallets';
|
|
128
|
+
export { SUPPORTED_WALLETS, getWalletByName, getDefaultWallet, getWalletsForPlatform } from './core/wallets';
|
package/dist/index.js
CHANGED
|
@@ -18,12 +18,13 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
18
18
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.TonConnectMobile = exports.TransactionInProgressError = exports.ConnectionInProgressError = exports.UserRejectedError = exports.TransactionTimeoutError = exports.ConnectionTimeoutError = exports.TonConnectError = void 0;
|
|
21
|
+
exports.getWalletsForPlatform = exports.getDefaultWallet = exports.getWalletByName = exports.SUPPORTED_WALLETS = exports.TonConnectMobile = exports.TransactionInProgressError = exports.ConnectionInProgressError = exports.UserRejectedError = exports.TransactionTimeoutError = exports.ConnectionTimeoutError = exports.TonConnectError = void 0;
|
|
22
22
|
const protocol_1 = require("./core/protocol");
|
|
23
23
|
const crypto_1 = require("./core/crypto");
|
|
24
24
|
const expo_1 = require("./adapters/expo");
|
|
25
25
|
const react_native_1 = require("./adapters/react-native");
|
|
26
26
|
const web_1 = require("./adapters/web");
|
|
27
|
+
const wallets_1 = require("./core/wallets");
|
|
27
28
|
/**
|
|
28
29
|
* Custom error classes
|
|
29
30
|
*/
|
|
@@ -91,11 +92,30 @@ class TonConnectMobile {
|
|
|
91
92
|
storageKeyPrefix: 'tonconnect_',
|
|
92
93
|
connectionTimeout: 300000, // 5 minutes
|
|
93
94
|
transactionTimeout: 300000, // 5 minutes
|
|
95
|
+
skipCanOpenURLCheck: true, // Skip canOpenURL check by default (Android issue)
|
|
96
|
+
preferredWallet: config.preferredWallet,
|
|
94
97
|
...config,
|
|
95
98
|
};
|
|
99
|
+
// Determine which wallet to use
|
|
100
|
+
if (this.config.preferredWallet) {
|
|
101
|
+
const wallet = (0, wallets_1.getWalletByName)(this.config.preferredWallet);
|
|
102
|
+
if (wallet) {
|
|
103
|
+
this.currentWallet = wallet;
|
|
104
|
+
console.log('[TON Connect] Using preferred wallet:', wallet.name);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
console.warn('[TON Connect] Preferred wallet not found, using default');
|
|
108
|
+
this.currentWallet = (0, wallets_1.getDefaultWallet)();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.currentWallet = (0, wallets_1.getDefaultWallet)();
|
|
113
|
+
}
|
|
96
114
|
console.log('[TON Connect] Initializing SDK with config:', {
|
|
97
115
|
manifestUrl: this.config.manifestUrl,
|
|
98
116
|
scheme: this.config.scheme,
|
|
117
|
+
wallet: this.currentWallet.name,
|
|
118
|
+
universalLink: this.currentWallet.universalLink,
|
|
99
119
|
});
|
|
100
120
|
// Initialize platform adapter
|
|
101
121
|
this.adapter = this.createAdapter();
|
|
@@ -278,9 +298,11 @@ class TonConnectMobile {
|
|
|
278
298
|
console.log('[TON Connect] Connection already in progress');
|
|
279
299
|
throw new ConnectionInProgressError();
|
|
280
300
|
}
|
|
281
|
-
// Build connection request URL
|
|
282
|
-
console.log('[TON Connect] Building connection request URL
|
|
283
|
-
|
|
301
|
+
// Build connection request URL (use wallet's universal link)
|
|
302
|
+
console.log('[TON Connect] Building connection request URL for wallet:', this.currentWallet.name);
|
|
303
|
+
console.log('[TON Connect] Using universal link:', this.currentWallet.universalLink);
|
|
304
|
+
const url = (0, protocol_1.buildConnectionRequest)(this.config.manifestUrl, this.config.scheme, this.currentWallet.universalLink);
|
|
305
|
+
console.log('[TON Connect] Built URL:', url.substring(0, 100) + '...');
|
|
284
306
|
// DEBUG: Log the URL being opened
|
|
285
307
|
console.log('[TON Connect] Opening URL:', url);
|
|
286
308
|
console.log('[TON Connect] Manifest URL:', this.config.manifestUrl);
|
|
@@ -316,7 +338,7 @@ class TonConnectMobile {
|
|
|
316
338
|
this.connectionPromise.timeout = timeout;
|
|
317
339
|
// Open wallet app
|
|
318
340
|
console.log('[TON Connect] Attempting to open wallet app...');
|
|
319
|
-
this.adapter.openURL(url).then((success) => {
|
|
341
|
+
this.adapter.openURL(url, this.config.skipCanOpenURLCheck).then((success) => {
|
|
320
342
|
console.log('[TON Connect] openURL result:', success);
|
|
321
343
|
// URL opened successfully, wait for callback
|
|
322
344
|
// If success is false, it should have thrown an error
|
|
@@ -353,8 +375,8 @@ class TonConnectMobile {
|
|
|
353
375
|
if (this.transactionPromise) {
|
|
354
376
|
throw new TransactionInProgressError();
|
|
355
377
|
}
|
|
356
|
-
// Build transaction request URL
|
|
357
|
-
const url = (0, protocol_1.buildTransactionRequest)(this.config.manifestUrl, request, this.config.scheme);
|
|
378
|
+
// Build transaction request URL (use universal link for Android compatibility)
|
|
379
|
+
const url = (0, protocol_1.buildTransactionRequest)(this.config.manifestUrl, request, this.config.scheme, this.currentWallet.universalLink);
|
|
358
380
|
// Create promise for transaction
|
|
359
381
|
return new Promise((resolve, reject) => {
|
|
360
382
|
let timeout = null;
|
|
@@ -383,7 +405,7 @@ class TonConnectMobile {
|
|
|
383
405
|
}, this.config.transactionTimeout);
|
|
384
406
|
this.transactionPromise.timeout = timeout;
|
|
385
407
|
// Open wallet app
|
|
386
|
-
this.adapter.openURL(url).then((success) => {
|
|
408
|
+
this.adapter.openURL(url, this.config.skipCanOpenURLCheck).then((success) => {
|
|
387
409
|
// URL opened successfully, wait for callback
|
|
388
410
|
// If success is false, it should have thrown an error
|
|
389
411
|
if (!success && this.transactionPromise) {
|
|
@@ -413,6 +435,29 @@ class TonConnectMobile {
|
|
|
413
435
|
getStatus() {
|
|
414
436
|
return { ...this.currentStatus };
|
|
415
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Get list of supported wallets
|
|
440
|
+
*/
|
|
441
|
+
getSupportedWallets() {
|
|
442
|
+
return wallets_1.SUPPORTED_WALLETS;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Get current wallet being used
|
|
446
|
+
*/
|
|
447
|
+
getCurrentWallet() {
|
|
448
|
+
return this.currentWallet;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Set preferred wallet for connections
|
|
452
|
+
*/
|
|
453
|
+
setPreferredWallet(walletName) {
|
|
454
|
+
const wallet = (0, wallets_1.getWalletByName)(walletName);
|
|
455
|
+
if (!wallet) {
|
|
456
|
+
throw new TonConnectError(`Wallet "${walletName}" not found. Available wallets: ${wallets_1.SUPPORTED_WALLETS.map(w => w.name).join(', ')}`);
|
|
457
|
+
}
|
|
458
|
+
this.currentWallet = wallet;
|
|
459
|
+
console.log('[TON Connect] Preferred wallet changed to:', wallet.name);
|
|
460
|
+
}
|
|
416
461
|
/**
|
|
417
462
|
* Subscribe to status changes
|
|
418
463
|
*/
|
|
@@ -548,3 +593,8 @@ class TonConnectMobile {
|
|
|
548
593
|
exports.TonConnectMobile = TonConnectMobile;
|
|
549
594
|
// Export types
|
|
550
595
|
__exportStar(require("./types"), exports);
|
|
596
|
+
var wallets_2 = require("./core/wallets");
|
|
597
|
+
Object.defineProperty(exports, "SUPPORTED_WALLETS", { enumerable: true, get: function () { return wallets_2.SUPPORTED_WALLETS; } });
|
|
598
|
+
Object.defineProperty(exports, "getWalletByName", { enumerable: true, get: function () { return wallets_2.getWalletByName; } });
|
|
599
|
+
Object.defineProperty(exports, "getDefaultWallet", { enumerable: true, get: function () { return wallets_2.getDefaultWallet; } });
|
|
600
|
+
Object.defineProperty(exports, "getWalletsForPlatform", { enumerable: true, get: function () { return wallets_2.getWalletsForPlatform; } });
|
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,16 @@ 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;
|
|
193
|
+
/** Preferred wallet name (optional)
|
|
194
|
+
* If not specified, will use default wallet (Tonkeeper)
|
|
195
|
+
* Available: 'Tonkeeper', 'MyTonWallet', 'Wallet in Telegram', 'Tonhub'
|
|
196
|
+
*/
|
|
197
|
+
preferredWallet?: string;
|
|
188
198
|
}
|
|
189
199
|
/**
|
|
190
200
|
* 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.6",
|
|
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,12 +48,27 @@ 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
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)');
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
// CRITICAL FIX: Android'de canOpenURL() tonconnect:// protokolünü tanımayabilir
|
|
58
73
|
// Bu yüzden direkt openURL() çağırıyoruz. Eğer açılamazsa hata fırlatır.
|
|
59
74
|
await Linking.openURL(url);
|
|
@@ -63,7 +78,7 @@ export class ExpoAdapter implements PlatformAdapter {
|
|
|
63
78
|
console.error('[ExpoAdapter] Error in openURL:', error);
|
|
64
79
|
// Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
|
|
65
80
|
const errorMessage = error?.message || String(error);
|
|
66
|
-
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found')) {
|
|
81
|
+
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
|
|
67
82
|
throw new Error(
|
|
68
83
|
'No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.'
|
|
69
84
|
);
|
|
@@ -46,12 +46,27 @@ 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
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)');
|
|
68
|
+
}
|
|
69
|
+
|
|
55
70
|
// CRITICAL FIX: Android'de canOpenURL() tonconnect:// protokolünü tanımayabilir
|
|
56
71
|
// Bu yüzden direkt openURL() çağırıyoruz. Eğer açılamazsa hata fırlatır.
|
|
57
72
|
await Linking.openURL(url);
|
|
@@ -61,7 +76,7 @@ export class ReactNativeAdapter implements PlatformAdapter {
|
|
|
61
76
|
console.error('[ReactNativeAdapter] Error in openURL:', error);
|
|
62
77
|
// Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
|
|
63
78
|
const errorMessage = error?.message || String(error);
|
|
64
|
-
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found')) {
|
|
79
|
+
if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
|
|
65
80
|
throw new Error(
|
|
66
81
|
'No TON wallet app found. Please install Tonkeeper or another TON Connect compatible wallet from Google Play Store.'
|
|
67
82
|
);
|
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,13 @@ 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>
|
|
128
|
+
* Or custom wallet universal link
|
|
125
129
|
*/
|
|
126
130
|
export function buildConnectionRequest(
|
|
127
131
|
manifestUrl: string,
|
|
128
|
-
returnScheme: string
|
|
132
|
+
returnScheme: string,
|
|
133
|
+
walletUniversalLink?: string
|
|
129
134
|
): string {
|
|
130
135
|
const payload: ConnectionRequestPayload = {
|
|
131
136
|
manifestUrl,
|
|
@@ -134,17 +139,27 @@ export function buildConnectionRequest(
|
|
|
134
139
|
};
|
|
135
140
|
|
|
136
141
|
const encoded = encodeBase64URL(payload);
|
|
137
|
-
|
|
142
|
+
|
|
143
|
+
// Use custom wallet universal link if provided
|
|
144
|
+
if (walletUniversalLink) {
|
|
145
|
+
return `${walletUniversalLink}?${encoded}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Default to Tonkeeper universal link for Android compatibility
|
|
149
|
+
return `${CONNECT_UNIVERSAL_PREFIX}?${encoded}`;
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
/**
|
|
141
153
|
* Build transaction request URL
|
|
142
154
|
* Format: tonconnect://send-transaction?<base64_encoded_payload>
|
|
155
|
+
* Or universal link: https://app.tonkeeper.com/ton-connect/send-transaction?<base64_encoded_payload>
|
|
156
|
+
* Or custom wallet universal link
|
|
143
157
|
*/
|
|
144
158
|
export function buildTransactionRequest(
|
|
145
159
|
manifestUrl: string,
|
|
146
160
|
request: SendTransactionRequest,
|
|
147
|
-
returnScheme: string
|
|
161
|
+
returnScheme: string,
|
|
162
|
+
walletUniversalLink?: string
|
|
148
163
|
): string {
|
|
149
164
|
const payload: TransactionRequestPayload = {
|
|
150
165
|
manifestUrl,
|
|
@@ -163,7 +178,18 @@ export function buildTransactionRequest(
|
|
|
163
178
|
};
|
|
164
179
|
|
|
165
180
|
const encoded = encodeBase64URL(payload);
|
|
166
|
-
|
|
181
|
+
|
|
182
|
+
// Use custom wallet universal link if provided
|
|
183
|
+
if (walletUniversalLink) {
|
|
184
|
+
// For transaction, append /send-transaction to the base universal link
|
|
185
|
+
const baseUrl = walletUniversalLink.endsWith('/ton-connect')
|
|
186
|
+
? walletUniversalLink
|
|
187
|
+
: `${walletUniversalLink}/ton-connect`;
|
|
188
|
+
return `${baseUrl}/send-transaction?${encoded}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Default to Tonkeeper universal link for Android compatibility
|
|
192
|
+
return `${SEND_TRANSACTION_UNIVERSAL_PREFIX}?${encoded}`;
|
|
167
193
|
}
|
|
168
194
|
|
|
169
195
|
/**
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TON Connect compatible wallet definitions
|
|
3
|
+
* Each wallet has its own universal link format
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface WalletDefinition {
|
|
7
|
+
/** Wallet name for display */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Wallet app name */
|
|
10
|
+
appName: string;
|
|
11
|
+
/** Universal link base URL for this wallet */
|
|
12
|
+
universalLink: string;
|
|
13
|
+
/** Deep link scheme (if supported) */
|
|
14
|
+
deepLink?: string;
|
|
15
|
+
/** Wallet icon URL (optional) */
|
|
16
|
+
iconUrl?: string;
|
|
17
|
+
/** Platform support */
|
|
18
|
+
platforms: ('ios' | 'android' | 'web')[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* List of supported TON Connect wallets
|
|
23
|
+
*/
|
|
24
|
+
export const SUPPORTED_WALLETS: WalletDefinition[] = [
|
|
25
|
+
{
|
|
26
|
+
name: 'Tonkeeper',
|
|
27
|
+
appName: 'Tonkeeper',
|
|
28
|
+
universalLink: 'https://app.tonkeeper.com/ton-connect',
|
|
29
|
+
deepLink: 'tonkeeper://',
|
|
30
|
+
platforms: ['ios', 'android'],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'MyTonWallet',
|
|
34
|
+
appName: 'MyTonWallet',
|
|
35
|
+
universalLink: 'https://connect.mytonwallet.org',
|
|
36
|
+
deepLink: 'mytonwallet://',
|
|
37
|
+
platforms: ['ios', 'android', 'web'],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Wallet in Telegram',
|
|
41
|
+
appName: 'Wallet',
|
|
42
|
+
universalLink: 'https://wallet.tonapi.io/ton-connect',
|
|
43
|
+
deepLink: 'tg://',
|
|
44
|
+
platforms: ['ios', 'android'],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Tonhub',
|
|
48
|
+
appName: 'Tonhub',
|
|
49
|
+
universalLink: 'https://tonhub.com/ton-connect',
|
|
50
|
+
deepLink: 'tonhub://',
|
|
51
|
+
platforms: ['ios', 'android'],
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get wallet definition by name
|
|
57
|
+
*/
|
|
58
|
+
export function getWalletByName(name: string): WalletDefinition | undefined {
|
|
59
|
+
return SUPPORTED_WALLETS.find(
|
|
60
|
+
(wallet) => wallet.name.toLowerCase() === name.toLowerCase() || wallet.appName.toLowerCase() === name.toLowerCase()
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get default wallet (Tonkeeper)
|
|
66
|
+
*/
|
|
67
|
+
export function getDefaultWallet(): WalletDefinition {
|
|
68
|
+
return SUPPORTED_WALLETS[0]; // Tonkeeper
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get all wallets for a specific platform
|
|
73
|
+
*/
|
|
74
|
+
export function getWalletsForPlatform(platform: 'ios' | 'android' | 'web'): WalletDefinition[] {
|
|
75
|
+
return SUPPORTED_WALLETS.filter((wallet) => wallet.platforms.includes(platform));
|
|
76
|
+
}
|
|
77
|
+
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ import { verifyConnectionProof, generateSessionId } from './core/crypto';
|
|
|
32
32
|
import { ExpoAdapter } from './adapters/expo';
|
|
33
33
|
import { ReactNativeAdapter } from './adapters/react-native';
|
|
34
34
|
import { WebAdapter } from './adapters/web';
|
|
35
|
+
import { getWalletByName, getDefaultWallet, SUPPORTED_WALLETS, type WalletDefinition } from './core/wallets';
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Custom error classes
|
|
@@ -83,10 +84,11 @@ export class TransactionInProgressError extends TonConnectError {
|
|
|
83
84
|
*/
|
|
84
85
|
export class TonConnectMobile {
|
|
85
86
|
private adapter: PlatformAdapter;
|
|
86
|
-
private config: Required<TonConnectMobileConfig
|
|
87
|
+
private config: Required<Omit<TonConnectMobileConfig, 'preferredWallet'>> & { preferredWallet?: string };
|
|
87
88
|
private statusChangeCallbacks: Set<StatusChangeCallback> = new Set();
|
|
88
89
|
private currentStatus: ConnectionStatus = { connected: false, wallet: null };
|
|
89
90
|
private urlUnsubscribe: (() => void) | null = null;
|
|
91
|
+
private currentWallet!: WalletDefinition;
|
|
90
92
|
private connectionPromise: {
|
|
91
93
|
resolve: (wallet: WalletInfo) => void;
|
|
92
94
|
reject: (error: Error) => void;
|
|
@@ -111,12 +113,30 @@ export class TonConnectMobile {
|
|
|
111
113
|
storageKeyPrefix: 'tonconnect_',
|
|
112
114
|
connectionTimeout: 300000, // 5 minutes
|
|
113
115
|
transactionTimeout: 300000, // 5 minutes
|
|
116
|
+
skipCanOpenURLCheck: true, // Skip canOpenURL check by default (Android issue)
|
|
117
|
+
preferredWallet: config.preferredWallet,
|
|
114
118
|
...config,
|
|
115
|
-
};
|
|
119
|
+
} as Required<Omit<TonConnectMobileConfig, 'preferredWallet'>> & { preferredWallet?: string };
|
|
120
|
+
|
|
121
|
+
// Determine which wallet to use
|
|
122
|
+
if (this.config.preferredWallet) {
|
|
123
|
+
const wallet = getWalletByName(this.config.preferredWallet);
|
|
124
|
+
if (wallet) {
|
|
125
|
+
this.currentWallet = wallet;
|
|
126
|
+
console.log('[TON Connect] Using preferred wallet:', wallet.name);
|
|
127
|
+
} else {
|
|
128
|
+
console.warn('[TON Connect] Preferred wallet not found, using default');
|
|
129
|
+
this.currentWallet = getDefaultWallet();
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
this.currentWallet = getDefaultWallet();
|
|
133
|
+
}
|
|
116
134
|
|
|
117
135
|
console.log('[TON Connect] Initializing SDK with config:', {
|
|
118
136
|
manifestUrl: this.config.manifestUrl,
|
|
119
137
|
scheme: this.config.scheme,
|
|
138
|
+
wallet: this.currentWallet.name,
|
|
139
|
+
universalLink: this.currentWallet.universalLink,
|
|
120
140
|
});
|
|
121
141
|
|
|
122
142
|
// Initialize platform adapter
|
|
@@ -319,9 +339,11 @@ export class TonConnectMobile {
|
|
|
319
339
|
throw new ConnectionInProgressError();
|
|
320
340
|
}
|
|
321
341
|
|
|
322
|
-
// Build connection request URL
|
|
323
|
-
console.log('[TON Connect] Building connection request URL
|
|
324
|
-
|
|
342
|
+
// Build connection request URL (use wallet's universal link)
|
|
343
|
+
console.log('[TON Connect] Building connection request URL for wallet:', this.currentWallet.name);
|
|
344
|
+
console.log('[TON Connect] Using universal link:', this.currentWallet.universalLink);
|
|
345
|
+
const url = buildConnectionRequest(this.config.manifestUrl, this.config.scheme, this.currentWallet.universalLink);
|
|
346
|
+
console.log('[TON Connect] Built URL:', url.substring(0, 100) + '...');
|
|
325
347
|
|
|
326
348
|
// DEBUG: Log the URL being opened
|
|
327
349
|
console.log('[TON Connect] Opening URL:', url);
|
|
@@ -363,7 +385,7 @@ export class TonConnectMobile {
|
|
|
363
385
|
|
|
364
386
|
// Open wallet app
|
|
365
387
|
console.log('[TON Connect] Attempting to open wallet app...');
|
|
366
|
-
this.adapter.openURL(url).then((success) => {
|
|
388
|
+
this.adapter.openURL(url, this.config.skipCanOpenURLCheck).then((success) => {
|
|
367
389
|
console.log('[TON Connect] openURL result:', success);
|
|
368
390
|
// URL opened successfully, wait for callback
|
|
369
391
|
// If success is false, it should have thrown an error
|
|
@@ -407,8 +429,8 @@ export class TonConnectMobile {
|
|
|
407
429
|
throw new TransactionInProgressError();
|
|
408
430
|
}
|
|
409
431
|
|
|
410
|
-
// Build transaction request URL
|
|
411
|
-
const url = buildTransactionRequest(this.config.manifestUrl, request, this.config.scheme);
|
|
432
|
+
// Build transaction request URL (use universal link for Android compatibility)
|
|
433
|
+
const url = buildTransactionRequest(this.config.manifestUrl, request, this.config.scheme, this.currentWallet.universalLink);
|
|
412
434
|
|
|
413
435
|
// Create promise for transaction
|
|
414
436
|
return new Promise<{ boc: string; signature: string }>((resolve, reject) => {
|
|
@@ -442,7 +464,7 @@ export class TonConnectMobile {
|
|
|
442
464
|
this.transactionPromise.timeout = timeout;
|
|
443
465
|
|
|
444
466
|
// Open wallet app
|
|
445
|
-
this.adapter.openURL(url).then((success) => {
|
|
467
|
+
this.adapter.openURL(url, this.config.skipCanOpenURLCheck).then((success) => {
|
|
446
468
|
// URL opened successfully, wait for callback
|
|
447
469
|
// If success is false, it should have thrown an error
|
|
448
470
|
if (!success && this.transactionPromise) {
|
|
@@ -480,6 +502,32 @@ export class TonConnectMobile {
|
|
|
480
502
|
return { ...this.currentStatus };
|
|
481
503
|
}
|
|
482
504
|
|
|
505
|
+
/**
|
|
506
|
+
* Get list of supported wallets
|
|
507
|
+
*/
|
|
508
|
+
getSupportedWallets(): WalletDefinition[] {
|
|
509
|
+
return SUPPORTED_WALLETS;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Get current wallet being used
|
|
514
|
+
*/
|
|
515
|
+
getCurrentWallet(): WalletDefinition {
|
|
516
|
+
return this.currentWallet;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Set preferred wallet for connections
|
|
521
|
+
*/
|
|
522
|
+
setPreferredWallet(walletName: string): void {
|
|
523
|
+
const wallet = getWalletByName(walletName);
|
|
524
|
+
if (!wallet) {
|
|
525
|
+
throw new TonConnectError(`Wallet "${walletName}" not found. Available wallets: ${SUPPORTED_WALLETS.map(w => w.name).join(', ')}`);
|
|
526
|
+
}
|
|
527
|
+
this.currentWallet = wallet;
|
|
528
|
+
console.log('[TON Connect] Preferred wallet changed to:', wallet.name);
|
|
529
|
+
}
|
|
530
|
+
|
|
483
531
|
/**
|
|
484
532
|
* Subscribe to status changes
|
|
485
533
|
*/
|
|
@@ -628,4 +676,6 @@ export class TonConnectMobile {
|
|
|
628
676
|
|
|
629
677
|
// Export types
|
|
630
678
|
export * from './types';
|
|
679
|
+
export type { WalletDefinition } from './core/wallets';
|
|
680
|
+
export { SUPPORTED_WALLETS, getWalletByName, getDefaultWallet, getWalletsForPlatform } from './core/wallets';
|
|
631
681
|
|
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,16 @@ 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;
|
|
205
|
+
/** Preferred wallet name (optional)
|
|
206
|
+
* If not specified, will use default wallet (Tonkeeper)
|
|
207
|
+
* Available: 'Tonkeeper', 'MyTonWallet', 'Wallet in Telegram', 'Tonhub'
|
|
208
|
+
*/
|
|
209
|
+
preferredWallet?: string;
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
/**
|