@blazium/ton-connect-mobile 1.2.5 → 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/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 -34
- package/dist/core/protocol.js +109 -288
- 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/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 -443
- 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
|
@@ -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';
|
package/dist/core/index.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Core
|
|
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
|
-
|
|
21
|
-
|
|
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; } });
|
package/dist/core/protocol.d.ts
CHANGED
|
@@ -1,53 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* TON Connect v2 Protocol Implementation
|
|
3
|
+
* Builds correct universal links and parses bridge responses
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import type { SendTransactionRequest, WalletInfo, ConnectEvent, ConnectErrorEvent, RpcResponse, RpcErrorResponse } from '../types';
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
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
|
|
10
|
+
export declare function buildConnectUniversalLink(universalLink: string, sessionId: string, manifestUrl: string, returnStrategy?: string): string;
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
+
* Build a universal link to bring wallet to foreground (for pending transactions)
|
|
13
|
+
* Format: {universalLink}?ret={returnStrategy}
|
|
12
14
|
*/
|
|
13
|
-
export declare function
|
|
15
|
+
export declare function buildReturnUniversalLink(universalLink: string, returnStrategy?: string): string;
|
|
14
16
|
/**
|
|
15
|
-
* Build
|
|
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
|
|
19
|
+
export declare function buildSendTransactionRpcRequest(request: SendTransactionRequest, id: number): string;
|
|
21
20
|
/**
|
|
22
|
-
* Build
|
|
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
|
|
23
|
+
export declare function buildDisconnectRpcRequest(id: number): string;
|
|
28
24
|
/**
|
|
29
|
-
* Parse
|
|
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
|
|
33
|
-
type: 'connect'
|
|
34
|
-
data:
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
*
|
|
43
|
-
* CRITICAL FIX: Only validate truly required fields (session, address, publicKey, name)
|
|
44
|
-
* appName and version are optional - extractWalletInfo has fallbacks for them
|
|
35
|
+
* Parse an RPC response from the wallet (for sendTransaction, disconnect, etc.)
|
|
45
36
|
*/
|
|
46
|
-
export declare function
|
|
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;
|
|
47
48
|
/**
|
|
48
|
-
*
|
|
49
|
+
* Extract wallet info from a connect event
|
|
49
50
|
*/
|
|
50
|
-
export declare function
|
|
51
|
+
export declare function extractWalletInfoFromEvent(event: ConnectEvent): WalletInfo;
|
|
51
52
|
/**
|
|
52
53
|
* Validate transaction request
|
|
53
54
|
*/
|