@tonconnect/sdk 3.3.0-beta.1 → 3.3.0-beta.3

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/lib/esm/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CONNECT_EVENT_ERROR_CODES, SEND_TRANSACTION_ERROR_CODES, SIGN_DATA_ERROR_CODES, Base64, SessionCrypto, hexToByteArray, CONNECT_ITEM_ERROR_CODES } from '@tonconnect/protocol';
2
- export { CHAIN, CONNECT_EVENT_ERROR_CODES, CONNECT_ITEM_ERROR_CODES, SEND_TRANSACTION_ERROR_CODES, SIGN_DATA_ERROR_CODES } from '@tonconnect/protocol';
2
+ export { CHAIN, CONNECT_EVENT_ERROR_CODES, CONNECT_ITEM_ERROR_CODES, SEND_TRANSACTION_ERROR_CODES, SIGN_DATA_ERROR_CODES, SessionCrypto } from '@tonconnect/protocol';
3
3
  import '@tonconnect/isomorphic-eventsource';
4
4
  import '@tonconnect/isomorphic-fetch';
5
5
 
@@ -412,12 +412,12 @@ function delay(timeout, options) {
412
412
  return __awaiter(this, void 0, void 0, function* () {
413
413
  return new Promise((resolve, reject) => {
414
414
  var _a, _b;
415
- if ((_a = options === null || options === void 0 ? void 0 : options.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
415
+ if ((_a = void 0 ) === null || _a === void 0 ? void 0 : _a.aborted) {
416
416
  reject(new TonConnectError('Delay aborted'));
417
417
  return;
418
418
  }
419
419
  const timeoutId = setTimeout(() => resolve(), timeout);
420
- (_b = options === null || options === void 0 ? void 0 : options.signal) === null || _b === void 0 ? void 0 : _b.addEventListener('abort', () => {
420
+ (_b = void 0 ) === null || _b === void 0 ? void 0 : _b.addEventListener('abort', () => {
421
421
  clearTimeout(timeoutId);
422
422
  reject(new TonConnectError('Delay aborted'));
423
423
  });
@@ -448,6 +448,7 @@ function createAbortController(signal) {
448
448
  * @param {T} fn - function to call
449
449
  * @param {CallForSuccessOptions} [options] - optional configuration options
450
450
  */
451
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
451
452
  function callForSuccess(fn, options) {
452
453
  return __awaiter(this, void 0, void 0, function* () {
453
454
  var _a, _b;
@@ -512,6 +513,7 @@ function logWarning(...args) {
512
513
  * @param {(...args: Args) => Promise<T>} createFn - A function that creates the resource.
513
514
  * @param {(resource: T) => Promise<void>} [disposeFn] - An optional function that disposes the resource.
514
515
  */
516
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
515
517
  function createResource(createFn, disposeFn) {
516
518
  let currentResource = null;
517
519
  let currentArgs = null;
@@ -1471,22 +1473,6 @@ function getWindow() {
1471
1473
  }
1472
1474
  return window;
1473
1475
  }
1474
- /**
1475
- * The function try to get window keys, if it is not available it returns empty array.
1476
- * As an example, for Safari's private mode it returns empty array, because the browser does not allow to get window keys.
1477
- */
1478
- function tryGetWindowKeys() {
1479
- const window = getWindow();
1480
- if (!window) {
1481
- return [];
1482
- }
1483
- try {
1484
- return Object.keys(window);
1485
- }
1486
- catch (_a) {
1487
- return [];
1488
- }
1489
- }
1490
1476
  function getDocument() {
1491
1477
  if (typeof document === 'undefined') {
1492
1478
  return undefined;
@@ -1531,6 +1517,43 @@ function isLocalStorageAvailable() {
1531
1517
  function isNodeJs() {
1532
1518
  return (typeof process !== 'undefined' && process.versions != null && process.versions.node != null);
1533
1519
  }
1520
+ /**
1521
+ * Returns the current domain (hostname) if available.
1522
+ * In browser environment, returns window.location.hostname.
1523
+ * In Node.js environment or when window is not available, returns null.
1524
+ */
1525
+ function getDomain() {
1526
+ try {
1527
+ // In browser environment
1528
+ if (typeof window !== 'undefined' && window.location) {
1529
+ return window.location.hostname;
1530
+ }
1531
+ else {
1532
+ // In Node.js environment, skip domain validation
1533
+ return null;
1534
+ }
1535
+ }
1536
+ catch (_a) {
1537
+ return null;
1538
+ }
1539
+ }
1540
+ /**
1541
+ * Returns an array of [key, value] pairs from window object if available.
1542
+ * In browser environment, returns Object.entries(window).
1543
+ * In Node.js environment or when window is not available, returns empty array.
1544
+ */
1545
+ function getWindowEntries() {
1546
+ const window = getWindow();
1547
+ if (!window) {
1548
+ return [];
1549
+ }
1550
+ try {
1551
+ return Object.entries(window);
1552
+ }
1553
+ catch (_a) {
1554
+ return [];
1555
+ }
1556
+ }
1534
1557
 
1535
1558
  class InjectedProvider {
1536
1559
  static fromStorage(storage) {
@@ -1553,8 +1576,8 @@ class InjectedProvider {
1553
1576
  if (!this.window) {
1554
1577
  return [];
1555
1578
  }
1556
- const windowKeys = tryGetWindowKeys();
1557
- const wallets = windowKeys.filter(([_, value]) => isJSBridgeWithMetadata(value));
1579
+ const windowEntries = getWindowEntries();
1580
+ const wallets = windowEntries.filter(([_key, value]) => isJSBridgeWithMetadata(value));
1558
1581
  return wallets.map(([jsBridgeKey, wallet]) => ({
1559
1582
  name: wallet.tonconnect.walletInfo.name,
1560
1583
  appName: wallet.tonconnect.walletInfo.app_name,
@@ -2100,11 +2123,6 @@ function enableQaMode() {
2100
2123
  function isQaModeEnabled() {
2101
2124
  return qaModeEnabled;
2102
2125
  }
2103
- function logValidationError(message) {
2104
- if (isQaModeEnabled()) {
2105
- console.error(`[QA Mode] Validation failed: ${message}`);
2106
- }
2107
- }
2108
2126
  function showQaModeBanner() {
2109
2127
  if (typeof window === 'undefined')
2110
2128
  return;
@@ -2173,10 +2191,10 @@ function addQaModeStyles() {
2173
2191
  function startBannerObserver() {
2174
2192
  if (typeof window === 'undefined' || bannerObserver)
2175
2193
  return;
2176
- bannerObserver = new MutationObserver((mutations) => {
2177
- mutations.forEach((mutation) => {
2194
+ bannerObserver = new MutationObserver(mutations => {
2195
+ mutations.forEach(mutation => {
2178
2196
  if (mutation.type === 'childList') {
2179
- mutation.removedNodes.forEach((node) => {
2197
+ mutation.removedNodes.forEach(node => {
2180
2198
  if (node.nodeType === Node.ELEMENT_NODE) {
2181
2199
  const element = node;
2182
2200
  if (element.id === 'ton-connect-qa-banner' && qaModeEnabled) {
@@ -2205,10 +2223,11 @@ function startBannerObserver() {
2205
2223
  class WalletsListManager {
2206
2224
  constructor(options) {
2207
2225
  var _a;
2208
- this.walletsListCache = null;
2209
- this.walletsListCacheCreationTimestamp = null;
2226
+ this.walletsListDTOCache = null;
2227
+ this.walletsListDTOCacheCreationTimestamp = null;
2210
2228
  if (isQaModeEnabled()) {
2211
- this.walletsListSource = 'https://raw.githubusercontent.com/ton-connect/wallets-list-staging/refs/heads/main/wallets-v2.json';
2229
+ this.walletsListSource =
2230
+ 'https://raw.githubusercontent.com/ton-connect/wallets-list-staging/refs/heads/main/wallets-v2.json';
2212
2231
  }
2213
2232
  else {
2214
2233
  this.walletsListSource =
@@ -2218,23 +2237,11 @@ class WalletsListManager {
2218
2237
  }
2219
2238
  getWallets() {
2220
2239
  return __awaiter(this, void 0, void 0, function* () {
2221
- if (this.cacheTTLMs &&
2222
- this.walletsListCacheCreationTimestamp &&
2223
- Date.now() > this.walletsListCacheCreationTimestamp + this.cacheTTLMs) {
2224
- this.walletsListCache = null;
2225
- }
2226
- if (!this.walletsListCache) {
2227
- this.walletsListCache = this.fetchWalletsList();
2228
- this.walletsListCache
2229
- .then(() => {
2230
- this.walletsListCacheCreationTimestamp = Date.now();
2231
- })
2232
- .catch(() => {
2233
- this.walletsListCache = null;
2234
- this.walletsListCacheCreationTimestamp = null;
2235
- });
2236
- }
2237
- return this.walletsListCache;
2240
+ const [walletsListDTO, currentlyInjectedWallets] = yield Promise.all([
2241
+ this.fetchWalletsListDTO(),
2242
+ this.getCurrentlyInjectedWallets()
2243
+ ]);
2244
+ return this.mergeWalletsLists(this.walletConfigDTOListToWalletConfigList(walletsListDTO), currentlyInjectedWallets);
2238
2245
  });
2239
2246
  }
2240
2247
  getEmbeddedWallet() {
@@ -2244,7 +2251,28 @@ class WalletsListManager {
2244
2251
  return embeddedWallets.length === 1 ? embeddedWallets[0] : null;
2245
2252
  });
2246
2253
  }
2247
- fetchWalletsList() {
2254
+ fetchWalletsListDTO() {
2255
+ return __awaiter(this, void 0, void 0, function* () {
2256
+ if (this.cacheTTLMs &&
2257
+ this.walletsListDTOCacheCreationTimestamp &&
2258
+ Date.now() > this.walletsListDTOCacheCreationTimestamp + this.cacheTTLMs) {
2259
+ this.walletsListDTOCache = null;
2260
+ }
2261
+ if (!this.walletsListDTOCache) {
2262
+ this.walletsListDTOCache = this.fetchWalletsListFromSource();
2263
+ this.walletsListDTOCache
2264
+ .then(() => {
2265
+ this.walletsListDTOCacheCreationTimestamp = Date.now();
2266
+ })
2267
+ .catch(() => {
2268
+ this.walletsListDTOCache = null;
2269
+ this.walletsListDTOCacheCreationTimestamp = null;
2270
+ });
2271
+ }
2272
+ return this.walletsListDTOCache;
2273
+ });
2274
+ }
2275
+ fetchWalletsListFromSource() {
2248
2276
  return __awaiter(this, void 0, void 0, function* () {
2249
2277
  let walletsList = [];
2250
2278
  try {
@@ -2265,16 +2293,21 @@ class WalletsListManager {
2265
2293
  logError(e);
2266
2294
  walletsList = FALLBACK_WALLETS_LIST;
2267
2295
  }
2268
- let currentlyInjectedWallets = [];
2269
- try {
2270
- currentlyInjectedWallets = InjectedProvider.getCurrentlyInjectedWallets();
2271
- }
2272
- catch (e) {
2273
- logError(e);
2274
- }
2275
- return this.mergeWalletsLists(this.walletConfigDTOListToWalletConfigList(walletsList), currentlyInjectedWallets);
2296
+ return walletsList;
2276
2297
  });
2277
2298
  }
2299
+ getCurrentlyInjectedWallets() {
2300
+ if (!isQaModeEnabled()) {
2301
+ return [];
2302
+ }
2303
+ try {
2304
+ return InjectedProvider.getCurrentlyInjectedWallets();
2305
+ }
2306
+ catch (e) {
2307
+ logError(e);
2308
+ return [];
2309
+ }
2310
+ }
2278
2311
  walletConfigDTOListToWalletConfigList(walletConfigDTO) {
2279
2312
  return walletConfigDTO.map(walletConfigDTO => {
2280
2313
  const walletConfig = {
@@ -2917,8 +2950,9 @@ class TonConnectTracker {
2917
2950
  }
2918
2951
  }
2919
2952
 
2920
- const tonConnectSdkVersion = "3.3.0-beta.1";
2953
+ const tonConnectSdkVersion = "3.3.0-beta.3";
2921
2954
 
2955
+ const bounceableTag = 0x11;
2922
2956
  const noBounceableTag = 0x51;
2923
2957
  const testOnlyTag = 0x80;
2924
2958
  /**
@@ -2988,23 +3022,40 @@ function parseUserFriendlyAddress(address) {
2988
3022
  throw new WrongAddressError(`Invalid address length: ${address}`);
2989
3023
  }
2990
3024
  const addr = decoded.slice(0, 34);
2991
- const checksum = decoded.slice(34);
3025
+ const checksum = decoded.slice(34, 36);
2992
3026
  const calculatedChecksum = crc16(addr);
2993
3027
  if (!checksum.every((byte, i) => byte === calculatedChecksum[i])) {
2994
3028
  throw new WrongAddressError(`Invalid checksum in address: ${address}`);
2995
3029
  }
2996
- const tag = addr[0];
2997
- const wc = addr[1];
3030
+ let tag = addr[0];
3031
+ let isTestOnly = false;
3032
+ let isBounceable = false;
3033
+ if (tag & testOnlyTag) {
3034
+ isTestOnly = true;
3035
+ tag = tag ^ testOnlyTag;
3036
+ }
3037
+ if (tag !== bounceableTag && tag !== noBounceableTag) {
3038
+ throw new WrongAddressError(`Unknown address tag: ${tag}`);
3039
+ }
3040
+ isBounceable = tag === bounceableTag;
3041
+ let wc = null;
3042
+ if (addr[1] === 0xff) {
3043
+ // TODO we should read signed integer here
3044
+ wc = -1;
3045
+ }
3046
+ else {
3047
+ wc = addr[1];
3048
+ }
2998
3049
  const hex = addr.slice(2);
2999
3050
  if (wc !== 0 && wc !== -1) {
3000
3051
  throw new WrongAddressError(`Invalid workchain: ${wc}`);
3001
3052
  }
3002
- const testOnly = (tag & testOnlyTag) !== 0;
3003
- const isBounceable = (tag & 0x40) !== 0;
3004
3053
  return {
3005
3054
  wc,
3006
- hex: Array.from(hex).map(b => b.toString(16).padStart(2, '0')).join(''),
3007
- testOnly,
3055
+ hex: Array.from(hex)
3056
+ .map(b => b.toString(16).padStart(2, '0'))
3057
+ .join(''),
3058
+ testOnly: isTestOnly,
3008
3059
  isBounceable
3009
3060
  };
3010
3061
  }
@@ -3081,12 +3132,21 @@ const BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{
3081
3132
  const BOC_PREFIX = 'te6cc';
3082
3133
  const INTEGER_REGEX = /^-?\d+$/;
3083
3134
  const POSITIVE_INTEGER_REGEX = /^\d+$/;
3135
+ const MAX_DOMAIN_BYTES = 128;
3136
+ const MAX_PAYLOAD_BYTES = 128;
3137
+ const MAX_TOTAL_BYTES = 222;
3084
3138
  function isValidNumber(value) {
3085
3139
  return typeof value === 'number' && !isNaN(value);
3086
3140
  }
3087
3141
  function isValidString(value) {
3088
3142
  return typeof value === 'string' && value.length > 0;
3089
3143
  }
3144
+ function isValidAddress(value) {
3145
+ return isValidString(value) && (isValidRawAddress(value) || isValidUserFriendlyAddress(value));
3146
+ }
3147
+ function isValidNetwork(value) {
3148
+ return isValidString(value) && /^-?\d+$/.test(value);
3149
+ }
3090
3150
  function isValidBoc(value) {
3091
3151
  return typeof value === 'string' && BASE64_REGEX.test(value) && value.startsWith(BOC_PREFIX);
3092
3152
  }
@@ -3100,49 +3160,33 @@ function hasExtraProperties(obj, allowedKeys) {
3100
3160
  return Object.keys(obj).some(key => !allowedKeys.includes(key));
3101
3161
  }
3102
3162
  function validateSendTransactionRequest(data) {
3103
- console.log('[Validation Debug] validateSendTransactionRequest called');
3104
- console.log('[Validation Debug] isQaModeEnabled():', isQaModeEnabled());
3105
3163
  if (!isValidObject(data)) {
3106
- const error = "Request must be an object";
3107
- logValidationError(error);
3108
- const shouldReturnNull = isQaModeEnabled();
3109
- console.log('[Validation Debug] Should return null:', shouldReturnNull);
3110
- return shouldReturnNull ? null : error;
3164
+ return 'Request must be an object';
3111
3165
  }
3112
3166
  const allowedKeys = ['validUntil', 'network', 'from', 'messages'];
3113
3167
  if (hasExtraProperties(data, allowedKeys)) {
3114
- const error = "Request contains extra properties";
3115
- logValidationError(error);
3116
- return isQaModeEnabled() ? null : error;
3168
+ return 'Request contains extra properties';
3117
3169
  }
3118
- if (!isValidNumber(data.validUntil)) {
3119
- const error = "Incorrect 'validUntil'";
3120
- logValidationError(error);
3121
- return isQaModeEnabled() ? null : error;
3122
- }
3123
- const now = Math.floor(Date.now() / 1000);
3124
- const fiveMinutesFromNow = now + 300;
3125
- if (data.validUntil > fiveMinutesFromNow) {
3126
- console.warn(`validUntil (${data.validUntil}) is more than 5 minutes from now (${now})`);
3170
+ if (data.validUntil) {
3171
+ if (!isValidNumber(data.validUntil)) {
3172
+ return "Incorrect 'validUntil'";
3173
+ }
3174
+ const now = Math.floor(Date.now() / 1000);
3175
+ const fiveMinutesFromNow = now + 300;
3176
+ if (data.validUntil > fiveMinutesFromNow) {
3177
+ console.warn(`validUntil (${data.validUntil}) is more than 5 minutes from now (${now})`);
3178
+ }
3127
3179
  }
3128
3180
  if (data.network !== undefined) {
3129
- if (!isValidString(data.network) || !/^[\d-]+$/.test(data.network)) {
3130
- const error = "Invalid 'network' format";
3131
- logValidationError(error);
3132
- return isQaModeEnabled() ? null : error;
3181
+ if (!isValidNetwork(data.network)) {
3182
+ return "Invalid 'network' format";
3133
3183
  }
3134
3184
  }
3135
- if (data.from !== undefined) {
3136
- if (!isValidString(data.from) || !isValidRawAddress(data.from)) {
3137
- const error = "Invalid 'from' address format";
3138
- logValidationError(error);
3139
- return isQaModeEnabled() ? null : error;
3140
- }
3185
+ if (data.from !== undefined && !isValidAddress(data.from)) {
3186
+ return "Invalid 'from' address format";
3141
3187
  }
3142
3188
  if (!isValidArray(data.messages) || data.messages.length === 0) {
3143
- const error = "'messages' is required";
3144
- logValidationError(error);
3145
- return isQaModeEnabled() ? null : error;
3189
+ return "'messages' is required";
3146
3190
  }
3147
3191
  for (let i = 0; i < data.messages.length; i++) {
3148
3192
  const message = data.messages[i];
@@ -3188,7 +3232,9 @@ function validateTransactionMessage(message, index) {
3188
3232
  return `Invalid 'extraCurrency' in message at index ${index}`;
3189
3233
  }
3190
3234
  for (const [key, value] of Object.entries(message.extraCurrency)) {
3191
- if (!INTEGER_REGEX.test(key) || typeof value !== 'string' || !POSITIVE_INTEGER_REGEX.test(value)) {
3235
+ if (!INTEGER_REGEX.test(key) ||
3236
+ typeof value !== 'string' ||
3237
+ !POSITIVE_INTEGER_REGEX.test(value)) {
3192
3238
  return `Invalid 'extraCurrency' format in message at index ${index}`;
3193
3239
  }
3194
3240
  }
@@ -3197,20 +3243,46 @@ function validateTransactionMessage(message, index) {
3197
3243
  }
3198
3244
  function validateConnectAdditionalRequest(data) {
3199
3245
  if (!isValidObject(data)) {
3200
- return "Request must be an object";
3246
+ return 'Request must be an object';
3201
3247
  }
3202
3248
  const allowedKeys = ['tonProof'];
3203
3249
  if (hasExtraProperties(data, allowedKeys)) {
3204
- return "Request contains extra properties";
3250
+ return 'Request contains extra properties';
3205
3251
  }
3206
- if (data.tonProof !== undefined && !isValidString(data.tonProof)) {
3207
- return "Invalid 'tonProof'";
3252
+ if (data.tonProof !== undefined) {
3253
+ if (typeof data.tonProof !== 'string') {
3254
+ return "Invalid 'tonProof'";
3255
+ }
3256
+ const payload = data.tonProof;
3257
+ if (payload.length === 0) {
3258
+ return "Empty 'tonProof' payload";
3259
+ }
3260
+ // Get current domain for validation first
3261
+ const domain = getDomain();
3262
+ if (!domain) {
3263
+ // In Node.js environment, skip domain validation
3264
+ return null;
3265
+ }
3266
+ // Validate domain size (max 128 bytes)
3267
+ const domainBytes = new TextEncoder().encode(domain).length;
3268
+ if (domainBytes > MAX_DOMAIN_BYTES) {
3269
+ return 'Current domain exceeds 128 bytes limit';
3270
+ }
3271
+ // Validate payload size (max 128 bytes)
3272
+ const payloadBytes = new TextEncoder().encode(payload).length;
3273
+ if (payloadBytes > MAX_PAYLOAD_BYTES) {
3274
+ return "'tonProof' payload exceeds 128 bytes limit";
3275
+ }
3276
+ // Validate total size (domain + payload <= 222 bytes)
3277
+ if (domainBytes + payloadBytes > MAX_TOTAL_BYTES) {
3278
+ return "'tonProof' domain + payload exceeds 222 bytes limit";
3279
+ }
3208
3280
  }
3209
3281
  return null;
3210
3282
  }
3211
3283
  function validateSignDataPayload(data) {
3212
3284
  if (!isValidObject(data)) {
3213
- return "Payload must be an object";
3285
+ return 'Payload must be an object';
3214
3286
  }
3215
3287
  if (!isValidString(data.type)) {
3216
3288
  return "'type' is required";
@@ -3229,17 +3301,17 @@ function validateSignDataPayload(data) {
3229
3301
  function validateSignDataPayloadText(data) {
3230
3302
  const allowedKeys = ['type', 'text', 'network', 'from'];
3231
3303
  if (hasExtraProperties(data, allowedKeys)) {
3232
- return "Text payload contains extra properties";
3304
+ return 'Text payload contains extra properties';
3233
3305
  }
3234
3306
  if (!isValidString(data.text)) {
3235
3307
  return "'text' is required";
3236
3308
  }
3237
3309
  if (data.network !== undefined) {
3238
- if (!isValidString(data.network) || !/^\d+$/.test(data.network)) {
3310
+ if (!isValidNetwork(data.network)) {
3239
3311
  return "Invalid 'network' format";
3240
3312
  }
3241
3313
  }
3242
- if (data.from !== undefined && !isValidString(data.from)) {
3314
+ if (data.from !== undefined && !isValidAddress(data.from)) {
3243
3315
  return "Invalid 'from'";
3244
3316
  }
3245
3317
  return null;
@@ -3247,17 +3319,17 @@ function validateSignDataPayloadText(data) {
3247
3319
  function validateSignDataPayloadBinary(data) {
3248
3320
  const allowedKeys = ['type', 'bytes', 'network', 'from'];
3249
3321
  if (hasExtraProperties(data, allowedKeys)) {
3250
- return "Binary payload contains extra properties";
3322
+ return 'Binary payload contains extra properties';
3251
3323
  }
3252
3324
  if (!isValidString(data.bytes)) {
3253
3325
  return "'bytes' is required";
3254
3326
  }
3255
3327
  if (data.network !== undefined) {
3256
- if (!isValidString(data.network) || !/^\d+$/.test(data.network)) {
3328
+ if (!isValidNetwork(data.network)) {
3257
3329
  return "Invalid 'network' format";
3258
3330
  }
3259
3331
  }
3260
- if (data.from !== undefined && !isValidString(data.from)) {
3332
+ if (data.from !== undefined && !isValidAddress(data.from)) {
3261
3333
  return "Invalid 'from'";
3262
3334
  }
3263
3335
  return null;
@@ -3265,7 +3337,7 @@ function validateSignDataPayloadBinary(data) {
3265
3337
  function validateSignDataPayloadCell(data) {
3266
3338
  const allowedKeys = ['type', 'schema', 'cell', 'network', 'from'];
3267
3339
  if (hasExtraProperties(data, allowedKeys)) {
3268
- return "Cell payload contains extra properties";
3340
+ return 'Cell payload contains extra properties';
3269
3341
  }
3270
3342
  if (!isValidString(data.schema)) {
3271
3343
  return "'schema' is required";
@@ -3277,15 +3349,95 @@ function validateSignDataPayloadCell(data) {
3277
3349
  return "Invalid 'cell' format (must be valid base64)";
3278
3350
  }
3279
3351
  if (data.network !== undefined) {
3280
- if (!isValidString(data.network) || !/^\d+$/.test(data.network)) {
3352
+ if (!isValidNetwork(data.network)) {
3281
3353
  return "Invalid 'network' format";
3282
3354
  }
3283
3355
  }
3284
- if (data.from !== undefined && !isValidString(data.from)) {
3356
+ if (data.from !== undefined && !isValidAddress(data.from)) {
3285
3357
  return "Invalid 'from'";
3286
3358
  }
3287
3359
  return null;
3288
3360
  }
3361
+ /**
3362
+ * Validates ton_proof item received from wallet in connect event.
3363
+ */
3364
+ // eslint-disable-next-line complexity
3365
+ function validateTonProofItemReply(data) {
3366
+ if (!isValidObject(data)) {
3367
+ return 'ton_proof item must be an object';
3368
+ }
3369
+ const allowedKeys = ['error', 'proof', 'name'];
3370
+ if (hasExtraProperties(data, allowedKeys)) {
3371
+ return 'ton_proof item contains extra properties';
3372
+ }
3373
+ const hasProof = Object.prototype.hasOwnProperty.call(data, 'proof');
3374
+ const hasError = Object.prototype.hasOwnProperty.call(data, 'error');
3375
+ if (!hasProof && !hasError) {
3376
+ return "'ton_proof' item must contain either 'proof' or 'error'";
3377
+ }
3378
+ if (hasProof && hasError) {
3379
+ return "'ton_proof' item must contain either 'proof' or 'error', not both";
3380
+ }
3381
+ if (hasProof) {
3382
+ const proof = data.proof;
3383
+ if (!isValidObject(proof)) {
3384
+ return "Invalid 'proof' object";
3385
+ }
3386
+ const allowedProofKeys = ['timestamp', 'domain', 'payload', 'signature'];
3387
+ if (hasExtraProperties(proof, allowedProofKeys)) {
3388
+ return 'ton_proof item contains extra properties';
3389
+ }
3390
+ if (!isValidNumber(proof.timestamp)) {
3391
+ return "Invalid 'proof.timestamp'";
3392
+ }
3393
+ const domain = proof.domain;
3394
+ if (!isValidObject(domain)) {
3395
+ return "Invalid 'proof.domain'";
3396
+ }
3397
+ if (!isValidNumber(domain.lengthBytes)) {
3398
+ return "Invalid 'proof.domain.lengthBytes'";
3399
+ }
3400
+ if (!isValidString(domain.value)) {
3401
+ return "Invalid 'proof.domain.value'";
3402
+ }
3403
+ // Try to verify that provided byte length matches actual byte length of value
3404
+ try {
3405
+ const encoderAvailable = typeof TextEncoder !== 'undefined';
3406
+ const actualLength = encoderAvailable
3407
+ ? new TextEncoder().encode(domain.value).length
3408
+ : domain.value.length;
3409
+ if (actualLength !== domain.lengthBytes) {
3410
+ return "'proof.domain.lengthBytes' does not match 'proof.domain.value'";
3411
+ }
3412
+ }
3413
+ catch (_a) {
3414
+ // Ignore environment issues; best-effort validation
3415
+ }
3416
+ if (!isValidString(proof.payload)) {
3417
+ return "Invalid 'proof.payload'";
3418
+ }
3419
+ if (!isValidString(proof.signature) || !BASE64_REGEX.test(proof.signature)) {
3420
+ return "Invalid 'proof.signature' format";
3421
+ }
3422
+ }
3423
+ if (hasError) {
3424
+ const error = data.error;
3425
+ if (!isValidObject(error)) {
3426
+ return "Invalid 'error' object";
3427
+ }
3428
+ const allowedErrorKeys = ['code', 'message'];
3429
+ if (hasExtraProperties(error, allowedErrorKeys)) {
3430
+ return 'ton_proof error contains extra properties';
3431
+ }
3432
+ if (!isValidNumber(error.code)) {
3433
+ return "Invalid 'error.code'";
3434
+ }
3435
+ if (!isValidString(error.message)) {
3436
+ return "Invalid 'error.message'";
3437
+ }
3438
+ }
3439
+ return null;
3440
+ }
3289
3441
 
3290
3442
  class TonConnect {
3291
3443
  /**
@@ -3609,6 +3761,34 @@ class TonConnect {
3609
3761
  prevAbortController === null || prevAbortController === void 0 ? void 0 : prevAbortController.abort();
3610
3762
  });
3611
3763
  }
3764
+ /**
3765
+ * Gets the current session ID if available.
3766
+ * @returns session ID string or null if not available.
3767
+ */
3768
+ getSessionId() {
3769
+ return __awaiter(this, void 0, void 0, function* () {
3770
+ if (!this.provider || !this.connected) {
3771
+ return null;
3772
+ }
3773
+ try {
3774
+ const connection = yield this.bridgeConnectionStorage.getConnection();
3775
+ if (!connection || connection.type === 'injected') {
3776
+ return null;
3777
+ }
3778
+ if ('sessionCrypto' in connection) {
3779
+ // Pending connection
3780
+ return connection.sessionCrypto.sessionId;
3781
+ }
3782
+ else {
3783
+ // Established connection
3784
+ return connection.session.sessionCrypto.sessionId;
3785
+ }
3786
+ }
3787
+ catch (_a) {
3788
+ return null;
3789
+ }
3790
+ });
3791
+ }
3612
3792
  /**
3613
3793
  * Pause bridge HTTP connection. Might be helpful, if you want to pause connections while browser tab is unfocused,
3614
3794
  * or if you use SDK with NodeJS and want to save server resources.
@@ -3698,44 +3878,59 @@ class TonConnect {
3698
3878
  }
3699
3879
  };
3700
3880
  if (tonProofItem) {
3881
+ const validationError = validateTonProofItemReply(tonProofItem);
3701
3882
  let tonProof = undefined;
3702
- try {
3703
- if ('proof' in tonProofItem) { // success
3704
- tonProof = {
3705
- name: 'ton_proof',
3706
- proof: {
3707
- timestamp: tonProofItem.proof.timestamp,
3708
- domain: {
3709
- lengthBytes: tonProofItem.proof.domain.lengthBytes,
3710
- value: tonProofItem.proof.domain.value,
3711
- },
3712
- payload: tonProofItem.proof.payload,
3713
- signature: tonProofItem.proof.signature,
3714
- }
3715
- };
3716
- }
3717
- else if ('error' in tonProofItem) { // error
3718
- tonProof = {
3719
- name: 'ton_proof',
3720
- error: {
3721
- code: tonProofItem.error.code,
3722
- message: tonProofItem.error.message,
3723
- }
3724
- };
3725
- }
3726
- else {
3727
- throw new TonConnectError('Invalid data format');
3883
+ if (validationError) {
3884
+ if (isQaModeEnabled()) {
3885
+ console.error('TonProofItem validation failed: ' + validationError);
3728
3886
  }
3729
- }
3730
- catch (e) {
3731
3887
  tonProof = {
3732
3888
  name: 'ton_proof',
3733
3889
  error: {
3734
3890
  code: CONNECT_ITEM_ERROR_CODES.UNKNOWN_ERROR,
3735
- message: 'Invalid data format'
3891
+ message: validationError
3736
3892
  }
3737
3893
  };
3738
3894
  }
3895
+ else {
3896
+ try {
3897
+ if ('proof' in tonProofItem) {
3898
+ tonProof = {
3899
+ name: 'ton_proof',
3900
+ proof: {
3901
+ timestamp: tonProofItem.proof.timestamp,
3902
+ domain: {
3903
+ lengthBytes: tonProofItem.proof.domain.lengthBytes,
3904
+ value: tonProofItem.proof.domain.value
3905
+ },
3906
+ payload: tonProofItem.proof.payload,
3907
+ signature: tonProofItem.proof.signature
3908
+ }
3909
+ };
3910
+ }
3911
+ else if ('error' in tonProofItem) {
3912
+ tonProof = {
3913
+ name: 'ton_proof',
3914
+ error: {
3915
+ code: tonProofItem.error.code,
3916
+ message: tonProofItem.error.message
3917
+ }
3918
+ };
3919
+ }
3920
+ else {
3921
+ throw new TonConnectError('Invalid data format');
3922
+ }
3923
+ }
3924
+ catch (e) {
3925
+ tonProof = {
3926
+ name: 'ton_proof',
3927
+ error: {
3928
+ code: CONNECT_ITEM_ERROR_CODES.UNKNOWN_ERROR,
3929
+ message: 'Invalid data format'
3930
+ }
3931
+ };
3932
+ }
3933
+ }
3739
3934
  wallet.connectItems = { tonProof };
3740
3935
  }
3741
3936
  this.wallet = wallet;