@blazium/ton-connect-mobile 1.2.4 → 1.2.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.
@@ -1,21 +1,24 @@
1
1
  "use strict";
2
2
  /**
3
- * Core protocol exports
3
+ * Core module exports
4
4
  */
5
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- var desc = Object.getOwnPropertyDescriptor(m, k);
8
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
- desc = { enumerable: true, get: function() { return m[k]; } };
10
- }
11
- Object.defineProperty(o, k2, desc);
12
- }) : (function(o, m, k, k2) {
13
- if (k2 === undefined) k2 = k;
14
- o[k2] = m[k];
15
- }));
16
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
- };
19
5
  Object.defineProperty(exports, "__esModule", { value: true });
20
- __exportStar(require("./protocol"), exports);
21
- __exportStar(require("./crypto"), exports);
6
+ exports.getWalletsForPlatform = exports.getDefaultWallet = exports.getWalletByName = exports.SUPPORTED_WALLETS = exports.validateTransactionRequest = exports.extractWalletInfoFromEvent = exports.parseRpcResponse = exports.parseConnectResponse = exports.buildDisconnectRpcRequest = exports.buildSendTransactionRpcRequest = exports.buildReturnUniversalLink = exports.buildConnectUniversalLink = exports.BridgeGateway = exports.SessionCrypto = void 0;
7
+ var session_1 = require("./session");
8
+ Object.defineProperty(exports, "SessionCrypto", { enumerable: true, get: function () { return session_1.SessionCrypto; } });
9
+ var bridge_1 = require("./bridge");
10
+ Object.defineProperty(exports, "BridgeGateway", { enumerable: true, get: function () { return bridge_1.BridgeGateway; } });
11
+ var protocol_1 = require("./protocol");
12
+ Object.defineProperty(exports, "buildConnectUniversalLink", { enumerable: true, get: function () { return protocol_1.buildConnectUniversalLink; } });
13
+ Object.defineProperty(exports, "buildReturnUniversalLink", { enumerable: true, get: function () { return protocol_1.buildReturnUniversalLink; } });
14
+ Object.defineProperty(exports, "buildSendTransactionRpcRequest", { enumerable: true, get: function () { return protocol_1.buildSendTransactionRpcRequest; } });
15
+ Object.defineProperty(exports, "buildDisconnectRpcRequest", { enumerable: true, get: function () { return protocol_1.buildDisconnectRpcRequest; } });
16
+ Object.defineProperty(exports, "parseConnectResponse", { enumerable: true, get: function () { return protocol_1.parseConnectResponse; } });
17
+ Object.defineProperty(exports, "parseRpcResponse", { enumerable: true, get: function () { return protocol_1.parseRpcResponse; } });
18
+ Object.defineProperty(exports, "extractWalletInfoFromEvent", { enumerable: true, get: function () { return protocol_1.extractWalletInfoFromEvent; } });
19
+ Object.defineProperty(exports, "validateTransactionRequest", { enumerable: true, get: function () { return protocol_1.validateTransactionRequest; } });
20
+ var wallets_1 = require("./wallets");
21
+ Object.defineProperty(exports, "SUPPORTED_WALLETS", { enumerable: true, get: function () { return wallets_1.SUPPORTED_WALLETS; } });
22
+ Object.defineProperty(exports, "getWalletByName", { enumerable: true, get: function () { return wallets_1.getWalletByName; } });
23
+ Object.defineProperty(exports, "getDefaultWallet", { enumerable: true, get: function () { return wallets_1.getDefaultWallet; } });
24
+ Object.defineProperty(exports, "getWalletsForPlatform", { enumerable: true, get: function () { return wallets_1.getWalletsForPlatform; } });
@@ -1,51 +1,54 @@
1
1
  /**
2
- * Core TonConnect protocol implementation
3
- * Pure TypeScript, no platform dependencies
2
+ * TON Connect v2 Protocol Implementation
3
+ * Builds correct universal links and parses bridge responses
4
4
  */
5
- import { ConnectionResponsePayload, TransactionResponsePayload, ErrorResponse, WalletInfo, SendTransactionRequest } from '../types';
5
+ import type { SendTransactionRequest, WalletInfo, ConnectEvent, ConnectErrorEvent, RpcResponse, RpcErrorResponse } from '../types';
6
6
  /**
7
- * Encode JSON to base64 URL-safe string
7
+ * Build a TON Connect v2 universal link for wallet connection
8
+ * Format: {universalLink}?v=2&id={sessionId}&r={connectRequest}&ret={returnStrategy}
8
9
  */
9
- export declare function encodeBase64URL(data: unknown): string;
10
+ export declare function buildConnectUniversalLink(universalLink: string, sessionId: string, manifestUrl: string, returnStrategy?: string): string;
10
11
  /**
11
- * Decode base64 URL-safe string to JSON
12
+ * Build a universal link to bring wallet to foreground (for pending transactions)
13
+ * Format: {universalLink}?ret={returnStrategy}
12
14
  */
13
- export declare function decodeBase64URL<T>(encoded: string): T;
15
+ export declare function buildReturnUniversalLink(universalLink: string, returnStrategy?: string): string;
14
16
  /**
15
- * Build connection request URL
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
+ * Build a JSON-RPC request for sendTransaction
19
18
  */
20
- export declare function buildConnectionRequest(manifestUrl: string, returnScheme: string, walletUniversalLink?: string, returnStrategy?: 'back' | 'post_redirect' | 'none', requiresReturnScheme?: boolean): string;
19
+ export declare function buildSendTransactionRpcRequest(request: SendTransactionRequest, id: number): string;
21
20
  /**
22
- * Build transaction request URL
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
21
+ * Build a JSON-RPC request for disconnect
26
22
  */
27
- export declare function buildTransactionRequest(manifestUrl: string, request: SendTransactionRequest, returnScheme: string, walletUniversalLink?: string, returnStrategy?: 'back' | 'post_redirect' | 'none', requiresReturnScheme?: boolean): string;
23
+ export declare function buildDisconnectRpcRequest(id: number): string;
28
24
  /**
29
- * Parse callback URL
30
- * Format: <scheme>://tonconnect?<base64_encoded_response>
25
+ * Parse a connect response from the wallet (received via bridge, after decryption)
31
26
  */
32
- export declare function parseCallbackURL(url: string, scheme: string): {
33
- type: 'connect' | 'transaction' | 'error' | 'unknown';
34
- data: ConnectionResponsePayload | TransactionResponsePayload | ErrorResponse | null;
35
- };
36
- /**
37
- * Extract wallet info from connection response
38
- * CRITICAL: This function assumes response has been validated by validateConnectionResponse
39
- */
40
- export declare function extractWalletInfo(response: ConnectionResponsePayload): WalletInfo;
27
+ export declare function parseConnectResponse(decrypted: string): {
28
+ type: 'connect';
29
+ data: ConnectEvent;
30
+ } | {
31
+ type: 'error';
32
+ data: ConnectErrorEvent;
33
+ } | null;
41
34
  /**
42
- * Validate connection response
35
+ * Parse an RPC response from the wallet (for sendTransaction, disconnect, etc.)
43
36
  */
44
- export declare function validateConnectionResponse(response: ConnectionResponsePayload): boolean;
37
+ export declare function parseRpcResponse(decrypted: string): {
38
+ type: 'result';
39
+ data: RpcResponse;
40
+ } | {
41
+ type: 'error';
42
+ data: RpcErrorResponse;
43
+ } | {
44
+ type: 'event';
45
+ event: string;
46
+ data: any;
47
+ } | null;
45
48
  /**
46
- * Validate transaction response
49
+ * Extract wallet info from a connect event
47
50
  */
48
- export declare function validateTransactionResponse(response: TransactionResponsePayload): boolean;
51
+ export declare function extractWalletInfoFromEvent(event: ConnectEvent): WalletInfo;
49
52
  /**
50
53
  * Validate transaction request
51
54
  */
@@ -1,289 +1,146 @@
1
1
  "use strict";
2
2
  /**
3
- * Core TonConnect protocol implementation
4
- * Pure TypeScript, no platform dependencies
3
+ * TON Connect v2 Protocol Implementation
4
+ * Builds correct universal links and parses bridge responses
5
5
  */
6
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;
7
+ exports.buildConnectUniversalLink = buildConnectUniversalLink;
8
+ exports.buildReturnUniversalLink = buildReturnUniversalLink;
9
+ exports.buildSendTransactionRpcRequest = buildSendTransactionRpcRequest;
10
+ exports.buildDisconnectRpcRequest = buildDisconnectRpcRequest;
11
+ exports.parseConnectResponse = parseConnectResponse;
12
+ exports.parseRpcResponse = parseRpcResponse;
13
+ exports.extractWalletInfoFromEvent = extractWalletInfoFromEvent;
15
14
  exports.validateTransactionRequest = validateTransactionRequest;
16
15
  /**
17
- * TonConnect protocol constants
16
+ * Protocol version
18
17
  */
19
18
  const PROTOCOL_VERSION = '2';
20
- const CONNECT_PREFIX = 'tonconnect://connect';
21
- const CONNECT_UNIVERSAL_PREFIX = 'https://app.tonkeeper.com/ton-connect';
22
- const SEND_TRANSACTION_PREFIX = 'tonconnect://send-transaction';
23
- const SEND_TRANSACTION_UNIVERSAL_PREFIX = 'https://app.tonkeeper.com/ton-connect/send-transaction';
24
- const CALLBACK_PREFIX = 'tonconnect';
25
19
  /**
26
- * Get TextEncoder with availability check
20
+ * Build a TON Connect v2 universal link for wallet connection
21
+ * Format: {universalLink}?v=2&id={sessionId}&r={connectRequest}&ret={returnStrategy}
27
22
  */
28
- function getTextEncoder() {
29
- if (typeof TextEncoder !== 'undefined') {
30
- return new TextEncoder();
31
- }
32
- throw new Error('TextEncoder is not available. Please use React Native 0.59+ or add a polyfill.');
33
- }
34
- /**
35
- * Encode string to base64
36
- */
37
- function base64Encode(str) {
38
- // Use TextEncoder to convert string to bytes
39
- const encoder = getTextEncoder();
40
- const bytes = encoder.encode(str);
41
- // Convert bytes to base64
42
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
43
- let result = '';
44
- let i = 0;
45
- while (i < bytes.length) {
46
- const a = bytes[i++];
47
- const b = i < bytes.length ? bytes[i++] : 0;
48
- const c = i < bytes.length ? bytes[i++] : 0;
49
- const bitmap = (a << 16) | (b << 8) | c;
50
- result += chars.charAt((bitmap >> 18) & 63);
51
- result += chars.charAt((bitmap >> 12) & 63);
52
- result += i - 2 < bytes.length ? chars.charAt((bitmap >> 6) & 63) : '=';
53
- result += i - 1 < bytes.length ? chars.charAt(bitmap & 63) : '=';
54
- }
55
- return result;
23
+ function buildConnectUniversalLink(universalLink, sessionId, manifestUrl, returnStrategy = 'back') {
24
+ // Build connect request (TON Connect v2 format)
25
+ const connectRequest = {
26
+ manifestUrl,
27
+ items: [{ name: 'ton_addr' }],
28
+ };
29
+ // Build URL with proper query parameters
30
+ const r = JSON.stringify(connectRequest);
31
+ const params = [
32
+ `v=${PROTOCOL_VERSION}`,
33
+ `id=${sessionId}`,
34
+ `r=${encodeURIComponent(r)}`,
35
+ `ret=${encodeURIComponent(returnStrategy)}`,
36
+ ];
37
+ // Handle wallet universal links that may already have query params
38
+ const separator = universalLink.includes('?') ? '&' : '?';
39
+ return `${universalLink}${separator}${params.join('&')}`;
56
40
  }
57
41
  /**
58
- * Decode base64 to string
42
+ * Build a universal link to bring wallet to foreground (for pending transactions)
43
+ * Format: {universalLink}?ret={returnStrategy}
59
44
  */
60
- function base64Decode(base64) {
61
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
62
- let buffer = 0;
63
- let bitsCollected = 0;
64
- let result = '';
65
- for (let i = 0; i < base64.length; i++) {
66
- const ch = base64[i];
67
- if (ch === '=')
68
- break;
69
- const index = chars.indexOf(ch);
70
- if (index === -1)
71
- continue;
72
- buffer = (buffer << 6) | index;
73
- bitsCollected += 6;
74
- if (bitsCollected >= 8) {
75
- bitsCollected -= 8;
76
- result += String.fromCharCode((buffer >> bitsCollected) & 0xff);
77
- buffer &= (1 << bitsCollected) - 1;
78
- }
79
- }
80
- return result;
45
+ function buildReturnUniversalLink(universalLink, returnStrategy = 'back') {
46
+ const separator = universalLink.includes('?') ? '&' : '?';
47
+ return `${universalLink}${separator}ret=${encodeURIComponent(returnStrategy)}`;
81
48
  }
82
49
  /**
83
- * Encode JSON to base64 URL-safe string
50
+ * Build a JSON-RPC request for sendTransaction
84
51
  */
85
- function encodeBase64URL(data) {
86
- const json = JSON.stringify(data);
87
- const base64 = base64Encode(json);
88
- // Convert to URL-safe base64
89
- return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
52
+ function buildSendTransactionRpcRequest(request, id) {
53
+ // TON Connect v2 sendTransaction format
54
+ const params = JSON.stringify({
55
+ valid_until: Math.floor(request.validUntil / 1000), // Convert ms to seconds
56
+ network: request.network === 'testnet' ? '-3' : '-239',
57
+ from: request.from,
58
+ messages: request.messages.map((msg) => ({
59
+ address: msg.address,
60
+ amount: msg.amount,
61
+ payload: msg.payload,
62
+ stateInit: msg.stateInit,
63
+ })),
64
+ });
65
+ return JSON.stringify({
66
+ method: 'sendTransaction',
67
+ params: [params],
68
+ id,
69
+ });
90
70
  }
91
71
  /**
92
- * Decode base64 URL-safe string to JSON
72
+ * Build a JSON-RPC request for disconnect
93
73
  */
94
- function decodeBase64URL(encoded) {
95
- // Convert from URL-safe base64
96
- const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
97
- // Add padding if needed
98
- const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
99
- const json = base64Decode(padded);
100
- return JSON.parse(json);
74
+ function buildDisconnectRpcRequest(id) {
75
+ return JSON.stringify({
76
+ method: 'disconnect',
77
+ params: [],
78
+ id,
79
+ });
101
80
  }
102
81
  /**
103
- * Build connection request URL
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
82
+ * Parse a connect response from the wallet (received via bridge, after decryption)
107
83
  */
108
- function buildConnectionRequest(manifestUrl, returnScheme, walletUniversalLink, returnStrategy, requiresReturnScheme) {
109
- // Build payload with required fields
110
- const payload = {
111
- manifestUrl,
112
- items: [{ name: 'ton_addr' }],
113
- returnStrategy: returnStrategy || 'back',
114
- };
115
- // CRITICAL FIX: Many wallets (Tonhub, MyTonWallet, Telegram Wallet) require returnScheme
116
- // in the payload to properly handle mobile app callbacks. While not in the official
117
- // protocol spec, it's a de-facto requirement for mobile apps.
118
- if (requiresReturnScheme !== false) {
119
- // Default to true if not specified - safer to include it
120
- payload.returnScheme = returnScheme;
121
- }
122
- const encoded = encodeBase64URL(payload);
123
- // Use custom wallet universal link if provided
124
- if (walletUniversalLink) {
125
- return `${walletUniversalLink}?${encoded}`;
126
- }
127
- // Default to Tonkeeper universal link for Android compatibility
128
- return `${CONNECT_UNIVERSAL_PREFIX}?${encoded}`;
129
- }
130
- /**
131
- * Build transaction request URL
132
- * Format: tonconnect://send-transaction?<base64_encoded_payload>
133
- * Or universal link: https://app.tonkeeper.com/ton-connect/send-transaction?<base64_encoded_payload>
134
- * Or custom wallet universal link
135
- */
136
- function buildTransactionRequest(manifestUrl, request, returnScheme, walletUniversalLink, returnStrategy, requiresReturnScheme) {
137
- const payload = {
138
- manifestUrl,
139
- request: {
140
- validUntil: request.validUntil,
141
- messages: request.messages.map((msg) => ({
142
- address: msg.address,
143
- amount: msg.amount,
144
- payload: msg.payload,
145
- stateInit: msg.stateInit,
146
- })),
147
- network: request.network,
148
- from: request.from,
149
- },
150
- returnStrategy: returnStrategy || 'back',
151
- };
152
- // CRITICAL FIX: Include returnScheme for mobile wallets that require it
153
- if (requiresReturnScheme !== false) {
154
- payload.returnScheme = returnScheme;
84
+ function parseConnectResponse(decrypted) {
85
+ try {
86
+ const parsed = JSON.parse(decrypted);
87
+ if (parsed.event === 'connect' && parsed.payload) {
88
+ return { type: 'connect', data: parsed };
89
+ }
90
+ if (parsed.event === 'connect_error' && parsed.payload) {
91
+ return { type: 'error', data: parsed };
92
+ }
93
+ return null;
155
94
  }
156
- const encoded = encodeBase64URL(payload);
157
- // Use custom wallet universal link if provided
158
- if (walletUniversalLink) {
159
- // For transaction, append /send-transaction to the base universal link
160
- const baseUrl = walletUniversalLink.endsWith('/ton-connect')
161
- ? walletUniversalLink
162
- : `${walletUniversalLink}/ton-connect`;
163
- return `${baseUrl}/send-transaction?${encoded}`;
95
+ catch {
96
+ return null;
164
97
  }
165
- // Default to Tonkeeper universal link for Android compatibility
166
- return `${SEND_TRANSACTION_UNIVERSAL_PREFIX}?${encoded}`;
167
98
  }
168
99
  /**
169
- * Parse callback URL
170
- * Format: <scheme>://tonconnect?<base64_encoded_response>
100
+ * Parse an RPC response from the wallet (for sendTransaction, disconnect, etc.)
171
101
  */
172
- function parseCallbackURL(url, scheme) {
102
+ function parseRpcResponse(decrypted) {
173
103
  try {
174
- // CRITICAL FIX: Validate URL input
175
- if (!url || typeof url !== 'string') {
176
- return { type: 'unknown', data: null };
177
- }
178
- // CRITICAL FIX: Validate URL length (prevent DoS)
179
- if (url.length > 10000) {
180
- return { type: 'unknown', data: null };
181
- }
182
- // CRITICAL FIX: Validate scheme format
183
- if (!scheme || typeof scheme !== 'string' || scheme.length === 0 || scheme.length > 50) {
184
- return { type: 'unknown', data: null };
104
+ const parsed = JSON.parse(decrypted);
105
+ // Check for events (disconnect, etc.)
106
+ if (parsed.event) {
107
+ return { type: 'event', event: parsed.event, data: parsed.payload || null };
185
108
  }
186
- // CRITICAL FIX: Exact scheme matching (case-sensitive)
187
- const expectedPrefix = `${scheme}://${CALLBACK_PREFIX}?`;
188
- if (!url.startsWith(expectedPrefix)) {
189
- return { type: 'unknown', data: null };
109
+ // Check for RPC result
110
+ if ('result' in parsed && parsed.id !== undefined) {
111
+ return { type: 'result', data: parsed };
190
112
  }
191
- // CRITICAL FIX: Validate URL structure - should be exactly scheme://tonconnect?<payload>
192
- // Check that there's no additional path or query params
193
- const urlAfterScheme = url.substring(scheme.length + 3); // After "scheme://"
194
- if (!urlAfterScheme.startsWith(`${CALLBACK_PREFIX}?`)) {
195
- return { type: 'unknown', data: null };
113
+ // Check for RPC error
114
+ if ('error' in parsed && parsed.id !== undefined) {
115
+ return { type: 'error', data: parsed };
196
116
  }
197
- // Extract encoded payload
198
- let encoded = url.substring(expectedPrefix.length);
199
- // CRITICAL FIX: Decode URL encoding first (wallet may URL-encode the payload)
200
- try {
201
- encoded = decodeURIComponent(encoded);
202
- }
203
- catch (error) {
204
- // If decodeURIComponent fails, try using the original encoded string
205
- // Some wallets may not URL-encode the payload
206
- console.log('[TON Connect] Payload not URL-encoded, using as-is');
207
- }
208
- // CRITICAL FIX: Validate base64 payload size (prevent DoS)
209
- if (encoded.length === 0 || encoded.length > 5000) {
210
- return { type: 'unknown', data: null };
211
- }
212
- // CRITICAL FIX: Validate base64 characters only (after URL decoding)
213
- if (!/^[A-Za-z0-9_-]+$/.test(encoded)) {
214
- return { type: 'unknown', data: null };
215
- }
216
- const decoded = decodeBase64URL(encoded);
217
- // Validate decoded data is an object
218
- if (!decoded || typeof decoded !== 'object' || Array.isArray(decoded)) {
219
- return { type: 'unknown', data: null };
220
- }
221
- // Check if it's an error response
222
- if ('error' in decoded && typeof decoded.error === 'object') {
223
- const errorData = decoded;
224
- if (errorData.error && typeof errorData.error.code === 'number' && typeof errorData.error.message === 'string') {
225
- return { type: 'error', data: errorData };
226
- }
227
- }
228
- // Check if it's a connection response (has session, address, publicKey)
229
- if ('session' in decoded &&
230
- 'address' in decoded &&
231
- 'publicKey' in decoded &&
232
- typeof decoded.session === 'string' &&
233
- typeof decoded.address === 'string' &&
234
- typeof decoded.publicKey === 'string') {
235
- return { type: 'connect', data: decoded };
236
- }
237
- // Check if it's a transaction response (has boc, signature)
238
- if ('boc' in decoded &&
239
- 'signature' in decoded &&
240
- typeof decoded.boc === 'string' &&
241
- typeof decoded.signature === 'string') {
242
- return { type: 'transaction', data: decoded };
243
- }
244
- return { type: 'unknown', data: null };
117
+ return null;
245
118
  }
246
- catch (error) {
247
- // Log error for debugging but don't expose details
248
- return { type: 'unknown', data: null };
119
+ catch {
120
+ return null;
249
121
  }
250
122
  }
251
123
  /**
252
- * Extract wallet info from connection response
253
- * CRITICAL: This function assumes response has been validated by validateConnectionResponse
124
+ * Extract wallet info from a connect event
254
125
  */
255
- function extractWalletInfo(response) {
256
- // CRITICAL FIX: Add null checks to prevent runtime errors
257
- if (!response || !response.name || !response.address || !response.publicKey) {
258
- throw new Error('Invalid connection response: missing required fields');
126
+ function extractWalletInfoFromEvent(event) {
127
+ const tonAddr = event.payload.items.find((item) => item.name === 'ton_addr');
128
+ if (!tonAddr) {
129
+ throw new Error('Connect response missing ton_addr item');
259
130
  }
131
+ const device = event.payload.device || {};
260
132
  return {
261
- name: response.name,
262
- appName: response.appName || response.name,
263
- version: response.version || 'unknown',
264
- platform: response.platform || 'unknown',
265
- address: response.address,
266
- publicKey: response.publicKey,
267
- icon: response.icon,
133
+ name: device.appName || 'Unknown Wallet',
134
+ appName: device.appName || 'unknown',
135
+ version: device.appVersion || 'unknown',
136
+ platform: device.platform || 'unknown',
137
+ address: tonAddr.address,
138
+ publicKey: tonAddr.publicKey,
139
+ network: tonAddr.network,
140
+ walletStateInit: tonAddr.walletStateInit,
141
+ icon: undefined,
268
142
  };
269
143
  }
270
- /**
271
- * Validate connection response
272
- */
273
- function validateConnectionResponse(response) {
274
- return !!(response.session &&
275
- response.address &&
276
- response.publicKey &&
277
- response.name &&
278
- response.appName &&
279
- response.version);
280
- }
281
- /**
282
- * Validate transaction response
283
- */
284
- function validateTransactionResponse(response) {
285
- return !!(response.boc && response.signature);
286
- }
287
144
  /**
288
145
  * Validate transaction request
289
146
  */
@@ -294,59 +151,26 @@ function validateTransactionRequest(request) {
294
151
  if (!request.messages || request.messages.length === 0) {
295
152
  return { valid: false, error: 'Transaction must have at least one message' };
296
153
  }
297
- // CRITICAL: Validate each message
298
154
  for (let i = 0; i < request.messages.length; i++) {
299
155
  const msg = request.messages[i];
300
- // Validate address
301
156
  if (!msg.address || typeof msg.address !== 'string') {
302
- return { valid: false, error: `Message ${i + 1}: Address is required and must be a string` };
303
- }
304
- // CRITICAL: Validate TON address format (EQ... or 0Q...)
305
- if (!/^(EQ|0Q)[A-Za-z0-9_-]{46}$/.test(msg.address)) {
306
- return { valid: false, error: `Message ${i + 1}: Invalid TON address format. Address must start with EQ or 0Q and be 48 characters long.` };
157
+ return { valid: false, error: `Message ${i + 1}: Address is required` };
307
158
  }
308
- // Validate amount
309
159
  if (!msg.amount || typeof msg.amount !== 'string') {
310
- return { valid: false, error: `Message ${i + 1}: Amount is required and must be a string (nanotons)` };
160
+ return { valid: false, error: `Message ${i + 1}: Amount is required (nanotons string)` };
311
161
  }
312
- // CRITICAL: Validate amount is a valid positive number (nanotons)
313
162
  try {
314
163
  const amount = BigInt(msg.amount);
315
164
  if (amount <= 0n) {
316
- return { valid: false, error: `Message ${i + 1}: Amount must be greater than 0` };
317
- }
318
- // Check for reasonable maximum (prevent overflow)
319
- if (amount > BigInt('1000000000000000000')) { // 1 billion TON
320
- return { valid: false, error: `Message ${i + 1}: Amount exceeds maximum allowed (1 billion TON)` };
165
+ return { valid: false, error: `Message ${i + 1}: Amount must be > 0` };
321
166
  }
322
167
  }
323
- catch (error) {
324
- return { valid: false, error: `Message ${i + 1}: Amount must be a valid number string (nanotons)` };
325
- }
326
- // Validate payload if provided (must be base64)
327
- if (msg.payload !== undefined && msg.payload !== null) {
328
- if (typeof msg.payload !== 'string') {
329
- return { valid: false, error: `Message ${i + 1}: Payload must be a base64 string` };
330
- }
331
- // Basic base64 validation
332
- if (msg.payload.length > 0 && !/^[A-Za-z0-9+/=]+$/.test(msg.payload)) {
333
- return { valid: false, error: `Message ${i + 1}: Payload must be valid base64 encoded` };
334
- }
335
- }
336
- // Validate stateInit if provided (must be base64)
337
- if (msg.stateInit !== undefined && msg.stateInit !== null) {
338
- if (typeof msg.stateInit !== 'string') {
339
- return { valid: false, error: `Message ${i + 1}: StateInit must be a base64 string` };
340
- }
341
- // Basic base64 validation
342
- if (msg.stateInit.length > 0 && !/^[A-Za-z0-9+/=]+$/.test(msg.stateInit)) {
343
- return { valid: false, error: `Message ${i + 1}: StateInit must be valid base64 encoded` };
344
- }
168
+ catch {
169
+ return { valid: false, error: `Message ${i + 1}: Amount must be a valid number string` };
345
170
  }
346
171
  }
347
- // CRITICAL: Limit maximum number of messages (prevent DoS)
348
172
  if (request.messages.length > 255) {
349
- return { valid: false, error: 'Transaction cannot have more than 255 messages' };
173
+ return { valid: false, error: 'Maximum 255 messages per transaction' };
350
174
  }
351
175
  return { valid: true };
352
176
  }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Session crypto for TON Connect v2 protocol
3
+ * Uses X25519 (NaCl box) for key exchange and message encryption
4
+ */
5
+ /**
6
+ * Convert Uint8Array to hex string
7
+ */
8
+ export declare function bytesToHex(bytes: Uint8Array): string;
9
+ /**
10
+ * Convert hex string to Uint8Array
11
+ */
12
+ export declare function hexToBytes(hex: string): Uint8Array;
13
+ /**
14
+ * Convert Uint8Array to base64
15
+ */
16
+ export declare function bytesToBase64(bytes: Uint8Array): string;
17
+ /**
18
+ * Convert base64 to Uint8Array
19
+ */
20
+ export declare function base64ToBytes(base64: string): Uint8Array;
21
+ /**
22
+ * Serialized session state for persistence
23
+ */
24
+ export interface SessionState {
25
+ secretKey: string;
26
+ walletPublicKey?: string;
27
+ }
28
+ /**
29
+ * Session crypto for TON Connect v2
30
+ * Handles X25519 key exchange and NaCl box encryption/decryption
31
+ */
32
+ export declare class SessionCrypto {
33
+ private keypair;
34
+ constructor(existingSecretKey?: Uint8Array);
35
+ /**
36
+ * Session ID = hex-encoded public key (used as client_id in bridge)
37
+ */
38
+ get sessionId(): string;
39
+ /**
40
+ * Public key bytes
41
+ */
42
+ get publicKey(): Uint8Array;
43
+ /**
44
+ * Secret key bytes (for persistence)
45
+ */
46
+ get secretKey(): Uint8Array;
47
+ /**
48
+ * Encrypt a message for a recipient
49
+ * Format: nonce (24 bytes) + ciphertext
50
+ */
51
+ encrypt(message: string, receiverPublicKey: Uint8Array): Uint8Array;
52
+ /**
53
+ * Decrypt a message from a sender
54
+ * Input format: nonce (24 bytes) + ciphertext
55
+ */
56
+ decrypt(encryptedMessage: Uint8Array, senderPublicKey: Uint8Array): string;
57
+ /**
58
+ * Serialize session for persistence
59
+ */
60
+ serialize(): SessionState;
61
+ /**
62
+ * Restore session from persisted state
63
+ */
64
+ static fromState(state: SessionState): SessionCrypto;
65
+ }