@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/dist/tonconnect-sdk.min.js +1 -1
- package/dist/tonconnect-sdk.min.js.map +1 -1
- package/lib/cjs/index.cjs +345 -146
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/esm/index.mjs +337 -142
- package/lib/esm/index.mjs.map +1 -1
- package/lib/types/index.d.ts +33 -3
- package/package.json +10 -9
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 =
|
|
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 =
|
|
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
|
|
1560
|
-
const wallets =
|
|
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(
|
|
2180
|
-
mutations.forEach(
|
|
2197
|
+
bannerObserver = new MutationObserver(mutations => {
|
|
2198
|
+
mutations.forEach(mutation => {
|
|
2181
2199
|
if (mutation.type === 'childList') {
|
|
2182
|
-
mutation.removedNodes.forEach(
|
|
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.
|
|
2212
|
-
this.
|
|
2229
|
+
this.walletsListDTOCache = null;
|
|
2230
|
+
this.walletsListDTOCacheCreationTimestamp = null;
|
|
2213
2231
|
if (isQaModeEnabled()) {
|
|
2214
|
-
this.walletsListSource =
|
|
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
|
-
|
|
2225
|
-
this.
|
|
2226
|
-
|
|
2227
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
3000
|
-
|
|
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)
|
|
3010
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
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 (!
|
|
3133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) ||
|
|
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
|
|
3249
|
+
return 'Request must be an object';
|
|
3204
3250
|
}
|
|
3205
3251
|
const allowedKeys = ['tonProof'];
|
|
3206
3252
|
if (hasExtraProperties(data, allowedKeys)) {
|
|
3207
|
-
return
|
|
3253
|
+
return 'Request contains extra properties';
|
|
3208
3254
|
}
|
|
3209
|
-
if (data.tonProof !== undefined
|
|
3210
|
-
|
|
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
|
|
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
|
|
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 (!
|
|
3313
|
+
if (!isValidNetwork(data.network)) {
|
|
3242
3314
|
return "Invalid 'network' format";
|
|
3243
3315
|
}
|
|
3244
3316
|
}
|
|
3245
|
-
if (data.from !== undefined && !
|
|
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
|
|
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 (!
|
|
3331
|
+
if (!isValidNetwork(data.network)) {
|
|
3260
3332
|
return "Invalid 'network' format";
|
|
3261
3333
|
}
|
|
3262
3334
|
}
|
|
3263
|
-
if (data.from !== undefined && !
|
|
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
|
|
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 (!
|
|
3355
|
+
if (!isValidNetwork(data.network)) {
|
|
3284
3356
|
return "Invalid 'network' format";
|
|
3285
3357
|
}
|
|
3286
3358
|
}
|
|
3287
|
-
if (data.from !== undefined && !
|
|
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
|
-
|
|
3706
|
-
if (
|
|
3707
|
-
|
|
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:
|
|
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,
|
|
3989
|
+
Object.defineProperty(exports, "CHAIN", {
|
|
3795
3990
|
enumerable: true,
|
|
3796
3991
|
get: function () { return protocol.CHAIN; }
|
|
3797
3992
|
});
|
|
3798
|
-
Object.defineProperty(exports,
|
|
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,
|
|
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,
|
|
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,
|
|
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;
|