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