@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.
- package/README.md +7 -20
- package/dist/adapters/react-native.js +8 -1
- package/dist/core/bridge.d.ts +61 -0
- package/dist/core/bridge.js +237 -0
- package/dist/core/crypto.d.ts +8 -19
- package/dist/core/crypto.js +15 -141
- package/dist/core/index.d.ts +5 -3
- package/dist/core/index.js +20 -17
- package/dist/core/protocol.d.ts +35 -32
- package/dist/core/protocol.js +109 -285
- package/dist/core/session.d.ts +65 -0
- package/dist/core/session.js +235 -0
- package/dist/core/wallets.d.ts +6 -6
- package/dist/core/wallets.js +17 -18
- package/dist/index.d.ts +33 -72
- package/dist/index.js +322 -769
- package/dist/react/TonConnectUIProvider.d.ts +4 -52
- package/dist/react/TonConnectUIProvider.js +18 -122
- package/dist/react/index.d.ts +1 -2
- package/dist/react/index.js +0 -1
- package/dist/types/index.d.ts +84 -139
- package/dist/types/index.js +1 -1
- package/package.json +2 -3
- package/src/adapters/react-native.ts +7 -1
- package/src/core/bridge.ts +307 -0
- package/src/core/crypto.ts +62 -238
- package/src/core/index.ts +17 -7
- package/src/core/protocol.ts +217 -441
- package/src/core/session.ts +247 -0
- package/src/core/wallets.ts +90 -93
- package/src/index.ts +811 -1338
- package/src/react/TonConnectUIProvider.tsx +272 -441
- package/src/react/index.ts +23 -27
- package/src/types/index.ts +217 -272
package/README.md
CHANGED
|
@@ -144,7 +144,6 @@ Access the TonConnectUI instance with all methods and features.
|
|
|
144
144
|
```typescript
|
|
145
145
|
const tonConnectUI = useTonConnectUI();
|
|
146
146
|
|
|
147
|
-
<<<<<<< HEAD
|
|
148
147
|
// Connection methods:
|
|
149
148
|
await tonConnectUI.connectWallet();
|
|
150
149
|
await tonConnectUI.disconnect();
|
|
@@ -241,7 +240,6 @@ new TonConnectMobile(config: TonConnectMobileConfig)
|
|
|
241
240
|
- `manifestUrl` (required): URL to your TonConnect manifest file
|
|
242
241
|
- `scheme` (required): Your app's deep link scheme
|
|
243
242
|
- `storageKeyPrefix` (optional): Prefix for storage keys (default: `'tonconnect_'`)
|
|
244
|
-
<<<<<<< HEAD
|
|
245
243
|
- `connectionTimeout` (optional): Connection timeout in ms (default: `300000` = 5 minutes)
|
|
246
244
|
- `transactionTimeout` (optional): Transaction timeout in ms (default: `300000` = 5 minutes)
|
|
247
245
|
- `skipCanOpenURLCheck` (optional): Skip canOpenURL check (default: `true` for Android compatibility)
|
|
@@ -325,7 +323,6 @@ const unsubscribe = ton.onStatusChange((status) => {
|
|
|
325
323
|
});
|
|
326
324
|
```
|
|
327
325
|
|
|
328
|
-
<<<<<<< HEAD
|
|
329
326
|
##### `getNetwork(): Network`
|
|
330
327
|
|
|
331
328
|
Get current network (mainnet or testnet).
|
|
@@ -445,20 +442,6 @@ ton.removeAllListeners(); // Remove all listeners
|
|
|
445
442
|
- iOS device or simulator
|
|
446
443
|
- Web browsers (for wallets with web support like Tonkeeper Web)
|
|
447
444
|
|
|
448
|
-
## Platform Support
|
|
449
|
-
|
|
450
|
-
- ✅ **Android**: Full support via Expo or React Native CLI
|
|
451
|
-
- ✅ **iOS**: Full support via Expo or React Native CLI
|
|
452
|
-
- ⚠️ **Web**: Deep links are not supported. The SDK will throw a clear error message if you try to use it in a web browser.
|
|
453
|
-
|
|
454
|
-
**Why?** The `tonconnect://` protocol is a custom URI scheme that requires a mobile app handler. Web browsers cannot handle these custom protocols.
|
|
455
|
-
|
|
456
|
-
**Testing**: To test wallet connections, use:
|
|
457
|
-
- Android device or emulator
|
|
458
|
-
- iOS device or simulator
|
|
459
|
-
- Not web browsers
|
|
460
|
-
>>>>>>> af0bd46f78c13fb8e9799027e48d4fa228a49e3c
|
|
461
|
-
|
|
462
445
|
## Configuration
|
|
463
446
|
|
|
464
447
|
### Expo Setup
|
|
@@ -526,13 +509,13 @@ The manifest URL must be accessible via HTTPS.
|
|
|
526
509
|
|
|
527
510
|
## Supported Wallets
|
|
528
511
|
|
|
529
|
-
<<<<<<< HEAD
|
|
530
512
|
- **Tonkeeper** - Full support (iOS, Android, Web)
|
|
531
513
|
- **MyTonWallet** - Full support (iOS, Android, Web)
|
|
532
514
|
- **Tonhub** - Full support (iOS, Android)
|
|
533
515
|
- **Wallet in Telegram** - Full support (iOS, Android)
|
|
534
516
|
|
|
535
517
|
**Note**: Wallet icons are automatically loaded from official sources. If an icon fails to load, a placeholder with the wallet's initial is shown.
|
|
518
|
+
|
|
536
519
|
## Migration from @tonconnect/ui-react
|
|
537
520
|
|
|
538
521
|
This SDK is a drop-in replacement for `@tonconnect/ui-react` in React Native/Expo environments.
|
|
@@ -613,7 +596,6 @@ MIT
|
|
|
613
596
|
For issues and questions:
|
|
614
597
|
- GitHub Issues: [https://github.com/blaziumdev/ton-connect-mobile/issues](https://github.com/blaziumdev/ton-connect-mobile/issues)
|
|
615
598
|
|
|
616
|
-
<<<<<<< HEAD
|
|
617
599
|
## New Features in v1.2.3
|
|
618
600
|
|
|
619
601
|
### 🌐 Network Switching
|
|
@@ -847,6 +829,11 @@ try {
|
|
|
847
829
|
|
|
848
830
|
## Changelog
|
|
849
831
|
|
|
832
|
+
### v1.2.5
|
|
833
|
+
- ✅ **FIXED**: Connection response validation - `appName` and `version` fields are now optional, improving compatibility with wallets that don't send these fields
|
|
834
|
+
- ✅ **FIXED**: ReactNativeAdapter URL listener error handling - Added try-catch block to prevent app crashes if URL listeners throw errors
|
|
835
|
+
- ✅ **IMPROVED**: Enhanced error handling robustness across the SDK
|
|
836
|
+
|
|
850
837
|
### v1.2.3
|
|
851
838
|
- ✅ **NEW**: Network switching - Switch between mainnet and testnet dynamically
|
|
852
839
|
- ✅ **NEW**: Event emitters - Listen to connect, disconnect, transaction, and error events
|
|
@@ -863,6 +850,7 @@ try {
|
|
|
863
850
|
- ✅ **IMPROVED**: Chain ID automatically updates when network changes
|
|
864
851
|
- ✅ **FIXED**: Tonkeeper now correctly shows as available on web
|
|
865
852
|
|
|
853
|
+
|
|
866
854
|
### v1.2.0
|
|
867
855
|
- ✅ **NEW**: Beautiful wallet selection modal component
|
|
868
856
|
- ✅ **NEW**: Transaction builder utilities (`buildTransferTransaction`, `tonToNano`, etc.)
|
|
@@ -873,7 +861,6 @@ try {
|
|
|
873
861
|
- ✅ Enhanced logging and debugging
|
|
874
862
|
- ✅ Better TypeScript types
|
|
875
863
|
|
|
876
|
-
|
|
877
864
|
### v1.1.5
|
|
878
865
|
- ✅ Full `@tonconnect/ui-react` compatibility
|
|
879
866
|
- ✅ React integration layer with hooks and components
|
|
@@ -34,7 +34,14 @@ class ReactNativeAdapter {
|
|
|
34
34
|
}
|
|
35
35
|
// Listen for deep links when app is already open
|
|
36
36
|
this.subscription = Linking.addEventListener('url', (event) => {
|
|
37
|
-
this.urlListeners.forEach((listener) =>
|
|
37
|
+
this.urlListeners.forEach((listener) => {
|
|
38
|
+
try {
|
|
39
|
+
listener(event.url);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error('[ReactNativeAdapter] Error in URL listener:', error);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
38
45
|
});
|
|
39
46
|
}
|
|
40
47
|
async openURL(url, skipCanOpenURLCheck = true) {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TON Connect v2 Bridge Gateway
|
|
3
|
+
* Handles SSE connection for receiving wallet responses and POST for sending messages
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Bridge message received from wallet
|
|
7
|
+
*/
|
|
8
|
+
export interface BridgeIncomingMessage {
|
|
9
|
+
from: string;
|
|
10
|
+
message: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Bridge Gateway for TON Connect v2 HTTP Bridge
|
|
14
|
+
*/
|
|
15
|
+
export declare class BridgeGateway {
|
|
16
|
+
private xhr;
|
|
17
|
+
private lastEventId;
|
|
18
|
+
private active;
|
|
19
|
+
private reconnectTimer;
|
|
20
|
+
private bridgeUrl;
|
|
21
|
+
private clientId;
|
|
22
|
+
private onMessageCallback;
|
|
23
|
+
private onErrorCallback;
|
|
24
|
+
/**
|
|
25
|
+
* Connect to the bridge SSE endpoint
|
|
26
|
+
* Listens for incoming messages from the wallet
|
|
27
|
+
*/
|
|
28
|
+
connect(bridgeUrl: string, clientId: string, onMessage: (msg: BridgeIncomingMessage) => void, onError?: (error: Error) => void): void;
|
|
29
|
+
/**
|
|
30
|
+
* Open SSE connection using XMLHttpRequest (works in React Native)
|
|
31
|
+
*/
|
|
32
|
+
private openSSE;
|
|
33
|
+
/**
|
|
34
|
+
* Fallback: poll bridge with fetch (for environments without XMLHttpRequest streaming)
|
|
35
|
+
*/
|
|
36
|
+
private pollWithFetch;
|
|
37
|
+
/**
|
|
38
|
+
* Handle a single SSE event data
|
|
39
|
+
*/
|
|
40
|
+
private handleEventData;
|
|
41
|
+
/**
|
|
42
|
+
* Schedule reconnection after a delay
|
|
43
|
+
*/
|
|
44
|
+
private scheduleReconnect;
|
|
45
|
+
/**
|
|
46
|
+
* Send an encrypted message via the bridge
|
|
47
|
+
*/
|
|
48
|
+
send(bridgeUrl: string, fromClientId: string, toClientId: string, encryptedMessage: Uint8Array, ttl?: number): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Close the bridge connection
|
|
51
|
+
*/
|
|
52
|
+
close(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Check if bridge is currently connected/active
|
|
55
|
+
*/
|
|
56
|
+
get isConnected(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Parse SSE text into events
|
|
59
|
+
*/
|
|
60
|
+
private parseSSE;
|
|
61
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TON Connect v2 Bridge Gateway
|
|
4
|
+
* Handles SSE connection for receiving wallet responses and POST for sending messages
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.BridgeGateway = void 0;
|
|
8
|
+
const session_1 = require("./session");
|
|
9
|
+
/**
|
|
10
|
+
* Bridge Gateway for TON Connect v2 HTTP Bridge
|
|
11
|
+
*/
|
|
12
|
+
class BridgeGateway {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.xhr = null;
|
|
15
|
+
this.lastEventId = '';
|
|
16
|
+
this.active = false;
|
|
17
|
+
this.reconnectTimer = null;
|
|
18
|
+
this.bridgeUrl = '';
|
|
19
|
+
this.clientId = '';
|
|
20
|
+
this.onMessageCallback = null;
|
|
21
|
+
this.onErrorCallback = null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Connect to the bridge SSE endpoint
|
|
25
|
+
* Listens for incoming messages from the wallet
|
|
26
|
+
*/
|
|
27
|
+
connect(bridgeUrl, clientId, onMessage, onError) {
|
|
28
|
+
this.bridgeUrl = bridgeUrl;
|
|
29
|
+
this.clientId = clientId;
|
|
30
|
+
this.onMessageCallback = onMessage;
|
|
31
|
+
this.onErrorCallback = onError || null;
|
|
32
|
+
this.active = true;
|
|
33
|
+
this.openSSE();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Open SSE connection using XMLHttpRequest (works in React Native)
|
|
37
|
+
*/
|
|
38
|
+
openSSE() {
|
|
39
|
+
if (!this.active)
|
|
40
|
+
return;
|
|
41
|
+
// Build URL
|
|
42
|
+
let url = `${this.bridgeUrl}/events?client_id=${this.clientId}`;
|
|
43
|
+
if (this.lastEventId) {
|
|
44
|
+
url += `&last_event_id=${this.lastEventId}`;
|
|
45
|
+
}
|
|
46
|
+
console.log('[Bridge] Opening SSE connection:', url);
|
|
47
|
+
// Use XMLHttpRequest for SSE (available in React Native)
|
|
48
|
+
if (typeof XMLHttpRequest === 'undefined') {
|
|
49
|
+
// Fallback: use fetch-based polling
|
|
50
|
+
this.pollWithFetch(url);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const xhr = new XMLHttpRequest();
|
|
54
|
+
this.xhr = xhr;
|
|
55
|
+
let processedLength = 0;
|
|
56
|
+
let buffer = '';
|
|
57
|
+
xhr.onreadystatechange = () => {
|
|
58
|
+
// LOADING (3) or DONE (4) — process incoming data
|
|
59
|
+
if (xhr.readyState >= 3) {
|
|
60
|
+
try {
|
|
61
|
+
const newData = xhr.responseText.substring(processedLength);
|
|
62
|
+
processedLength = xhr.responseText.length;
|
|
63
|
+
if (newData) {
|
|
64
|
+
buffer += newData;
|
|
65
|
+
const parsed = this.parseSSE(buffer);
|
|
66
|
+
buffer = parsed.remaining;
|
|
67
|
+
for (const event of parsed.events) {
|
|
68
|
+
if (event.id) {
|
|
69
|
+
this.lastEventId = event.id;
|
|
70
|
+
}
|
|
71
|
+
if (event.data) {
|
|
72
|
+
this.handleEventData(event.data);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
// Ignore parse errors, continue listening
|
|
79
|
+
console.warn('[Bridge] SSE parse error:', e);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Connection closed (readyState 4) — reconnect if still active
|
|
83
|
+
if (xhr.readyState === 4 && this.active) {
|
|
84
|
+
console.log('[Bridge] SSE connection closed, reconnecting...');
|
|
85
|
+
this.scheduleReconnect();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
xhr.onerror = () => {
|
|
89
|
+
console.error('[Bridge] SSE connection error');
|
|
90
|
+
if (this.active) {
|
|
91
|
+
this.scheduleReconnect();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
xhr.open('GET', url, true);
|
|
95
|
+
xhr.setRequestHeader('Accept', 'text/event-stream');
|
|
96
|
+
xhr.send();
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Fallback: poll bridge with fetch (for environments without XMLHttpRequest streaming)
|
|
100
|
+
*/
|
|
101
|
+
async pollWithFetch(url) {
|
|
102
|
+
while (this.active) {
|
|
103
|
+
try {
|
|
104
|
+
let pollUrl = `${this.bridgeUrl}/events?client_id=${this.clientId}`;
|
|
105
|
+
if (this.lastEventId) {
|
|
106
|
+
pollUrl += `&last_event_id=${this.lastEventId}`;
|
|
107
|
+
}
|
|
108
|
+
const controller = new AbortController();
|
|
109
|
+
const timeoutId = setTimeout(() => controller.abort(), 25000);
|
|
110
|
+
const response = await fetch(pollUrl, {
|
|
111
|
+
headers: { 'Accept': 'text/event-stream' },
|
|
112
|
+
signal: controller.signal,
|
|
113
|
+
});
|
|
114
|
+
clearTimeout(timeoutId);
|
|
115
|
+
const text = await response.text();
|
|
116
|
+
const parsed = this.parseSSE(text);
|
|
117
|
+
for (const event of parsed.events) {
|
|
118
|
+
if (event.id) {
|
|
119
|
+
this.lastEventId = event.id;
|
|
120
|
+
}
|
|
121
|
+
if (event.data) {
|
|
122
|
+
this.handleEventData(event.data);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (error?.name === 'AbortError') {
|
|
128
|
+
// Timeout — reconnect
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
console.error('[Bridge] Fetch poll error:', error);
|
|
132
|
+
// Wait before retrying
|
|
133
|
+
await new Promise((resolve) => setTimeout(() => resolve(), 2000));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Handle a single SSE event data
|
|
139
|
+
*/
|
|
140
|
+
handleEventData(data) {
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(data);
|
|
143
|
+
if (parsed.from && parsed.message) {
|
|
144
|
+
console.log('[Bridge] Received message from:', parsed.from.substring(0, 16) + '...');
|
|
145
|
+
if (this.onMessageCallback) {
|
|
146
|
+
this.onMessageCallback(parsed);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
// Might be a heartbeat or other non-JSON data — ignore
|
|
152
|
+
console.log('[Bridge] Non-message event:', data.substring(0, 50));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Schedule reconnection after a delay
|
|
157
|
+
*/
|
|
158
|
+
scheduleReconnect() {
|
|
159
|
+
if (this.reconnectTimer) {
|
|
160
|
+
clearTimeout(this.reconnectTimer);
|
|
161
|
+
}
|
|
162
|
+
this.reconnectTimer = setTimeout(() => {
|
|
163
|
+
if (this.active) {
|
|
164
|
+
this.openSSE();
|
|
165
|
+
}
|
|
166
|
+
}, 1500);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Send an encrypted message via the bridge
|
|
170
|
+
*/
|
|
171
|
+
async send(bridgeUrl, fromClientId, toClientId, encryptedMessage, ttl = 300) {
|
|
172
|
+
const base64Message = (0, session_1.bytesToBase64)(encryptedMessage);
|
|
173
|
+
const url = `${bridgeUrl}/message?client_id=${fromClientId}&to=${toClientId}&ttl=${ttl}`;
|
|
174
|
+
console.log('[Bridge] Sending message to:', toClientId.substring(0, 16) + '...');
|
|
175
|
+
const response = await fetch(url, {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
body: base64Message,
|
|
178
|
+
headers: {
|
|
179
|
+
'Content-Type': 'text/plain',
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
throw new Error(`Bridge send failed: ${response.status} ${response.statusText}`);
|
|
184
|
+
}
|
|
185
|
+
console.log('[Bridge] Message sent successfully');
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Close the bridge connection
|
|
189
|
+
*/
|
|
190
|
+
close() {
|
|
191
|
+
console.log('[Bridge] Closing connection');
|
|
192
|
+
this.active = false;
|
|
193
|
+
if (this.xhr) {
|
|
194
|
+
this.xhr.abort();
|
|
195
|
+
this.xhr = null;
|
|
196
|
+
}
|
|
197
|
+
if (this.reconnectTimer) {
|
|
198
|
+
clearTimeout(this.reconnectTimer);
|
|
199
|
+
this.reconnectTimer = null;
|
|
200
|
+
}
|
|
201
|
+
this.onMessageCallback = null;
|
|
202
|
+
this.onErrorCallback = null;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Check if bridge is currently connected/active
|
|
206
|
+
*/
|
|
207
|
+
get isConnected() {
|
|
208
|
+
return this.active;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Parse SSE text into events
|
|
212
|
+
*/
|
|
213
|
+
parseSSE(text) {
|
|
214
|
+
const events = [];
|
|
215
|
+
const parts = text.split('\n\n');
|
|
216
|
+
const remaining = parts.pop() || '';
|
|
217
|
+
for (const part of parts) {
|
|
218
|
+
if (!part.trim())
|
|
219
|
+
continue;
|
|
220
|
+
const event = {};
|
|
221
|
+
const lines = part.split('\n');
|
|
222
|
+
for (const line of lines) {
|
|
223
|
+
if (line.startsWith('id:')) {
|
|
224
|
+
event.id = line.substring(3).trim();
|
|
225
|
+
}
|
|
226
|
+
else if (line.startsWith('data:')) {
|
|
227
|
+
event.data = (event.data || '') + line.substring(5).trim();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (event.data || event.id) {
|
|
231
|
+
events.push(event);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return { events, remaining };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exports.BridgeGateway = BridgeGateway;
|
package/dist/core/crypto.d.ts
CHANGED
|
@@ -1,28 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cryptographic utilities for TonConnect
|
|
3
|
-
*
|
|
3
|
+
* Legacy module — primary crypto is now in session.ts
|
|
4
|
+
* This module provides helper functions that may be used externally
|
|
4
5
|
*/
|
|
5
|
-
import { ConnectionResponsePayload } from '../types';
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* The proof is signed by the wallet to verify authenticity
|
|
7
|
+
* Convert hex string to Uint8Array
|
|
9
8
|
*/
|
|
10
|
-
export declare function
|
|
11
|
-
/**
|
|
12
|
-
* Verify transaction signature
|
|
13
|
-
*
|
|
14
|
-
* WARNING: This function only performs basic format validation.
|
|
15
|
-
* Full signature verification requires parsing the BOC (Bag of Cells) and
|
|
16
|
-
* verifying the signature against the transaction hash, which requires
|
|
17
|
-
* TON library integration (@ton/core or @ton/crypto).
|
|
18
|
-
*
|
|
19
|
-
* For production use, transaction signatures should be verified server-side
|
|
20
|
-
* using proper TON libraries.
|
|
21
|
-
*
|
|
22
|
-
* @returns false - Always returns false to be safe until proper implementation
|
|
23
|
-
*/
|
|
24
|
-
export declare function verifyTransactionSignature(boc: string, signature: string, publicKey: string): boolean;
|
|
9
|
+
export declare function hexToBytes(hex: string): Uint8Array;
|
|
25
10
|
/**
|
|
26
11
|
* Generate random session ID
|
|
27
12
|
*/
|
|
28
13
|
export declare function generateSessionId(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Verify Ed25519 signature
|
|
16
|
+
*/
|
|
17
|
+
export declare function verifySignature(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean;
|
package/dist/core/crypto.js
CHANGED
|
@@ -1,153 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Cryptographic utilities for TonConnect
|
|
4
|
-
*
|
|
4
|
+
* Legacy module — primary crypto is now in session.ts
|
|
5
|
+
* This module provides helper functions that may be used externally
|
|
5
6
|
*/
|
|
6
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
8
|
-
exports.verifyTransactionSignature = verifyTransactionSignature;
|
|
8
|
+
exports.hexToBytes = hexToBytes;
|
|
9
9
|
exports.generateSessionId = generateSessionId;
|
|
10
|
+
exports.verifySignature = verifySignature;
|
|
10
11
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
11
12
|
const nacl = require('tweetnacl');
|
|
12
|
-
/**
|
|
13
|
-
* Decode base64 string to Uint8Array
|
|
14
|
-
*/
|
|
15
|
-
function decodeBase64(base64) {
|
|
16
|
-
// Remove padding and convert URL-safe to standard base64
|
|
17
|
-
const cleanBase64 = base64.replace(/-/g, '+').replace(/_/g, '/');
|
|
18
|
-
const padded = cleanBase64 + '='.repeat((4 - (cleanBase64.length % 4)) % 4);
|
|
19
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
20
|
-
const bytes = [];
|
|
21
|
-
let buffer = 0;
|
|
22
|
-
let bitsCollected = 0;
|
|
23
|
-
for (let i = 0; i < padded.length; i++) {
|
|
24
|
-
const ch = padded[i];
|
|
25
|
-
if (ch === '=')
|
|
26
|
-
break;
|
|
27
|
-
const index = chars.indexOf(ch);
|
|
28
|
-
if (index === -1)
|
|
29
|
-
continue;
|
|
30
|
-
buffer = (buffer << 6) | index;
|
|
31
|
-
bitsCollected += 6;
|
|
32
|
-
if (bitsCollected >= 8) {
|
|
33
|
-
bitsCollected -= 8;
|
|
34
|
-
bytes.push((buffer >> bitsCollected) & 0xff);
|
|
35
|
-
buffer &= (1 << bitsCollected) - 1;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return new Uint8Array(bytes);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Get TextEncoder with fallback
|
|
42
|
-
*/
|
|
43
|
-
function getTextEncoder() {
|
|
44
|
-
if (typeof TextEncoder !== 'undefined') {
|
|
45
|
-
return new TextEncoder();
|
|
46
|
-
}
|
|
47
|
-
// Fallback implementation for older React Native
|
|
48
|
-
throw new Error('TextEncoder is not available. Please use React Native 0.59+ or add a polyfill.');
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Verify connection proof signature
|
|
52
|
-
* The proof is signed by the wallet to verify authenticity
|
|
53
|
-
*/
|
|
54
|
-
function verifyConnectionProof(response, manifestUrl) {
|
|
55
|
-
// HIGH FIX: Log warning if proof is missing but allow for compatibility
|
|
56
|
-
if (!response.proof) {
|
|
57
|
-
console.warn('TON Connect: Connection proof missing - wallet may not support proof verification');
|
|
58
|
-
// Allow connection for compatibility, but log warning
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
try {
|
|
62
|
-
const { timestamp, domain, signature } = response.proof;
|
|
63
|
-
// Validate proof structure
|
|
64
|
-
if (typeof timestamp !== 'number' || !domain || typeof domain.lengthBytes !== 'number' || typeof domain.value !== 'string' || typeof signature !== 'string') {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
// Build the message that was signed
|
|
68
|
-
// Format: <timestamp>.<domain_length>.<domain_value>.<address>.<publicKey>
|
|
69
|
-
const domainLength = domain.lengthBytes;
|
|
70
|
-
const message = `${timestamp}.${domainLength}.${domain.value}.${response.address}.${response.publicKey}`;
|
|
71
|
-
// Convert public key from hex to Uint8Array
|
|
72
|
-
const publicKeyBytes = hexToBytes(response.publicKey);
|
|
73
|
-
if (publicKeyBytes.length !== 32) {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
// Convert signature from base64 to Uint8Array
|
|
77
|
-
const signatureBytes = decodeBase64(signature);
|
|
78
|
-
if (signatureBytes.length !== 64) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
// Verify signature using nacl
|
|
82
|
-
const encoder = getTextEncoder();
|
|
83
|
-
const messageBytes = encoder.encode(message);
|
|
84
|
-
return nacl.sign.detached.verify(messageBytes, signatureBytes, publicKeyBytes);
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
// If verification fails, return false
|
|
88
|
-
console.error('TON Connect: Proof verification error:', error);
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Verify transaction signature
|
|
94
|
-
*
|
|
95
|
-
* WARNING: This function only performs basic format validation.
|
|
96
|
-
* Full signature verification requires parsing the BOC (Bag of Cells) and
|
|
97
|
-
* verifying the signature against the transaction hash, which requires
|
|
98
|
-
* TON library integration (@ton/core or @ton/crypto).
|
|
99
|
-
*
|
|
100
|
-
* For production use, transaction signatures should be verified server-side
|
|
101
|
-
* using proper TON libraries.
|
|
102
|
-
*
|
|
103
|
-
* @returns false - Always returns false to be safe until proper implementation
|
|
104
|
-
*/
|
|
105
|
-
function verifyTransactionSignature(boc, signature, publicKey) {
|
|
106
|
-
// CRITICAL FIX: This function does not actually verify signatures
|
|
107
|
-
// It only checks format. For security, we return false until proper implementation.
|
|
108
|
-
try {
|
|
109
|
-
// Basic format validation
|
|
110
|
-
if (!boc || typeof boc !== 'string' || boc.length === 0) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
if (!signature || typeof signature !== 'string' || signature.length === 0) {
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
if (!publicKey || typeof publicKey !== 'string' || publicKey.length === 0) {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
// Convert public key from hex to Uint8Array
|
|
120
|
-
const publicKeyBytes = hexToBytes(publicKey);
|
|
121
|
-
if (publicKeyBytes.length !== 32) {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
// Convert signature from base64 to Uint8Array
|
|
125
|
-
const signatureBytes = decodeBase64(signature);
|
|
126
|
-
if (signatureBytes.length !== 64) {
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
// Convert BOC from base64 to Uint8Array
|
|
130
|
-
const bocBytes = decodeBase64(boc);
|
|
131
|
-
if (bocBytes.length === 0) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
// CRITICAL: Return false - actual signature verification requires TON library
|
|
135
|
-
// TODO: Integrate @ton/core or @ton/crypto for proper BOC parsing and signature verification
|
|
136
|
-
console.warn('TON Connect: Transaction signature verification not fully implemented. Signature format is valid but not cryptographically verified. Verify server-side using @ton/core.');
|
|
137
|
-
return false; // Fail-safe: reject until properly implemented
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
console.error('TON Connect: Transaction signature verification error:', error);
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
13
|
/**
|
|
145
14
|
* Convert hex string to Uint8Array
|
|
146
15
|
*/
|
|
147
16
|
function hexToBytes(hex) {
|
|
148
|
-
// Remove 0x prefix if present
|
|
149
17
|
const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
150
|
-
// Handle odd-length hex strings
|
|
151
18
|
const paddedHex = cleanHex.length % 2 === 0 ? cleanHex : '0' + cleanHex;
|
|
152
19
|
const bytes = new Uint8Array(paddedHex.length / 2);
|
|
153
20
|
for (let i = 0; i < paddedHex.length; i += 2) {
|
|
@@ -160,13 +27,11 @@ function hexToBytes(hex) {
|
|
|
160
27
|
*/
|
|
161
28
|
function getSecureRandomBytes(length) {
|
|
162
29
|
const bytes = new Uint8Array(length);
|
|
163
|
-
// Try to use crypto.getRandomValues (available in React Native with polyfill)
|
|
164
30
|
// eslint-disable-next-line no-undef
|
|
165
31
|
if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.getRandomValues) {
|
|
166
32
|
globalThis.crypto.getRandomValues(bytes);
|
|
167
33
|
return bytes;
|
|
168
34
|
}
|
|
169
|
-
// HIGH FIX: Throw error instead of using insecure Math.random()
|
|
170
35
|
throw new Error('Cryptographically secure random number generation not available. ' +
|
|
171
36
|
'Please install react-native-get-random-values or use React Native 0.59+');
|
|
172
37
|
}
|
|
@@ -174,10 +39,19 @@ function getSecureRandomBytes(length) {
|
|
|
174
39
|
* Generate random session ID
|
|
175
40
|
*/
|
|
176
41
|
function generateSessionId() {
|
|
177
|
-
// HIGH FIX: Use secure random bytes
|
|
178
42
|
const bytes = getSecureRandomBytes(32);
|
|
179
|
-
// Convert to hex string
|
|
180
43
|
return Array.from(bytes)
|
|
181
44
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
182
45
|
.join('');
|
|
183
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Verify Ed25519 signature
|
|
49
|
+
*/
|
|
50
|
+
function verifySignature(message, signature, publicKey) {
|
|
51
|
+
try {
|
|
52
|
+
return nacl.sign.detached.verify(message, signature, publicKey);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Core
|
|
2
|
+
* Core module exports
|
|
3
3
|
*/
|
|
4
|
-
export
|
|
5
|
-
export
|
|
4
|
+
export { SessionCrypto } from './session';
|
|
5
|
+
export { BridgeGateway } from './bridge';
|
|
6
|
+
export { buildConnectUniversalLink, buildReturnUniversalLink, buildSendTransactionRpcRequest, buildDisconnectRpcRequest, parseConnectResponse, parseRpcResponse, extractWalletInfoFromEvent, validateTransactionRequest, } from './protocol';
|
|
7
|
+
export { SUPPORTED_WALLETS, getWalletByName, getDefaultWallet, getWalletsForPlatform } from './wallets';
|