@dynamic-labs/ton 4.60.0 → 4.61.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/package.cjs +1 -1
  3. package/package.js +1 -1
  4. package/package.json +9 -9
  5. package/src/TonWalletConnector.cjs +1 -1
  6. package/src/TonWalletConnector.js +1 -1
  7. package/src/connectors/TonConnectConnector/TonConnectConnector.cjs +709 -0
  8. package/src/connectors/TonConnectConnector/TonConnectConnector.d.ts +169 -0
  9. package/src/connectors/TonConnectConnector/TonConnectConnector.js +705 -0
  10. package/src/connectors/TonConnectConnector/index.d.ts +1 -0
  11. package/src/consts.d.ts +4 -0
  12. package/src/index.cjs +11 -5
  13. package/src/index.d.ts +6 -1
  14. package/src/index.js +8 -3
  15. package/src/types.d.ts +61 -1
  16. package/src/utils/debugLog/debugLog.cjs +55 -0
  17. package/src/utils/debugLog/debugLog.d.ts +34 -0
  18. package/src/utils/debugLog/debugLog.js +49 -0
  19. package/src/utils/debugLog/index.d.ts +1 -0
  20. package/src/utils/fetchTonWalletConnectors/fetchTonWalletConnectors.cjs +113 -0
  21. package/src/utils/fetchTonWalletConnectors/fetchTonWalletConnectors.d.ts +41 -0
  22. package/src/utils/fetchTonWalletConnectors/fetchTonWalletConnectors.js +108 -0
  23. package/src/utils/fetchTonWalletConnectors/index.d.ts +1 -0
  24. package/src/utils/index.d.ts +1 -0
  25. package/src/waas/connector/DynamicWaasTonConnector.d.ts +6 -9
  26. package/src/wallet/TonWallet/TonWallet.cjs +140 -0
  27. package/src/wallet/TonWallet/TonWallet.d.ts +65 -0
  28. package/src/wallet/TonWallet/TonWallet.js +136 -0
  29. package/src/wallet/TonWallet/index.d.ts +1 -0
  30. package/src/wallet/WaasTonWallet.cjs +1 -1
  31. package/src/wallet/WaasTonWallet.js +1 -1
  32. package/src/wallet/isTonWallet/isTonWallet.cjs +7 -1
  33. package/src/wallet/isTonWallet/isTonWallet.d.ts +7 -1
  34. package/src/wallet/isTonWallet/isTonWallet.js +7 -1
  35. package/src/TonWalletConnectors.cjs +0 -13
  36. package/src/TonWalletConnectors.d.ts +0 -2
  37. package/src/TonWalletConnectors.js +0 -9
  38. package/src/wallet/TonWallet.cjs +0 -109
  39. package/src/wallet/TonWallet.d.ts +0 -60
  40. package/src/wallet/TonWallet.js +0 -105
@@ -0,0 +1,705 @@
1
+ 'use client'
2
+ import { __awaiter } from '../../../_virtual/_tslib.js';
3
+ import { TonConnect } from '@tonconnect/sdk';
4
+ import { Address, beginCell, Cell, loadStateInit } from '@ton/core';
5
+ import { TonClient, fromNano } from '@ton/ton';
6
+ import { WalletConnectorBase, logger } from '@dynamic-labs/wallet-connector-core';
7
+ import { DynamicError } from '@dynamic-labs/utils';
8
+ import { TonWallet } from '../../wallet/TonWallet/TonWallet.js';
9
+ import { debugLogMultiline, debugLogWithLevel, debugLog } from '../../utils/debugLog/debugLog.js';
10
+ import { TonUiTransaction, NANOTON_PER_TON } from '../../utils/TonUiTransaction/TonUiTransaction.js';
11
+ import { prepareTonTransfer } from '../../utils/prepareTonTransfer/prepareTonTransfer.js';
12
+ import { prepareJettonTransfer } from '../../utils/prepareJettonTransfer/prepareJettonTransfer.js';
13
+
14
+ /**
15
+ * TON Connect connector implementation
16
+ *
17
+ * Uses the TON Connect SDK to connect with TON wallets that support
18
+ * the TON Connect protocol (Tonkeeper, MyTonWallet, etc.)
19
+ */
20
+ class TonConnectConnector extends WalletConnectorBase {
21
+ /**
22
+ * Get the TonConnect instance (for discovering wallets and advanced usage)
23
+ */
24
+ getTonConnect() {
25
+ if (!this.tonConnect) {
26
+ const manifestUrl = typeof window !== 'undefined'
27
+ ? `${window.location.origin}/tonconnect-manifest.json`
28
+ : 'https://dynamic.xyz/tonconnect-manifest.json';
29
+ // Debug logging
30
+ debugLogMultiline([
31
+ '[TON Connect] Initializing TonConnect with manifest URL:',
32
+ manifestUrl,
33
+ ], [
34
+ '[TON Connect] Current origin:',
35
+ typeof window !== 'undefined' ? window.location.origin : 'SSR',
36
+ ]);
37
+ this.tonConnect = new TonConnect({
38
+ manifestUrl,
39
+ });
40
+ }
41
+ return this.tonConnect;
42
+ }
43
+ /**
44
+ * Creates a new TON Connect connector
45
+ *
46
+ * @param opts - Configuration options
47
+ */
48
+ constructor(opts) {
49
+ super(opts);
50
+ this.ChainWallet = TonWallet;
51
+ this.name = 'TON Connect';
52
+ this.overrideKey = 'tonconnect';
53
+ this.connectedChain = 'TON';
54
+ this.supportedChains = ['TON'];
55
+ this.canConnectViaQrCode = true;
56
+ this.connectedWallet = null;
57
+ this.pendingProofPayload = null;
58
+ this.tonNetworks = opts.tonNetworks || [];
59
+ this.overrideKey = opts.overrideKey || 'tonconnect';
60
+ // Only initialize TonConnect in browser environment
61
+ // This prevents SSR errors and allows the connector to be created
62
+ if (typeof window !== 'undefined') {
63
+ try {
64
+ const manifestUrl = opts.manifestUrl ||
65
+ `${window.location.origin}/tonconnect-manifest.json`;
66
+ // Debug logging
67
+ debugLogMultiline(['[TON Connect] Constructor - manifest URL:', manifestUrl], [
68
+ '[TON Connect] Constructor - window.location:',
69
+ { href: window.location.href, origin: window.location.origin },
70
+ ]);
71
+ this.tonConnect = new TonConnect({
72
+ manifestUrl,
73
+ });
74
+ }
75
+ catch (error) {
76
+ // If TonConnect fails to initialize, set to null and initialize lazily
77
+ logger.error('[TON Connect] Failed to initialize TonConnect:', error);
78
+ debugLogWithLevel('error', '[TON Connect] Constructor - Error initializing:', error);
79
+ this.tonConnect = null;
80
+ }
81
+ }
82
+ else {
83
+ // For SSR, tonConnect will be null and initialized lazily in getTonConnect()
84
+ this.tonConnect = null;
85
+ }
86
+ }
87
+ /**
88
+ * Setup event listeners for TON Connect
89
+ */
90
+ setupEventListeners() {
91
+ if (typeof window === 'undefined') {
92
+ return;
93
+ }
94
+ const tonConnect = this.getTonConnect();
95
+ tonConnect.onStatusChange((wallet) => {
96
+ if (wallet) {
97
+ this.connectedWallet = wallet;
98
+ const userFriendlyAddress = this.convertAddressToUserFriendly(wallet.account.address);
99
+ this.emit('accountChange', { accounts: [userFriendlyAddress] });
100
+ }
101
+ else {
102
+ this.connectedWallet = null;
103
+ this.emit('disconnect');
104
+ }
105
+ });
106
+ }
107
+ /**
108
+ * Check if TON Connect is available
109
+ */
110
+ isInstalledOnBrowser() {
111
+ // TON Connect wallets are mobile apps, not browser extensions
112
+ // Return false so they show as QR code/deep link wallets
113
+ return false;
114
+ }
115
+ /**
116
+ * Connect to a TON wallet
117
+ * @param tonProofPayload - Optional payload for tonProof authentication
118
+ */
119
+ connect(tonProofPayload) {
120
+ return __awaiter(this, void 0, void 0, function* () {
121
+ try {
122
+ const tonConnect = this.getTonConnect();
123
+ // If already connected, return early
124
+ if (tonConnect.connected) {
125
+ return;
126
+ }
127
+ // Get available wallets
128
+ const wallets = yield tonConnect.getWallets();
129
+ if (wallets.length === 0) {
130
+ throw new DynamicError('No TON wallets available');
131
+ }
132
+ const matchingWallet = this.findMatchingWallet(wallets);
133
+ if (!matchingWallet) {
134
+ throw new DynamicError(`TON wallet "${this.overrideKey}" not found in available wallets`);
135
+ }
136
+ try {
137
+ // Use provided payload or pending payload for tonProof
138
+ const proofPayload = tonProofPayload || this.pendingProofPayload;
139
+ const connectOptions = proofPayload
140
+ ? { request: { tonProof: proofPayload } }
141
+ : undefined;
142
+ tonConnect.connect(matchingWallet, connectOptions);
143
+ }
144
+ catch (error) {
145
+ debugLogWithLevel('error', '[TON Connect] Error during connect:', error);
146
+ throw error;
147
+ }
148
+ }
149
+ catch (error) {
150
+ if (this.isUserRejectionError(error)) {
151
+ throw new DynamicError('User rejected connection');
152
+ }
153
+ logger.error('[TON Connect] Connection failed:', error);
154
+ throw new DynamicError(`Failed to connect to TON wallet: ${error instanceof Error ? error.message : String(error)}`);
155
+ }
156
+ });
157
+ }
158
+ /**
159
+ * Find a wallet matching this connector's overrideKey
160
+ */
161
+ findMatchingWallet(wallets) {
162
+ const overrideKeyLower = this.overrideKey.toLowerCase();
163
+ return (wallets.find((wallet) => wallet.appName.toLowerCase() === overrideKeyLower ||
164
+ wallet.name.toLowerCase() === overrideKeyLower) ||
165
+ wallets.find((wallet) => wallet.appName.toLowerCase().startsWith(overrideKeyLower) ||
166
+ wallet.name.toLowerCase().startsWith(overrideKeyLower)) ||
167
+ wallets.find((wallet) => wallet.appName.toLowerCase().includes(overrideKeyLower) ||
168
+ wallet.name.toLowerCase().includes(overrideKeyLower)));
169
+ }
170
+ /**
171
+ * Get the current wallet address in user-friendly format (EQ... or UQ...)
172
+ */
173
+ getAddress() {
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ var _a, _b, _c;
176
+ const tonConnect = this.getTonConnect();
177
+ // If already connected, return the address immediately
178
+ if (tonConnect.connected && ((_a = tonConnect.account) === null || _a === void 0 ? void 0 : _a.address)) {
179
+ return this.convertAddressToUserFriendly(tonConnect.account.address);
180
+ }
181
+ // If not connected, initiate connection and wait for it to complete
182
+ try {
183
+ // Start the connection process (shows QR code/deep link)
184
+ yield this.connect();
185
+ // After connect() returns, the QR code is shown but connection is not yet established
186
+ // Wait for the connection to be established by polling or waiting for status change
187
+ // TON Connect SDK doesn't provide a clean way to wait, so we poll
188
+ const maxWaitTime = 300000; // 5 minutes
189
+ const pollInterval = 500; // Check every 500ms
190
+ const startTime = Date.now();
191
+ while (!tonConnect.connected && Date.now() - startTime < maxWaitTime) {
192
+ yield new Promise((resolve) => setTimeout(resolve, pollInterval));
193
+ // Check if connected after the delay
194
+ if (tonConnect.connected && ((_b = tonConnect.account) === null || _b === void 0 ? void 0 : _b.address)) {
195
+ const userFriendlyAddress = this.convertAddressToUserFriendly(tonConnect.account.address);
196
+ return userFriendlyAddress;
197
+ }
198
+ }
199
+ // If we get here, either timeout or still not connected
200
+ if (!tonConnect.connected) {
201
+ throw new DynamicError('Connection timeout - user did not approve the connection');
202
+ }
203
+ const rawAddress = (_c = tonConnect.account) === null || _c === void 0 ? void 0 : _c.address;
204
+ return rawAddress
205
+ ? this.convertAddressToUserFriendly(rawAddress)
206
+ : undefined;
207
+ }
208
+ catch (error) {
209
+ logger.error('[TON Connect] Failed to get address:', error);
210
+ throw error;
211
+ }
212
+ });
213
+ }
214
+ /**
215
+ * Get connected accounts
216
+ */
217
+ getConnectedAccounts() {
218
+ return __awaiter(this, void 0, void 0, function* () {
219
+ var _a;
220
+ const tonConnect = this.getTonConnect();
221
+ if (!tonConnect.connected || !((_a = tonConnect.account) === null || _a === void 0 ? void 0 : _a.address)) {
222
+ return [];
223
+ }
224
+ return [this.convertAddressToUserFriendly(tonConnect.account.address)];
225
+ });
226
+ }
227
+ /**
228
+ * Get the current network
229
+ */
230
+ getNetwork() {
231
+ return __awaiter(this, void 0, void 0, function* () {
232
+ var _a;
233
+ const tonConnect = this.getTonConnect();
234
+ if (!tonConnect.connected) {
235
+ return '-239'; // Mainnet default (chain ID -239)
236
+ }
237
+ const { account } = tonConnect;
238
+ // TON mainnet chain ID is -239, testnet is -3
239
+ return ((_a = account === null || account === void 0 ? void 0 : account.chain) === null || _a === void 0 ? void 0 : _a.toString()) || '-239';
240
+ });
241
+ }
242
+ /**
243
+ * Check if current network is testnet
244
+ */
245
+ isTestnet() {
246
+ return __awaiter(this, void 0, void 0, function* () {
247
+ const network = yield this.getNetwork();
248
+ // TON testnet chain ID is -3
249
+ return network === '-3';
250
+ });
251
+ }
252
+ /**
253
+ * Sign a message
254
+ */
255
+ signMessage(messageToSign) {
256
+ return __awaiter(this, void 0, void 0, function* () {
257
+ const tonConnect = this.getTonConnect();
258
+ if (!tonConnect.connected) {
259
+ throw new DynamicError('Wallet not connected');
260
+ }
261
+ try {
262
+ const result = yield tonConnect.signData({
263
+ text: messageToSign,
264
+ type: 'text',
265
+ });
266
+ return result.signature;
267
+ }
268
+ catch (error) {
269
+ if (this.isUserRejectionError(error)) {
270
+ throw new DynamicError('User rejected message signing');
271
+ }
272
+ logger.error('[TON Connect] Sign message failed:', error);
273
+ throw new DynamicError(`Failed to sign message: ${error instanceof Error ? error.message : String(error)}`);
274
+ }
275
+ });
276
+ }
277
+ /**
278
+ * Send a transaction
279
+ */
280
+ sendTransaction(request) {
281
+ return __awaiter(this, void 0, void 0, function* () {
282
+ const tonConnect = this.getTonConnect();
283
+ if (!tonConnect.connected) {
284
+ throw new DynamicError('Wallet not connected');
285
+ }
286
+ try {
287
+ // Convert addresses from raw format (0:hex) to user-friendly format (EQ...)
288
+ // TON Connect SDK expects user-friendly format for message addresses
289
+ const messages = request.messages.map((message) => (Object.assign(Object.assign({}, message), { address: this.convertAddressToUserFriendly(message.address) })));
290
+ // 'from' is omitted as TonConnect uses the connected wallet's address
291
+ const transactionRequest = {
292
+ messages,
293
+ network: request.network,
294
+ validUntil: request.validUntil || Math.floor(Date.now() / 1000) + 300, // 5 minutes from now
295
+ };
296
+ const result = yield tonConnect.sendTransaction(transactionRequest);
297
+ return {
298
+ boc: result.boc,
299
+ hash: result.boc, // TON Connect returns boc as the transaction identifier
300
+ };
301
+ }
302
+ catch (error) {
303
+ if (this.isUserRejectionError(error)) {
304
+ throw new DynamicError('User rejected transaction');
305
+ }
306
+ logger.error('[TON Connect] Send transaction failed:', error);
307
+ throw new DynamicError(`Failed to send transaction: ${error instanceof Error ? error.message : String(error)}`);
308
+ }
309
+ });
310
+ }
311
+ /**
312
+ * Send Jetton transaction
313
+ */
314
+ sendJettonTransaction(
315
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
316
+ _options) {
317
+ return __awaiter(this, void 0, void 0, function* () {
318
+ // Jetton transfers require constructing a specific message
319
+ // This is a simplified version - full implementation would need
320
+ // to construct the proper Jetton transfer message
321
+ throw new DynamicError('Jetton transfers not yet implemented');
322
+ });
323
+ }
324
+ /**
325
+ * Get TonClient for the current or specified network.
326
+ */
327
+ getTonClient(chainId) {
328
+ var _a, _b, _c, _d, _e;
329
+ const targetChainId = chainId || ((_b = (_a = this.tonNetworks) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.chainId);
330
+ const network = (_c = this.tonNetworks) === null || _c === void 0 ? void 0 : _c.find((net) => net.chainId === targetChainId);
331
+ if (!network) {
332
+ throw new DynamicError(`Network configuration not found for chainId: ${targetChainId}`);
333
+ }
334
+ const endpoint = ((_d = network.privateCustomerRpcUrls) === null || _d === void 0 ? void 0 : _d[0]) || ((_e = network.rpcUrls) === null || _e === void 0 ? void 0 : _e[0]);
335
+ if (!endpoint) {
336
+ throw new DynamicError(`No RPC endpoint found for chainId: ${targetChainId}`);
337
+ }
338
+ return new TonClient({ endpoint });
339
+ }
340
+ /**
341
+ * Get balance of an address
342
+ */
343
+ getBalance(address) {
344
+ return __awaiter(this, void 0, void 0, function* () {
345
+ try {
346
+ const client = this.getTonClient();
347
+ const tonAddress = Address.parse(address);
348
+ const balanceNano = yield client.getBalance(tonAddress);
349
+ const balance = fromNano(balanceNano);
350
+ return balance;
351
+ }
352
+ catch (error) {
353
+ logger.error('[TON Connect] Failed to get balance:', error);
354
+ return undefined;
355
+ }
356
+ });
357
+ }
358
+ /**
359
+ * Get enabled networks
360
+ */
361
+ getEnabledNetworks() {
362
+ return this.tonNetworks;
363
+ }
364
+ /**
365
+ * Switch network (TON Connect handles this automatically)
366
+ */
367
+ switchNetwork() {
368
+ return __awaiter(this, void 0, void 0, function* () {
369
+ // TON Connect manages network switching through the wallet UI
370
+ throw new DynamicError('Network switching must be done manually in the TON wallet');
371
+ });
372
+ }
373
+ /**
374
+ * Disconnect and cleanup
375
+ */
376
+ endSession() {
377
+ return __awaiter(this, void 0, void 0, function* () {
378
+ try {
379
+ const tonConnect = this.getTonConnect();
380
+ if (tonConnect.connected) {
381
+ yield tonConnect.disconnect();
382
+ }
383
+ }
384
+ catch (error) {
385
+ logger.debug('[TON Connect] Error during disconnect:', error);
386
+ }
387
+ });
388
+ }
389
+ /**
390
+ * Create a UI transaction for the send balance flow
391
+ */
392
+ createUiTransaction(from) {
393
+ return __awaiter(this, void 0, void 0, function* () {
394
+ yield this.validateActiveWallet(from);
395
+ const client = this.getTonClient();
396
+ return new TonUiTransaction({
397
+ client,
398
+ from,
399
+ onSubmit: (transaction) => __awaiter(this, void 0, void 0, function* () { return this.internalSendUiTransaction(transaction); }),
400
+ });
401
+ });
402
+ }
403
+ /**
404
+ * Internal handler for UI transaction submission
405
+ */
406
+ internalSendUiTransaction(transaction) {
407
+ return __awaiter(this, void 0, void 0, function* () {
408
+ var _a;
409
+ if (!transaction.to) {
410
+ throw new DynamicError('Destination address is required');
411
+ }
412
+ const tonConnect = this.getTonConnect();
413
+ if (!tonConnect.connected || !((_a = tonConnect.account) === null || _a === void 0 ? void 0 : _a.address)) {
414
+ throw new DynamicError('Wallet not connected');
415
+ }
416
+ const walletAddress = this.convertAddressToUserFriendly(tonConnect.account.address);
417
+ const client = this.getTonClient();
418
+ const network = tonConnect.account.chain;
419
+ // Jetton (non-native token) transfers
420
+ // TODO: Test it when token balances are implemented
421
+ if (transaction.nonNativeAddress && transaction.nonNativeValue) {
422
+ const request = yield prepareJettonTransfer({
423
+ client,
424
+ forwardTonAmount: BigInt(0),
425
+ jettonAmount: transaction.nonNativeValue,
426
+ jettonMasterAddress: transaction.nonNativeAddress,
427
+ network,
428
+ recipientAddress: transaction.to,
429
+ timeout: 60,
430
+ walletAddress,
431
+ });
432
+ const result = yield this.sendTransaction(request);
433
+ return result.boc || result.hash || '';
434
+ }
435
+ // Native TON transfers
436
+ if (transaction.value) {
437
+ const tonAmount = Number(transaction.value) / NANOTON_PER_TON;
438
+ const request = yield prepareTonTransfer({
439
+ amount: tonAmount,
440
+ client,
441
+ network,
442
+ recipient: transaction.to,
443
+ timeout: 60,
444
+ walletAddress,
445
+ });
446
+ const result = yield this.sendTransaction(request);
447
+ return result.boc || result.hash || '';
448
+ }
449
+ throw new DynamicError('Invalid transaction parameters');
450
+ });
451
+ }
452
+ /**
453
+ * Convert TON address from raw format (0:hex) to user-friendly format (UQ...)
454
+ * TON Connect SDK expects user-friendly format for message addresses
455
+ */
456
+ convertAddressToUserFriendly(address) {
457
+ var _a;
458
+ try {
459
+ if (address.startsWith('EQ') || address.startsWith('UQ')) {
460
+ return address;
461
+ }
462
+ // Detect if testnet from connected wallet
463
+ const tonConnect = this.getTonConnect();
464
+ const isTestnet = ((_a = tonConnect.account) === null || _a === void 0 ? void 0 : _a.chain) === '-3';
465
+ // Convert from raw format (0:hex) to user-friendly format
466
+ return Address.parse(address).toString({
467
+ bounceable: false,
468
+ testOnly: isTestnet,
469
+ urlSafe: true,
470
+ });
471
+ }
472
+ catch (error) {
473
+ // If parsing fails, return original address
474
+ logger.debug('[TON Connect] Failed to convert address format:', error);
475
+ return address;
476
+ }
477
+ }
478
+ /**
479
+ * Encode a comment as TON message payload
480
+ *
481
+ * TON comments are encoded as a Cell with:
482
+ * - First 32 bits (4 bytes): 0x00000000 (comment opcode)
483
+ * - Then the UTF-8 bytes of the comment
484
+ *
485
+ * For long messages, the comment is split across multiple cells using references.
486
+ * The Cell is then serialized to base64 BOC format for TON Connect SDK
487
+ */
488
+ encodeComment(comment) {
489
+ const encoder = new TextEncoder();
490
+ const commentBytes = encoder.encode(comment);
491
+ // TON Cell can hold max 1023 bits
492
+ // We use 32 bits for the opcode, leaving ~991 bits (~123 bytes) for data
493
+ // For longer messages, we split into multiple cells using references
494
+ const maxBytesPerCell = 123; // Conservative estimate to avoid overflow
495
+ if (commentBytes.length <= maxBytesPerCell) {
496
+ // Short message fits in one cell
497
+ const cell = beginCell()
498
+ .storeUint(0, 32) // Comment opcode
499
+ .storeBuffer(Buffer.from(commentBytes))
500
+ .endCell();
501
+ return cell.toBoc().toString('base64');
502
+ }
503
+ // Long message: split across multiple cells
504
+ // First cell: opcode + first chunk
505
+ // Remaining chunks go in reference cells
506
+ const firstChunk = commentBytes.slice(0, maxBytesPerCell);
507
+ const remainingBytes = commentBytes.slice(maxBytesPerCell);
508
+ // Build reference cells for remaining data
509
+ let remainingData = remainingBytes;
510
+ // Create cells for remaining data (each can hold ~127 bytes)
511
+ const referenceCells = [];
512
+ while (remainingData.length > 0) {
513
+ const chunk = remainingData.slice(0, 127);
514
+ remainingData = remainingData.slice(127);
515
+ referenceCells.push(beginCell().storeBuffer(Buffer.from(chunk)).endCell());
516
+ }
517
+ // Build the main cell with opcode, first chunk, and references
518
+ let mainCellBuilder = beginCell()
519
+ .storeUint(0, 32) // Comment opcode
520
+ .storeBuffer(Buffer.from(firstChunk));
521
+ // Add reference cells
522
+ for (const refCell of referenceCells) {
523
+ mainCellBuilder = mainCellBuilder.storeRef(refCell);
524
+ }
525
+ const mainCell = mainCellBuilder.endCell();
526
+ // Serialize to base64 BOC (Bag of Cells) format
527
+ return mainCell.toBoc().toString('base64');
528
+ }
529
+ /**
530
+ * Check if error is a user rejection
531
+ */
532
+ isUserRejectionError(error) {
533
+ return (error !== null &&
534
+ typeof error === 'object' &&
535
+ 'name' in error &&
536
+ error.name === 'UserRejectsError');
537
+ }
538
+ /**
539
+ * Generate a TON Connect proof for authentication
540
+ * @param payload - The payload string to include in the proof
541
+ * @returns TON Connect proof object
542
+ */
543
+ generateTonConnectProof(payload) {
544
+ return __awaiter(this, void 0, void 0, function* () {
545
+ var _a, _b, _c, _d, _e, _f;
546
+ const tonConnect = this.getTonConnect();
547
+ // If already connected with a proof, return it
548
+ if ((_b = (_a = this.connectedWallet) === null || _a === void 0 ? void 0 : _a.connectItems) === null || _b === void 0 ? void 0 : _b.tonProof) {
549
+ const { tonProof } = this.connectedWallet.connectItems;
550
+ if ('proof' in tonProof) {
551
+ debugLog('[TON Connect] Returning existing proof from connection');
552
+ return {
553
+ address: this.connectedWallet.account.address,
554
+ domain: tonProof.proof.domain,
555
+ payload: tonProof.proof.payload,
556
+ signature: tonProof.proof.signature,
557
+ timestamp: tonProof.proof.timestamp,
558
+ };
559
+ }
560
+ }
561
+ // If connected but no proof, disconnect and reconnect with proof
562
+ if (tonConnect.connected) {
563
+ debugLog('[TON Connect] Already connected without proof, disconnecting to reconnect with tonProof');
564
+ yield tonConnect.disconnect();
565
+ this.connectedWallet = null;
566
+ }
567
+ // Store the payload for the connect call
568
+ this.pendingProofPayload = payload;
569
+ // Connect with tonProof
570
+ yield this.connect(payload);
571
+ // Wait for the connection to be established with the proof
572
+ const maxWaitTime = 300000; // 5 minutes
573
+ const pollInterval = 500;
574
+ const startTime = Date.now();
575
+ while (Date.now() - startTime < maxWaitTime) {
576
+ yield new Promise((resolve) => setTimeout(resolve, pollInterval));
577
+ if (tonConnect.connected &&
578
+ ((_d = (_c = tonConnect.wallet) === null || _c === void 0 ? void 0 : _c.connectItems) === null || _d === void 0 ? void 0 : _d.tonProof) &&
579
+ 'proof' in tonConnect.wallet.connectItems.tonProof) {
580
+ this.connectedWallet = tonConnect.wallet;
581
+ const { tonProof } = tonConnect.wallet.connectItems;
582
+ // Clear the pending payload
583
+ this.pendingProofPayload = null;
584
+ return {
585
+ address: tonConnect.wallet.account.address,
586
+ domain: tonProof.proof.domain,
587
+ payload: tonProof.proof.payload,
588
+ signature: tonProof.proof.signature,
589
+ timestamp: tonProof.proof.timestamp,
590
+ };
591
+ }
592
+ // Check if connected but proof was rejected or not supported
593
+ if (tonConnect.connected && !((_f = (_e = tonConnect.wallet) === null || _e === void 0 ? void 0 : _e.connectItems) === null || _f === void 0 ? void 0 : _f.tonProof)) {
594
+ this.pendingProofPayload = null;
595
+ throw new DynamicError('Wallet connected but did not provide proof. The wallet may not support tonProof.');
596
+ }
597
+ }
598
+ this.pendingProofPayload = null;
599
+ throw new DynamicError('Connection timeout - user did not approve the connection');
600
+ });
601
+ }
602
+ /**
603
+ * Override proveOwnership to use tonProof for wallet verification
604
+ *
605
+ * For TON wallets, we use the tonProof mechanism during connection
606
+ * which is the standard way to prove wallet ownership in TON Connect.
607
+ * The backend expects a specific format with state_init and public_key.
608
+ */
609
+ proveOwnership(address, messageToSign) {
610
+ return __awaiter(this, void 0, void 0, function* () {
611
+ yield this.validateActiveWallet(address);
612
+ return this.proveOwnershipWithTonProof(messageToSign);
613
+ });
614
+ }
615
+ /**
616
+ * proveOwnership using tonProof
617
+ *
618
+ * Returns the proof in the format expected by the backend:
619
+ * {
620
+ * address: string,
621
+ * proof: { domain, payload, signature, state_init, timestamp },
622
+ * public_key: string
623
+ * }
624
+ */
625
+ proveOwnershipWithTonProof(messageToSign) {
626
+ return __awaiter(this, void 0, void 0, function* () {
627
+ var _a, _b;
628
+ // Extract the nonce from the SIWE message to use as tonProof payload
629
+ // The tonProof payload has a 128 byte limit, so we can't use the full message
630
+ const payload = this.extractNonceFromMessage(messageToSign);
631
+ // Use tonProof for authentication - this ensures we have connectedWallet set
632
+ const proof = yield this.generateTonConnectProof(payload);
633
+ // Get state_init from the connected wallet account
634
+ const stateInit = (_a = this.connectedWallet) === null || _a === void 0 ? void 0 : _a.account.walletStateInit;
635
+ if (!stateInit) {
636
+ throw new DynamicError('Wallet did not provide state_init required for verification');
637
+ }
638
+ // Get public key from wallet - this is the key that signed the proof
639
+ // The wallet provides this in account.publicKey (hex without 0x prefix)
640
+ const walletProvidedKey = (_b = this.connectedWallet) === null || _b === void 0 ? void 0 : _b.account.publicKey;
641
+ const publicKey = walletProvidedKey || this.extractPublicKeyFromStateInit(stateInit);
642
+ return JSON.stringify({
643
+ address: proof.address,
644
+ proof: {
645
+ domain: proof.domain,
646
+ payload: proof.payload,
647
+ signature: proof.signature,
648
+ state_init: stateInit,
649
+ timestamp: proof.timestamp,
650
+ },
651
+ public_key: publicKey,
652
+ });
653
+ });
654
+ }
655
+ /**
656
+ * Extract public key from state_init cell
657
+ *
658
+ * For standard TON wallets, the public key is stored in the data cell:
659
+ * - First 32 bits: seqno (skipped)
660
+ * - Next 256 bits (32 bytes): public key
661
+ *
662
+ * This matches the extraction logic in the backend.
663
+ */
664
+ extractPublicKeyFromStateInit(stateInitBase64) {
665
+ try {
666
+ const cell = Cell.fromBase64(stateInitBase64);
667
+ const stateInit = loadStateInit(cell.beginParse());
668
+ const dataCell = stateInit.data;
669
+ if (!dataCell) {
670
+ throw new Error('No data cell in state_init');
671
+ }
672
+ const dataSlice = dataCell.beginParse();
673
+ // Skip first 32 bits (seqno for most wallets)
674
+ dataSlice.loadUint(32);
675
+ // Next 256 bits (32 bytes) is the public key
676
+ const publicKeyBuffer = dataSlice.loadBuffer(32);
677
+ // Convert to hex string (no 0x prefix, as expected by backend)
678
+ const hexKey = Buffer.from(publicKeyBuffer).toString('hex');
679
+ return hexKey;
680
+ }
681
+ catch (error) {
682
+ logger.error('[TON Connect] Failed to extract public key from state_init:', error);
683
+ debugLog('[TON Connect] state_init that failed:', stateInitBase64);
684
+ throw new DynamicError('Failed to extract public key from wallet state_init');
685
+ }
686
+ }
687
+ /**
688
+ * Extract the nonce from a SIWE-style message
689
+ *
690
+ * @param message - The full message to extract nonce from
691
+ * @returns A short payload suitable for tonProof
692
+ */
693
+ extractNonceFromMessage(message) {
694
+ const nonceMatch = message.match(/Nonce:\s*([^\n\r]+)/i);
695
+ if (nonceMatch === null || nonceMatch === void 0 ? void 0 : nonceMatch[1]) {
696
+ const nonce = nonceMatch[1].trim();
697
+ if (nonce.length <= 128) {
698
+ return nonce;
699
+ }
700
+ }
701
+ return message.slice(0, 64);
702
+ }
703
+ }
704
+
705
+ export { TonConnectConnector };