@blazium/ton-connect-mobile 1.0.0

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,260 @@
1
+ "use strict";
2
+ /**
3
+ * Core TonConnect protocol implementation
4
+ * Pure TypeScript, no platform dependencies
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.encodeBase64URL = encodeBase64URL;
8
+ exports.decodeBase64URL = decodeBase64URL;
9
+ exports.buildConnectionRequest = buildConnectionRequest;
10
+ exports.buildTransactionRequest = buildTransactionRequest;
11
+ exports.parseCallbackURL = parseCallbackURL;
12
+ exports.extractWalletInfo = extractWalletInfo;
13
+ exports.validateConnectionResponse = validateConnectionResponse;
14
+ exports.validateTransactionResponse = validateTransactionResponse;
15
+ exports.validateTransactionRequest = validateTransactionRequest;
16
+ /**
17
+ * TonConnect protocol constants
18
+ */
19
+ const PROTOCOL_VERSION = '2';
20
+ const CONNECT_PREFIX = 'tonconnect://connect';
21
+ const SEND_TRANSACTION_PREFIX = 'tonconnect://send-transaction';
22
+ const CALLBACK_PREFIX = 'tonconnect';
23
+ /**
24
+ * Get TextEncoder with availability check
25
+ */
26
+ function getTextEncoder() {
27
+ if (typeof TextEncoder !== 'undefined') {
28
+ return new TextEncoder();
29
+ }
30
+ throw new Error('TextEncoder is not available. Please use React Native 0.59+ or add a polyfill.');
31
+ }
32
+ /**
33
+ * Encode string to base64
34
+ */
35
+ function base64Encode(str) {
36
+ // Use TextEncoder to convert string to bytes
37
+ const encoder = getTextEncoder();
38
+ const bytes = encoder.encode(str);
39
+ // Convert bytes to base64
40
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
41
+ let result = '';
42
+ let i = 0;
43
+ while (i < bytes.length) {
44
+ const a = bytes[i++];
45
+ const b = i < bytes.length ? bytes[i++] : 0;
46
+ const c = i < bytes.length ? bytes[i++] : 0;
47
+ const bitmap = (a << 16) | (b << 8) | c;
48
+ result += chars.charAt((bitmap >> 18) & 63);
49
+ result += chars.charAt((bitmap >> 12) & 63);
50
+ result += i - 2 < bytes.length ? chars.charAt((bitmap >> 6) & 63) : '=';
51
+ result += i - 1 < bytes.length ? chars.charAt(bitmap & 63) : '=';
52
+ }
53
+ return result;
54
+ }
55
+ /**
56
+ * Decode base64 to string
57
+ */
58
+ function base64Decode(base64) {
59
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
60
+ let buffer = 0;
61
+ let bitsCollected = 0;
62
+ let result = '';
63
+ for (let i = 0; i < base64.length; i++) {
64
+ const ch = base64[i];
65
+ if (ch === '=')
66
+ break;
67
+ const index = chars.indexOf(ch);
68
+ if (index === -1)
69
+ continue;
70
+ buffer = (buffer << 6) | index;
71
+ bitsCollected += 6;
72
+ if (bitsCollected >= 8) {
73
+ bitsCollected -= 8;
74
+ result += String.fromCharCode((buffer >> bitsCollected) & 0xff);
75
+ buffer &= (1 << bitsCollected) - 1;
76
+ }
77
+ }
78
+ return result;
79
+ }
80
+ /**
81
+ * Encode JSON to base64 URL-safe string
82
+ */
83
+ function encodeBase64URL(data) {
84
+ const json = JSON.stringify(data);
85
+ const base64 = base64Encode(json);
86
+ // Convert to URL-safe base64
87
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
88
+ }
89
+ /**
90
+ * Decode base64 URL-safe string to JSON
91
+ */
92
+ function decodeBase64URL(encoded) {
93
+ // Convert from URL-safe base64
94
+ const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
95
+ // Add padding if needed
96
+ const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
97
+ const json = base64Decode(padded);
98
+ return JSON.parse(json);
99
+ }
100
+ /**
101
+ * Build connection request URL
102
+ * Format: tonconnect://connect?<base64_encoded_payload>
103
+ */
104
+ function buildConnectionRequest(manifestUrl, returnScheme) {
105
+ const payload = {
106
+ manifestUrl,
107
+ items: [{ name: 'ton_addr' }],
108
+ returnStrategy: 'back',
109
+ };
110
+ const encoded = encodeBase64URL(payload);
111
+ return `${CONNECT_PREFIX}?${encoded}`;
112
+ }
113
+ /**
114
+ * Build transaction request URL
115
+ * Format: tonconnect://send-transaction?<base64_encoded_payload>
116
+ */
117
+ function buildTransactionRequest(manifestUrl, request, returnScheme) {
118
+ const payload = {
119
+ manifestUrl,
120
+ request: {
121
+ validUntil: request.validUntil,
122
+ messages: request.messages.map((msg) => ({
123
+ address: msg.address,
124
+ amount: msg.amount,
125
+ payload: msg.payload,
126
+ stateInit: msg.stateInit,
127
+ })),
128
+ network: request.network,
129
+ from: request.from,
130
+ },
131
+ returnStrategy: 'back',
132
+ };
133
+ const encoded = encodeBase64URL(payload);
134
+ return `${SEND_TRANSACTION_PREFIX}?${encoded}`;
135
+ }
136
+ /**
137
+ * Parse callback URL
138
+ * Format: <scheme>://tonconnect?<base64_encoded_response>
139
+ */
140
+ function parseCallbackURL(url, scheme) {
141
+ try {
142
+ // CRITICAL FIX: Validate URL input
143
+ if (!url || typeof url !== 'string') {
144
+ return { type: 'unknown', data: null };
145
+ }
146
+ // CRITICAL FIX: Validate URL length (prevent DoS)
147
+ if (url.length > 10000) {
148
+ return { type: 'unknown', data: null };
149
+ }
150
+ // CRITICAL FIX: Validate scheme format
151
+ if (!scheme || typeof scheme !== 'string' || scheme.length === 0 || scheme.length > 50) {
152
+ return { type: 'unknown', data: null };
153
+ }
154
+ // CRITICAL FIX: Exact scheme matching (case-sensitive)
155
+ const expectedPrefix = `${scheme}://${CALLBACK_PREFIX}?`;
156
+ if (!url.startsWith(expectedPrefix)) {
157
+ return { type: 'unknown', data: null };
158
+ }
159
+ // CRITICAL FIX: Validate URL structure - should be exactly scheme://tonconnect?<payload>
160
+ // Check that there's no additional path or query params
161
+ const urlAfterScheme = url.substring(scheme.length + 3); // After "scheme://"
162
+ if (!urlAfterScheme.startsWith(`${CALLBACK_PREFIX}?`)) {
163
+ return { type: 'unknown', data: null };
164
+ }
165
+ // Extract encoded payload
166
+ const encoded = url.substring(expectedPrefix.length);
167
+ // CRITICAL FIX: Validate base64 payload size (prevent DoS)
168
+ if (encoded.length === 0 || encoded.length > 5000) {
169
+ return { type: 'unknown', data: null };
170
+ }
171
+ // CRITICAL FIX: Validate base64 characters only
172
+ if (!/^[A-Za-z0-9_-]+$/.test(encoded)) {
173
+ return { type: 'unknown', data: null };
174
+ }
175
+ const decoded = decodeBase64URL(encoded);
176
+ // Validate decoded data is an object
177
+ if (!decoded || typeof decoded !== 'object' || Array.isArray(decoded)) {
178
+ return { type: 'unknown', data: null };
179
+ }
180
+ // Check if it's an error response
181
+ if ('error' in decoded && typeof decoded.error === 'object') {
182
+ const errorData = decoded;
183
+ if (errorData.error && typeof errorData.error.code === 'number' && typeof errorData.error.message === 'string') {
184
+ return { type: 'error', data: errorData };
185
+ }
186
+ }
187
+ // Check if it's a connection response (has session, address, publicKey)
188
+ if ('session' in decoded &&
189
+ 'address' in decoded &&
190
+ 'publicKey' in decoded &&
191
+ typeof decoded.session === 'string' &&
192
+ typeof decoded.address === 'string' &&
193
+ typeof decoded.publicKey === 'string') {
194
+ return { type: 'connect', data: decoded };
195
+ }
196
+ // Check if it's a transaction response (has boc, signature)
197
+ if ('boc' in decoded &&
198
+ 'signature' in decoded &&
199
+ typeof decoded.boc === 'string' &&
200
+ typeof decoded.signature === 'string') {
201
+ return { type: 'transaction', data: decoded };
202
+ }
203
+ return { type: 'unknown', data: null };
204
+ }
205
+ catch (error) {
206
+ // Log error for debugging but don't expose details
207
+ return { type: 'unknown', data: null };
208
+ }
209
+ }
210
+ /**
211
+ * Extract wallet info from connection response
212
+ */
213
+ function extractWalletInfo(response) {
214
+ return {
215
+ name: response.name,
216
+ appName: response.appName,
217
+ version: response.version,
218
+ platform: response.platform || 'unknown',
219
+ address: response.address,
220
+ publicKey: response.publicKey,
221
+ icon: response.icon,
222
+ };
223
+ }
224
+ /**
225
+ * Validate connection response
226
+ */
227
+ function validateConnectionResponse(response) {
228
+ return !!(response.session &&
229
+ response.address &&
230
+ response.publicKey &&
231
+ response.name &&
232
+ response.appName &&
233
+ response.version);
234
+ }
235
+ /**
236
+ * Validate transaction response
237
+ */
238
+ function validateTransactionResponse(response) {
239
+ return !!(response.boc && response.signature);
240
+ }
241
+ /**
242
+ * Validate transaction request
243
+ */
244
+ function validateTransactionRequest(request) {
245
+ if (!request.validUntil || request.validUntil <= Date.now()) {
246
+ return { valid: false, error: 'Transaction request has expired' };
247
+ }
248
+ if (!request.messages || request.messages.length === 0) {
249
+ return { valid: false, error: 'Transaction must have at least one message' };
250
+ }
251
+ for (const msg of request.messages) {
252
+ if (!msg.address) {
253
+ return { valid: false, error: 'Message address is required' };
254
+ }
255
+ if (!msg.amount || isNaN(Number(msg.amount))) {
256
+ return { valid: false, error: 'Message amount must be a valid number' };
257
+ }
258
+ }
259
+ return { valid: true };
260
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * TON Connect Mobile SDK
3
+ * Production-ready implementation for React Native and Expo
4
+ */
5
+ import { TonConnectMobileConfig, ConnectionStatus, WalletInfo, SendTransactionRequest, StatusChangeCallback } from './types';
6
+ /**
7
+ * Custom error classes
8
+ */
9
+ export declare class TonConnectError extends Error {
10
+ code?: string | undefined;
11
+ constructor(message: string, code?: string | undefined);
12
+ }
13
+ export declare class ConnectionTimeoutError extends TonConnectError {
14
+ constructor();
15
+ }
16
+ export declare class TransactionTimeoutError extends TonConnectError {
17
+ constructor();
18
+ }
19
+ export declare class UserRejectedError extends TonConnectError {
20
+ constructor();
21
+ }
22
+ export declare class ConnectionInProgressError extends TonConnectError {
23
+ constructor();
24
+ }
25
+ export declare class TransactionInProgressError extends TonConnectError {
26
+ constructor();
27
+ }
28
+ /**
29
+ * Main TON Connect Mobile SDK class
30
+ */
31
+ export declare class TonConnectMobile {
32
+ private adapter;
33
+ private config;
34
+ private statusChangeCallbacks;
35
+ private currentStatus;
36
+ private urlUnsubscribe;
37
+ private connectionPromise;
38
+ private transactionPromise;
39
+ constructor(config: TonConnectMobileConfig);
40
+ /**
41
+ * Create platform adapter based on available modules
42
+ */
43
+ private createAdapter;
44
+ /**
45
+ * Set up URL listener for wallet callbacks
46
+ */
47
+ private setupURLListener;
48
+ /**
49
+ * Handle callback from wallet
50
+ */
51
+ private handleCallback;
52
+ /**
53
+ * Handle connection response from wallet
54
+ */
55
+ private handleConnectionResponse;
56
+ /**
57
+ * Handle transaction response from wallet
58
+ */
59
+ private handleTransactionResponse;
60
+ /**
61
+ * Reject current promise with error
62
+ */
63
+ private rejectWithError;
64
+ /**
65
+ * Connect to wallet
66
+ */
67
+ connect(): Promise<WalletInfo>;
68
+ /**
69
+ * Send transaction
70
+ */
71
+ sendTransaction(request: SendTransactionRequest): Promise<{
72
+ boc: string;
73
+ signature: string;
74
+ }>;
75
+ /**
76
+ * Disconnect from wallet
77
+ */
78
+ disconnect(): Promise<void>;
79
+ /**
80
+ * Get current connection status
81
+ */
82
+ getStatus(): ConnectionStatus;
83
+ /**
84
+ * Subscribe to status changes
85
+ */
86
+ onStatusChange(callback: StatusChangeCallback): () => void;
87
+ /**
88
+ * Notify all status change callbacks
89
+ */
90
+ private notifyStatusChange;
91
+ /**
92
+ * Validate session ID format
93
+ */
94
+ private validateSessionId;
95
+ /**
96
+ * Save session to storage
97
+ */
98
+ private saveSession;
99
+ /**
100
+ * Load session from storage
101
+ */
102
+ private loadSession;
103
+ /**
104
+ * Clear session from storage
105
+ */
106
+ private clearSession;
107
+ /**
108
+ * Cleanup resources
109
+ */
110
+ destroy(): void;
111
+ }
112
+ export * from './types';