@blazium/ton-connect-mobile 1.1.5 → 1.2.1

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.
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ /**
3
+ * WalletSelectionModal component
4
+ * Provides a beautiful wallet selection UI compatible with @tonconnect/ui-react
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.WalletSelectionModal = WalletSelectionModal;
11
+ const react_1 = __importDefault(require("react"));
12
+ const react_native_1 = require("react-native");
13
+ const index_1 = require("./index");
14
+ /**
15
+ * WalletSelectionModal - Beautiful wallet selection modal
16
+ * Compatible with @tonconnect/ui-react modal behavior
17
+ */
18
+ function WalletSelectionModal({ visible, onClose, wallets: customWallets, style, }) {
19
+ const tonConnectUI = (0, index_1.useTonConnectUI)();
20
+ const sdk = (0, index_1.useTonConnectSDK)();
21
+ const [wallets, setWallets] = react_1.default.useState([]);
22
+ const [connectingWallet, setConnectingWallet] = react_1.default.useState(null);
23
+ // Load wallets
24
+ react_1.default.useEffect(() => {
25
+ if (customWallets) {
26
+ setWallets(customWallets);
27
+ }
28
+ else {
29
+ const supportedWallets = sdk.getSupportedWallets();
30
+ // CRITICAL FIX: On web, show all wallets with universalLink (they can open in new tab)
31
+ // On mobile, filter by platform
32
+ const platform = react_native_1.Platform.OS === 'ios' ? 'ios' : react_native_1.Platform.OS === 'android' ? 'android' : 'web';
33
+ let platformWallets;
34
+ if (platform === 'web') {
35
+ // On web, show all wallets with universalLink (they can open in new tab)
36
+ platformWallets = supportedWallets.filter((w) => w.platforms.includes('web') || !!w.universalLink);
37
+ }
38
+ else {
39
+ platformWallets = supportedWallets.filter((w) => w.platforms.includes(platform));
40
+ }
41
+ setWallets(platformWallets);
42
+ }
43
+ }, [sdk, customWallets]);
44
+ // Handle wallet selection
45
+ const handleSelectWallet = async (wallet) => {
46
+ // Prevent multiple simultaneous connection attempts
47
+ if (connectingWallet) {
48
+ return;
49
+ }
50
+ try {
51
+ setConnectingWallet(wallet.name);
52
+ // Set preferred wallet
53
+ try {
54
+ sdk.setPreferredWallet(wallet.name);
55
+ }
56
+ catch (error) {
57
+ console.error('[WalletSelectionModal] Failed to set preferred wallet:', error);
58
+ // Continue anyway - SDK will use default wallet
59
+ }
60
+ // Close modal
61
+ onClose();
62
+ // Small delay to ensure modal closes
63
+ await new Promise((resolve) => setTimeout(() => resolve(), 100));
64
+ // Connect
65
+ await tonConnectUI.connectWallet();
66
+ }
67
+ catch (error) {
68
+ console.error('[WalletSelectionModal] Wallet connection error:', error);
69
+ setConnectingWallet(null);
70
+ // Error is handled by SDK/UI, just reset connecting state
71
+ }
72
+ };
73
+ return (react_1.default.createElement(react_native_1.Modal, { visible: visible, animationType: "slide", transparent: true, onRequestClose: onClose },
74
+ react_1.default.createElement(react_native_1.View, { style: [styles.overlay, style] },
75
+ react_1.default.createElement(react_native_1.View, { style: styles.modalContainer },
76
+ react_1.default.createElement(react_native_1.View, { style: styles.header },
77
+ react_1.default.createElement(react_native_1.Text, { style: styles.title }, "Connect your TON wallet"),
78
+ react_1.default.createElement(react_native_1.Text, { style: styles.subtitle }, "Choose a wallet to connect to this app"),
79
+ react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.closeButton, onPress: onClose },
80
+ react_1.default.createElement(react_native_1.Text, { style: styles.closeButtonText }, "\u2715"))),
81
+ react_1.default.createElement(react_native_1.ScrollView, { style: styles.walletList, showsVerticalScrollIndicator: false }, wallets.length === 0 ? (react_1.default.createElement(react_native_1.View, { style: styles.emptyState },
82
+ react_1.default.createElement(react_native_1.Text, { style: styles.emptyStateText }, "No wallets available"),
83
+ react_1.default.createElement(react_native_1.Text, { style: styles.emptyStateSubtext }, "Please install a TON wallet app to continue"))) : (wallets.map((wallet) => {
84
+ const isConnecting = connectingWallet === wallet.name;
85
+ return (react_1.default.createElement(react_native_1.TouchableOpacity, { key: wallet.name, style: [styles.walletItem, isConnecting && styles.walletItemConnecting], onPress: () => handleSelectWallet(wallet), disabled: isConnecting },
86
+ react_1.default.createElement(react_native_1.View, { style: styles.walletIconContainer },
87
+ react_1.default.createElement(react_native_1.View, { style: styles.walletIconPlaceholder },
88
+ react_1.default.createElement(react_native_1.Text, { style: styles.walletIconText }, wallet.name.charAt(0).toUpperCase()))),
89
+ react_1.default.createElement(react_native_1.View, { style: styles.walletInfo },
90
+ react_1.default.createElement(react_native_1.Text, { style: styles.walletName }, wallet.name),
91
+ react_1.default.createElement(react_native_1.Text, { style: styles.walletAppName }, wallet.appName)),
92
+ isConnecting && (react_1.default.createElement(react_native_1.View, { style: styles.connectingIndicator },
93
+ react_1.default.createElement(react_native_1.Text, { style: styles.connectingText }, "Connecting...")))));
94
+ }))),
95
+ react_1.default.createElement(react_native_1.View, { style: styles.footer },
96
+ react_1.default.createElement(react_native_1.Text, { style: styles.footerText }, "By connecting, you agree to the app's Terms of Service and Privacy Policy"))))));
97
+ }
98
+ const styles = react_native_1.StyleSheet.create({
99
+ overlay: {
100
+ flex: 1,
101
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
102
+ justifyContent: 'flex-end',
103
+ },
104
+ modalContainer: {
105
+ backgroundColor: '#1a1a1a',
106
+ borderTopLeftRadius: 24,
107
+ borderTopRightRadius: 24,
108
+ maxHeight: '85%',
109
+ paddingBottom: react_native_1.Platform.OS === 'ios' ? 34 : 20,
110
+ },
111
+ header: {
112
+ padding: 24,
113
+ borderBottomWidth: 1,
114
+ borderBottomColor: '#2a2a2a',
115
+ position: 'relative',
116
+ },
117
+ title: {
118
+ fontSize: 28,
119
+ fontWeight: 'bold',
120
+ color: '#ffffff',
121
+ marginBottom: 8,
122
+ },
123
+ subtitle: {
124
+ fontSize: 15,
125
+ color: '#999999',
126
+ lineHeight: 20,
127
+ },
128
+ closeButton: {
129
+ position: 'absolute',
130
+ top: 24,
131
+ right: 24,
132
+ width: 32,
133
+ height: 32,
134
+ borderRadius: 16,
135
+ backgroundColor: '#2a2a2a',
136
+ justifyContent: 'center',
137
+ alignItems: 'center',
138
+ },
139
+ closeButtonText: {
140
+ color: '#ffffff',
141
+ fontSize: 18,
142
+ fontWeight: '600',
143
+ },
144
+ walletList: {
145
+ padding: 16,
146
+ maxHeight: 400,
147
+ },
148
+ walletItem: {
149
+ flexDirection: 'row',
150
+ alignItems: 'center',
151
+ padding: 16,
152
+ backgroundColor: '#2a2a2a',
153
+ borderRadius: 16,
154
+ marginBottom: 12,
155
+ minHeight: 72,
156
+ },
157
+ walletItemConnecting: {
158
+ opacity: 0.7,
159
+ },
160
+ walletIconContainer: {
161
+ marginRight: 16,
162
+ },
163
+ walletIcon: {
164
+ width: 48,
165
+ height: 48,
166
+ borderRadius: 12,
167
+ },
168
+ walletIconPlaceholder: {
169
+ width: 48,
170
+ height: 48,
171
+ borderRadius: 12,
172
+ backgroundColor: '#3a3a3a',
173
+ justifyContent: 'center',
174
+ alignItems: 'center',
175
+ },
176
+ walletIconText: {
177
+ color: '#ffffff',
178
+ fontSize: 20,
179
+ fontWeight: 'bold',
180
+ },
181
+ walletInfo: {
182
+ flex: 1,
183
+ },
184
+ walletName: {
185
+ fontSize: 17,
186
+ fontWeight: '600',
187
+ color: '#ffffff',
188
+ marginBottom: 4,
189
+ },
190
+ walletAppName: {
191
+ fontSize: 14,
192
+ color: '#999999',
193
+ },
194
+ connectingIndicator: {
195
+ marginLeft: 12,
196
+ },
197
+ connectingText: {
198
+ fontSize: 14,
199
+ color: '#0088cc',
200
+ fontWeight: '500',
201
+ },
202
+ emptyState: {
203
+ padding: 40,
204
+ alignItems: 'center',
205
+ },
206
+ emptyStateText: {
207
+ fontSize: 18,
208
+ fontWeight: '600',
209
+ color: '#ffffff',
210
+ marginBottom: 8,
211
+ },
212
+ emptyStateSubtext: {
213
+ fontSize: 14,
214
+ color: '#999999',
215
+ textAlign: 'center',
216
+ },
217
+ footer: {
218
+ padding: 16,
219
+ borderTopWidth: 1,
220
+ borderTopColor: '#2a2a2a',
221
+ },
222
+ footerText: {
223
+ fontSize: 12,
224
+ color: '#666666',
225
+ textAlign: 'center',
226
+ lineHeight: 16,
227
+ },
228
+ });
@@ -6,3 +6,5 @@ export { TonConnectUIProvider, useTonConnectUI, useTonWallet, useTonConnectModal
6
6
  export type { TonConnectUIProviderProps, TonConnectUI, WalletState, Account, TransactionResponse, SignDataRequest, SignDataResponse, } from './TonConnectUIProvider';
7
7
  export { TonConnectButton } from './TonConnectButton';
8
8
  export type { TonConnectButtonProps } from './TonConnectButton';
9
+ export { WalletSelectionModal } from './WalletSelectionModal';
10
+ export type { WalletSelectionModalProps } from './WalletSelectionModal';
@@ -4,7 +4,7 @@
4
4
  * Re-export all React components and hooks
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.TonConnectButton = exports.useTonConnectSDK = exports.useTonConnectModal = exports.useTonWallet = exports.useTonConnectUI = exports.TonConnectUIProvider = void 0;
7
+ exports.WalletSelectionModal = exports.TonConnectButton = exports.useTonConnectSDK = exports.useTonConnectModal = exports.useTonWallet = exports.useTonConnectUI = exports.TonConnectUIProvider = void 0;
8
8
  var TonConnectUIProvider_1 = require("./TonConnectUIProvider");
9
9
  Object.defineProperty(exports, "TonConnectUIProvider", { enumerable: true, get: function () { return TonConnectUIProvider_1.TonConnectUIProvider; } });
10
10
  Object.defineProperty(exports, "useTonConnectUI", { enumerable: true, get: function () { return TonConnectUIProvider_1.useTonConnectUI; } });
@@ -13,3 +13,5 @@ Object.defineProperty(exports, "useTonConnectModal", { enumerable: true, get: fu
13
13
  Object.defineProperty(exports, "useTonConnectSDK", { enumerable: true, get: function () { return TonConnectUIProvider_1.useTonConnectSDK; } });
14
14
  var TonConnectButton_1 = require("./TonConnectButton");
15
15
  Object.defineProperty(exports, "TonConnectButton", { enumerable: true, get: function () { return TonConnectButton_1.TonConnectButton; } });
16
+ var WalletSelectionModal_1 = require("./WalletSelectionModal");
17
+ Object.defineProperty(exports, "WalletSelectionModal", { enumerable: true, get: function () { return WalletSelectionModal_1.WalletSelectionModal; } });
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Retry Utilities
3
+ * Helper functions for retrying operations with exponential backoff
4
+ */
5
+ export interface RetryOptions {
6
+ /** Maximum number of retry attempts (default: 3) */
7
+ maxAttempts?: number;
8
+ /** Initial delay in milliseconds (default: 1000) */
9
+ initialDelay?: number;
10
+ /** Maximum delay in milliseconds (default: 10000) */
11
+ maxDelay?: number;
12
+ /** Multiplier for exponential backoff (default: 2) */
13
+ multiplier?: number;
14
+ /** Function to determine if error should be retried (default: retry all errors) */
15
+ shouldRetry?: (error: Error) => boolean;
16
+ }
17
+ /**
18
+ * Retry a function with exponential backoff
19
+ * @param fn - Function to retry
20
+ * @param options - Retry options
21
+ * @returns Promise that resolves with the function result
22
+ */
23
+ export declare function retry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
24
+ /**
25
+ * Retry with custom delay function
26
+ * @param fn - Function to retry
27
+ * @param getDelay - Function that returns delay for each attempt (attempt number, last error)
28
+ * @param maxAttempts - Maximum number of attempts
29
+ * @returns Promise that resolves with the function result
30
+ */
31
+ export declare function retryWithCustomDelay<T>(fn: () => Promise<T>, getDelay: (attempt: number, error: Error | null) => number, maxAttempts?: number): Promise<T>;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ /**
3
+ * Retry Utilities
4
+ * Helper functions for retrying operations with exponential backoff
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.retry = retry;
8
+ exports.retryWithCustomDelay = retryWithCustomDelay;
9
+ /**
10
+ * Retry a function with exponential backoff
11
+ * @param fn - Function to retry
12
+ * @param options - Retry options
13
+ * @returns Promise that resolves with the function result
14
+ */
15
+ async function retry(fn, options = {}) {
16
+ const { maxAttempts = 3, initialDelay = 1000, maxDelay = 10000, multiplier = 2, shouldRetry = () => true, } = options;
17
+ let lastError = null;
18
+ let delay = initialDelay;
19
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
20
+ try {
21
+ return await fn();
22
+ }
23
+ catch (error) {
24
+ lastError = error instanceof Error ? error : new Error(String(error));
25
+ // Don't retry if shouldRetry returns false
26
+ if (!shouldRetry(lastError)) {
27
+ throw lastError;
28
+ }
29
+ // Don't retry on last attempt
30
+ if (attempt === maxAttempts) {
31
+ throw lastError;
32
+ }
33
+ // Wait before retrying
34
+ await new Promise((resolve) => setTimeout(() => resolve(), delay));
35
+ // Increase delay for next attempt (exponential backoff)
36
+ delay = Math.min(delay * multiplier, maxDelay);
37
+ }
38
+ }
39
+ // This should never be reached, but TypeScript needs it
40
+ throw lastError || new Error('Retry failed');
41
+ }
42
+ /**
43
+ * Retry with custom delay function
44
+ * @param fn - Function to retry
45
+ * @param getDelay - Function that returns delay for each attempt (attempt number, last error)
46
+ * @param maxAttempts - Maximum number of attempts
47
+ * @returns Promise that resolves with the function result
48
+ */
49
+ async function retryWithCustomDelay(fn, getDelay, maxAttempts = 3) {
50
+ let lastError = null;
51
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
52
+ try {
53
+ return await fn();
54
+ }
55
+ catch (error) {
56
+ lastError = error instanceof Error ? error : new Error(String(error));
57
+ // Don't retry on last attempt
58
+ if (attempt === maxAttempts) {
59
+ throw lastError;
60
+ }
61
+ // Wait before retrying
62
+ const delay = getDelay(attempt, lastError);
63
+ await new Promise((resolve) => setTimeout(() => resolve(), delay));
64
+ }
65
+ }
66
+ throw lastError || new Error('Retry failed');
67
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Transaction Builder Utilities
3
+ * Helper functions for building TON Connect transaction requests
4
+ */
5
+ import type { SendTransactionRequest } from '../types';
6
+ /**
7
+ * Convert TON amount to nanotons
8
+ * @param tonAmount - Amount in TON (e.g., 1.5 for 1.5 TON)
9
+ * @returns Amount in nanotons as string
10
+ */
11
+ export declare function tonToNano(tonAmount: number | string): string;
12
+ /**
13
+ * Convert nanotons to TON
14
+ * @param nanotons - Amount in nanotons as string
15
+ * @returns Amount in TON as number
16
+ */
17
+ export declare function nanoToTon(nanotons: string): number;
18
+ /**
19
+ * Build a simple TON transfer transaction
20
+ * @param to - Recipient address (EQ... format)
21
+ * @param amount - Amount in TON (will be converted to nanotons)
22
+ * @param validUntil - Optional expiration timestamp (default: 5 minutes from now)
23
+ * @returns Transaction request
24
+ */
25
+ export declare function buildTransferTransaction(to: string, amount: number | string, validUntil?: number): SendTransactionRequest;
26
+ /**
27
+ * Build a transaction with multiple recipients
28
+ * @param transfers - Array of {to, amount} transfers
29
+ * @param validUntil - Optional expiration timestamp (default: 5 minutes from now)
30
+ * @returns Transaction request
31
+ */
32
+ export declare function buildMultiTransferTransaction(transfers: Array<{
33
+ to: string;
34
+ amount: number | string;
35
+ }>, validUntil?: number): SendTransactionRequest;
36
+ /**
37
+ * Build a transaction with custom payload
38
+ * @param to - Recipient address
39
+ * @param amount - Amount in TON
40
+ * @param payload - Base64 encoded payload
41
+ * @param validUntil - Optional expiration timestamp
42
+ * @returns Transaction request
43
+ */
44
+ export declare function buildTransactionWithPayload(to: string, amount: number | string, payload: string, validUntil?: number): SendTransactionRequest;
45
+ /**
46
+ * Build a transaction with state init (for contract deployment)
47
+ * @param to - Recipient address
48
+ * @param amount - Amount in TON
49
+ * @param stateInit - Base64 encoded state init
50
+ * @param validUntil - Optional expiration timestamp
51
+ * @returns Transaction request
52
+ */
53
+ export declare function buildTransactionWithStateInit(to: string, amount: number | string, stateInit: string, validUntil?: number): SendTransactionRequest;
54
+ /**
55
+ * Validate TON address format
56
+ * @param address - Address to validate
57
+ * @returns true if address is valid
58
+ */
59
+ export declare function isValidTonAddress(address: string): boolean;
60
+ /**
61
+ * Format TON address for display (with ellipsis)
62
+ * @param address - Full address
63
+ * @param startLength - Characters to show at start (default: 6)
64
+ * @param endLength - Characters to show at end (default: 4)
65
+ * @returns Formatted address
66
+ */
67
+ export declare function formatTonAddress(address: string, startLength?: number, endLength?: number): string;
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ /**
3
+ * Transaction Builder Utilities
4
+ * Helper functions for building TON Connect transaction requests
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.tonToNano = tonToNano;
8
+ exports.nanoToTon = nanoToTon;
9
+ exports.buildTransferTransaction = buildTransferTransaction;
10
+ exports.buildMultiTransferTransaction = buildMultiTransferTransaction;
11
+ exports.buildTransactionWithPayload = buildTransactionWithPayload;
12
+ exports.buildTransactionWithStateInit = buildTransactionWithStateInit;
13
+ exports.isValidTonAddress = isValidTonAddress;
14
+ exports.formatTonAddress = formatTonAddress;
15
+ /**
16
+ * Convert TON amount to nanotons
17
+ * @param tonAmount - Amount in TON (e.g., 1.5 for 1.5 TON)
18
+ * @returns Amount in nanotons as string
19
+ */
20
+ function tonToNano(tonAmount) {
21
+ const amount = typeof tonAmount === 'string' ? parseFloat(tonAmount) : tonAmount;
22
+ if (isNaN(amount) || amount < 0) {
23
+ throw new Error('Invalid TON amount');
24
+ }
25
+ const nanotons = Math.floor(amount * 1000000000);
26
+ return nanotons.toString();
27
+ }
28
+ /**
29
+ * Convert nanotons to TON
30
+ * @param nanotons - Amount in nanotons as string
31
+ * @returns Amount in TON as number
32
+ */
33
+ function nanoToTon(nanotons) {
34
+ const nano = BigInt(nanotons);
35
+ return Number(nano) / 1000000000;
36
+ }
37
+ /**
38
+ * Build a simple TON transfer transaction
39
+ * @param to - Recipient address (EQ... format)
40
+ * @param amount - Amount in TON (will be converted to nanotons)
41
+ * @param validUntil - Optional expiration timestamp (default: 5 minutes from now)
42
+ * @returns Transaction request
43
+ */
44
+ function buildTransferTransaction(to, amount, validUntil) {
45
+ // Validate address
46
+ if (!to || typeof to !== 'string') {
47
+ throw new Error('Recipient address is required');
48
+ }
49
+ if (!isValidTonAddress(to)) {
50
+ throw new Error(`Invalid TON address format: ${to}`);
51
+ }
52
+ // Validate amount
53
+ const nanoAmount = tonToNano(amount);
54
+ if (BigInt(nanoAmount) <= 0n) {
55
+ throw new Error('Transaction amount must be greater than 0');
56
+ }
57
+ // Validate validUntil
58
+ const expiration = validUntil || Date.now() + 5 * 60 * 1000; // 5 minutes default
59
+ if (expiration <= Date.now()) {
60
+ throw new Error('Transaction expiration must be in the future');
61
+ }
62
+ return {
63
+ validUntil: expiration,
64
+ messages: [
65
+ {
66
+ address: to,
67
+ amount: nanoAmount,
68
+ },
69
+ ],
70
+ };
71
+ }
72
+ /**
73
+ * Build a transaction with multiple recipients
74
+ * @param transfers - Array of {to, amount} transfers
75
+ * @param validUntil - Optional expiration timestamp (default: 5 minutes from now)
76
+ * @returns Transaction request
77
+ */
78
+ function buildMultiTransferTransaction(transfers, validUntil) {
79
+ // Validate transfers array
80
+ if (!transfers || !Array.isArray(transfers) || transfers.length === 0) {
81
+ throw new Error('Transfers array is required and cannot be empty');
82
+ }
83
+ if (transfers.length > 255) {
84
+ throw new Error('Maximum 255 transfers allowed per transaction');
85
+ }
86
+ // Validate each transfer
87
+ const messages = transfers.map((transfer, index) => {
88
+ if (!transfer.to || typeof transfer.to !== 'string') {
89
+ throw new Error(`Transfer ${index + 1}: Recipient address is required`);
90
+ }
91
+ if (!isValidTonAddress(transfer.to)) {
92
+ throw new Error(`Transfer ${index + 1}: Invalid TON address format: ${transfer.to}`);
93
+ }
94
+ const nanoAmount = tonToNano(transfer.amount);
95
+ if (BigInt(nanoAmount) <= 0n) {
96
+ throw new Error(`Transfer ${index + 1}: Amount must be greater than 0`);
97
+ }
98
+ return {
99
+ address: transfer.to,
100
+ amount: nanoAmount,
101
+ };
102
+ });
103
+ // Validate validUntil
104
+ const expiration = validUntil || Date.now() + 5 * 60 * 1000;
105
+ if (expiration <= Date.now()) {
106
+ throw new Error('Transaction expiration must be in the future');
107
+ }
108
+ return {
109
+ validUntil: expiration,
110
+ messages,
111
+ };
112
+ }
113
+ /**
114
+ * Build a transaction with custom payload
115
+ * @param to - Recipient address
116
+ * @param amount - Amount in TON
117
+ * @param payload - Base64 encoded payload
118
+ * @param validUntil - Optional expiration timestamp
119
+ * @returns Transaction request
120
+ */
121
+ function buildTransactionWithPayload(to, amount, payload, validUntil) {
122
+ return {
123
+ validUntil: validUntil || Date.now() + 5 * 60 * 1000,
124
+ messages: [
125
+ {
126
+ address: to,
127
+ amount: tonToNano(amount),
128
+ payload,
129
+ },
130
+ ],
131
+ };
132
+ }
133
+ /**
134
+ * Build a transaction with state init (for contract deployment)
135
+ * @param to - Recipient address
136
+ * @param amount - Amount in TON
137
+ * @param stateInit - Base64 encoded state init
138
+ * @param validUntil - Optional expiration timestamp
139
+ * @returns Transaction request
140
+ */
141
+ function buildTransactionWithStateInit(to, amount, stateInit, validUntil) {
142
+ return {
143
+ validUntil: validUntil || Date.now() + 5 * 60 * 1000,
144
+ messages: [
145
+ {
146
+ address: to,
147
+ amount: tonToNano(amount),
148
+ stateInit,
149
+ },
150
+ ],
151
+ };
152
+ }
153
+ /**
154
+ * Validate TON address format
155
+ * @param address - Address to validate
156
+ * @returns true if address is valid
157
+ */
158
+ function isValidTonAddress(address) {
159
+ // TON addresses start with EQ or 0Q and are 48 characters long (base64)
160
+ return /^(EQ|0Q)[A-Za-z0-9_-]{46}$/.test(address);
161
+ }
162
+ /**
163
+ * Format TON address for display (with ellipsis)
164
+ * @param address - Full address
165
+ * @param startLength - Characters to show at start (default: 6)
166
+ * @param endLength - Characters to show at end (default: 4)
167
+ * @returns Formatted address
168
+ */
169
+ function formatTonAddress(address, startLength = 6, endLength = 4) {
170
+ if (address.length <= startLength + endLength) {
171
+ return address;
172
+ }
173
+ return `${address.substring(0, startLength)}...${address.substring(address.length - endLength)}`;
174
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blazium/ton-connect-mobile",
3
- "version": "1.1.5",
3
+ "version": "1.2.1",
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",
@@ -78,14 +78,14 @@ export class ExpoAdapter implements PlatformAdapter {
78
78
  console.log('[ExpoAdapter] Skipping canOpenURL check (Android compatibility)');
79
79
  }
80
80
 
81
- // CRITICAL FIX: Android'de canOpenURL() tonconnect:// protokolünü tanımayabilir
82
- // Bu yüzden direkt openURL() çağırıyoruz. Eğer açılamazsa hata fırlatır.
81
+ // CRITICAL FIX: On Android, canOpenURL() may not recognize tonconnect:// protocol
82
+ // So we call openURL() directly. If it fails, it will throw an error.
83
83
  await Linking.openURL(url);
84
84
  console.log('[ExpoAdapter] URL opened successfully');
85
85
  return true;
86
86
  } catch (error: any) {
87
87
  console.error('[ExpoAdapter] Error in openURL:', error);
88
- // Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
88
+ // On Android, if tonconnect:// protocol is not recognized or wallet is not installed, it will throw an error
89
89
  const errorMessage = error?.message || String(error);
90
90
  if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
91
91
  throw new Error(
@@ -67,14 +67,14 @@ export class ReactNativeAdapter implements PlatformAdapter {
67
67
  console.log('[ReactNativeAdapter] Skipping canOpenURL check (Android compatibility)');
68
68
  }
69
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.
70
+ // CRITICAL FIX: On Android, canOpenURL() may not recognize tonconnect:// protocol
71
+ // So we call openURL() directly. If it fails, it will throw an error.
72
72
  await Linking.openURL(url);
73
73
  console.log('[ReactNativeAdapter] URL opened successfully');
74
74
  return true;
75
75
  } catch (error: any) {
76
76
  console.error('[ReactNativeAdapter] Error in openURL:', error);
77
- // Android'de tonconnect:// protokolü tanınmıyorsa veya cüzdan yüklü değilse hata verir
77
+ // On Android, if tonconnect:// protocol is not recognized or wallet is not installed, it will throw an error
78
78
  const errorMessage = error?.message || String(error);
79
79
  if (errorMessage.includes('No Activity found') || errorMessage.includes('No app found') || errorMessage.includes('Cannot open URL')) {
80
80
  throw new Error(