@tonconnect/sdk 3.3.0-beta.2 → 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 +319 -132
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/esm/index.mjs +316 -133
- package/lib/esm/index.mjs.map +1 -1
- package/lib/types/index.d.ts +33 -3
- package/package.json +2 -1
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
|
|
|
@@ -1473,22 +1473,6 @@ function getWindow() {
|
|
|
1473
1473
|
}
|
|
1474
1474
|
return window;
|
|
1475
1475
|
}
|
|
1476
|
-
/**
|
|
1477
|
-
* The function try to get window keys, if it is not available it returns empty array.
|
|
1478
|
-
* As an example, for Safari's private mode it returns empty array, because the browser does not allow to get window keys.
|
|
1479
|
-
*/
|
|
1480
|
-
function tryGetWindowKeys() {
|
|
1481
|
-
const window = getWindow();
|
|
1482
|
-
if (!window) {
|
|
1483
|
-
return [];
|
|
1484
|
-
}
|
|
1485
|
-
try {
|
|
1486
|
-
return Object.keys(window);
|
|
1487
|
-
}
|
|
1488
|
-
catch (_a) {
|
|
1489
|
-
return [];
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
1476
|
function getDocument() {
|
|
1493
1477
|
if (typeof document === 'undefined') {
|
|
1494
1478
|
return undefined;
|
|
@@ -1533,6 +1517,43 @@ function isLocalStorageAvailable() {
|
|
|
1533
1517
|
function isNodeJs() {
|
|
1534
1518
|
return (typeof process !== 'undefined' && process.versions != null && process.versions.node != null);
|
|
1535
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
|
+
}
|
|
1536
1557
|
|
|
1537
1558
|
class InjectedProvider {
|
|
1538
1559
|
static fromStorage(storage) {
|
|
@@ -1555,8 +1576,8 @@ class InjectedProvider {
|
|
|
1555
1576
|
if (!this.window) {
|
|
1556
1577
|
return [];
|
|
1557
1578
|
}
|
|
1558
|
-
const
|
|
1559
|
-
const wallets =
|
|
1579
|
+
const windowEntries = getWindowEntries();
|
|
1580
|
+
const wallets = windowEntries.filter(([_key, value]) => isJSBridgeWithMetadata(value));
|
|
1560
1581
|
return wallets.map(([jsBridgeKey, wallet]) => ({
|
|
1561
1582
|
name: wallet.tonconnect.walletInfo.name,
|
|
1562
1583
|
appName: wallet.tonconnect.walletInfo.app_name,
|
|
@@ -2102,11 +2123,6 @@ function enableQaMode() {
|
|
|
2102
2123
|
function isQaModeEnabled() {
|
|
2103
2124
|
return qaModeEnabled;
|
|
2104
2125
|
}
|
|
2105
|
-
function logValidationError(message) {
|
|
2106
|
-
if (isQaModeEnabled()) {
|
|
2107
|
-
console.error(`[QA Mode] Validation failed: ${message}`);
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
2126
|
function showQaModeBanner() {
|
|
2111
2127
|
if (typeof window === 'undefined')
|
|
2112
2128
|
return;
|
|
@@ -2207,8 +2223,8 @@ function startBannerObserver() {
|
|
|
2207
2223
|
class WalletsListManager {
|
|
2208
2224
|
constructor(options) {
|
|
2209
2225
|
var _a;
|
|
2210
|
-
this.
|
|
2211
|
-
this.
|
|
2226
|
+
this.walletsListDTOCache = null;
|
|
2227
|
+
this.walletsListDTOCacheCreationTimestamp = null;
|
|
2212
2228
|
if (isQaModeEnabled()) {
|
|
2213
2229
|
this.walletsListSource =
|
|
2214
2230
|
'https://raw.githubusercontent.com/ton-connect/wallets-list-staging/refs/heads/main/wallets-v2.json';
|
|
@@ -2221,23 +2237,11 @@ class WalletsListManager {
|
|
|
2221
2237
|
}
|
|
2222
2238
|
getWallets() {
|
|
2223
2239
|
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;
|
|
2240
|
+
const [walletsListDTO, currentlyInjectedWallets] = yield Promise.all([
|
|
2241
|
+
this.fetchWalletsListDTO(),
|
|
2242
|
+
this.getCurrentlyInjectedWallets()
|
|
2243
|
+
]);
|
|
2244
|
+
return this.mergeWalletsLists(this.walletConfigDTOListToWalletConfigList(walletsListDTO), currentlyInjectedWallets);
|
|
2241
2245
|
});
|
|
2242
2246
|
}
|
|
2243
2247
|
getEmbeddedWallet() {
|
|
@@ -2247,7 +2251,28 @@ class WalletsListManager {
|
|
|
2247
2251
|
return embeddedWallets.length === 1 ? embeddedWallets[0] : null;
|
|
2248
2252
|
});
|
|
2249
2253
|
}
|
|
2250
|
-
|
|
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() {
|
|
2251
2276
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2252
2277
|
let walletsList = [];
|
|
2253
2278
|
try {
|
|
@@ -2268,16 +2293,21 @@ class WalletsListManager {
|
|
|
2268
2293
|
logError(e);
|
|
2269
2294
|
walletsList = FALLBACK_WALLETS_LIST;
|
|
2270
2295
|
}
|
|
2271
|
-
|
|
2272
|
-
try {
|
|
2273
|
-
currentlyInjectedWallets = InjectedProvider.getCurrentlyInjectedWallets();
|
|
2274
|
-
}
|
|
2275
|
-
catch (e) {
|
|
2276
|
-
logError(e);
|
|
2277
|
-
}
|
|
2278
|
-
return this.mergeWalletsLists(this.walletConfigDTOListToWalletConfigList(walletsList), currentlyInjectedWallets);
|
|
2296
|
+
return walletsList;
|
|
2279
2297
|
});
|
|
2280
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
|
+
}
|
|
2281
2311
|
walletConfigDTOListToWalletConfigList(walletConfigDTO) {
|
|
2282
2312
|
return walletConfigDTO.map(walletConfigDTO => {
|
|
2283
2313
|
const walletConfig = {
|
|
@@ -2920,8 +2950,9 @@ class TonConnectTracker {
|
|
|
2920
2950
|
}
|
|
2921
2951
|
}
|
|
2922
2952
|
|
|
2923
|
-
const tonConnectSdkVersion = "3.3.0-beta.
|
|
2953
|
+
const tonConnectSdkVersion = "3.3.0-beta.3";
|
|
2924
2954
|
|
|
2955
|
+
const bounceableTag = 0x11;
|
|
2925
2956
|
const noBounceableTag = 0x51;
|
|
2926
2957
|
const testOnlyTag = 0x80;
|
|
2927
2958
|
/**
|
|
@@ -2991,25 +3022,40 @@ function parseUserFriendlyAddress(address) {
|
|
|
2991
3022
|
throw new WrongAddressError(`Invalid address length: ${address}`);
|
|
2992
3023
|
}
|
|
2993
3024
|
const addr = decoded.slice(0, 34);
|
|
2994
|
-
const checksum = decoded.slice(34);
|
|
3025
|
+
const checksum = decoded.slice(34, 36);
|
|
2995
3026
|
const calculatedChecksum = crc16(addr);
|
|
2996
3027
|
if (!checksum.every((byte, i) => byte === calculatedChecksum[i])) {
|
|
2997
3028
|
throw new WrongAddressError(`Invalid checksum in address: ${address}`);
|
|
2998
3029
|
}
|
|
2999
|
-
|
|
3000
|
-
|
|
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
|
+
}
|
|
3001
3049
|
const hex = addr.slice(2);
|
|
3002
3050
|
if (wc !== 0 && wc !== -1) {
|
|
3003
3051
|
throw new WrongAddressError(`Invalid workchain: ${wc}`);
|
|
3004
3052
|
}
|
|
3005
|
-
const testOnly = (tag & testOnlyTag) !== 0;
|
|
3006
|
-
const isBounceable = (tag & 0x40) !== 0;
|
|
3007
3053
|
return {
|
|
3008
3054
|
wc,
|
|
3009
3055
|
hex: Array.from(hex)
|
|
3010
3056
|
.map(b => b.toString(16).padStart(2, '0'))
|
|
3011
3057
|
.join(''),
|
|
3012
|
-
testOnly,
|
|
3058
|
+
testOnly: isTestOnly,
|
|
3013
3059
|
isBounceable
|
|
3014
3060
|
};
|
|
3015
3061
|
}
|
|
@@ -3086,12 +3132,21 @@ const BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{
|
|
|
3086
3132
|
const BOC_PREFIX = 'te6cc';
|
|
3087
3133
|
const INTEGER_REGEX = /^-?\d+$/;
|
|
3088
3134
|
const POSITIVE_INTEGER_REGEX = /^\d+$/;
|
|
3135
|
+
const MAX_DOMAIN_BYTES = 128;
|
|
3136
|
+
const MAX_PAYLOAD_BYTES = 128;
|
|
3137
|
+
const MAX_TOTAL_BYTES = 222;
|
|
3089
3138
|
function isValidNumber(value) {
|
|
3090
3139
|
return typeof value === 'number' && !isNaN(value);
|
|
3091
3140
|
}
|
|
3092
3141
|
function isValidString(value) {
|
|
3093
3142
|
return typeof value === 'string' && value.length > 0;
|
|
3094
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
|
+
}
|
|
3095
3150
|
function isValidBoc(value) {
|
|
3096
3151
|
return typeof value === 'string' && BASE64_REGEX.test(value) && value.startsWith(BOC_PREFIX);
|
|
3097
3152
|
}
|
|
@@ -3105,52 +3160,33 @@ function hasExtraProperties(obj, allowedKeys) {
|
|
|
3105
3160
|
return Object.keys(obj).some(key => !allowedKeys.includes(key));
|
|
3106
3161
|
}
|
|
3107
3162
|
function validateSendTransactionRequest(data) {
|
|
3108
|
-
// eslint-disable-next-line no-console
|
|
3109
|
-
console.log('[Validation Debug] validateSendTransactionRequest called');
|
|
3110
|
-
// eslint-disable-next-line no-console
|
|
3111
|
-
console.log('[Validation Debug] isQaModeEnabled():', isQaModeEnabled());
|
|
3112
3163
|
if (!isValidObject(data)) {
|
|
3113
|
-
|
|
3114
|
-
logValidationError(error);
|
|
3115
|
-
const shouldReturnNull = isQaModeEnabled();
|
|
3116
|
-
// eslint-disable-next-line no-console
|
|
3117
|
-
console.log('[Validation Debug] Should return null:', shouldReturnNull);
|
|
3118
|
-
return shouldReturnNull ? null : error;
|
|
3164
|
+
return 'Request must be an object';
|
|
3119
3165
|
}
|
|
3120
3166
|
const allowedKeys = ['validUntil', 'network', 'from', 'messages'];
|
|
3121
3167
|
if (hasExtraProperties(data, allowedKeys)) {
|
|
3122
|
-
|
|
3123
|
-
logValidationError(error);
|
|
3124
|
-
return isQaModeEnabled() ? null : error;
|
|
3125
|
-
}
|
|
3126
|
-
if (!isValidNumber(data.validUntil)) {
|
|
3127
|
-
const error = "Incorrect 'validUntil'";
|
|
3128
|
-
logValidationError(error);
|
|
3129
|
-
return isQaModeEnabled() ? null : error;
|
|
3168
|
+
return 'Request contains extra properties';
|
|
3130
3169
|
}
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
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
|
+
}
|
|
3135
3179
|
}
|
|
3136
3180
|
if (data.network !== undefined) {
|
|
3137
|
-
if (!
|
|
3138
|
-
|
|
3139
|
-
logValidationError(error);
|
|
3140
|
-
return isQaModeEnabled() ? null : error;
|
|
3181
|
+
if (!isValidNetwork(data.network)) {
|
|
3182
|
+
return "Invalid 'network' format";
|
|
3141
3183
|
}
|
|
3142
3184
|
}
|
|
3143
|
-
if (data.from !== undefined) {
|
|
3144
|
-
|
|
3145
|
-
const error = "Invalid 'from' address format";
|
|
3146
|
-
logValidationError(error);
|
|
3147
|
-
return isQaModeEnabled() ? null : error;
|
|
3148
|
-
}
|
|
3185
|
+
if (data.from !== undefined && !isValidAddress(data.from)) {
|
|
3186
|
+
return "Invalid 'from' address format";
|
|
3149
3187
|
}
|
|
3150
3188
|
if (!isValidArray(data.messages) || data.messages.length === 0) {
|
|
3151
|
-
|
|
3152
|
-
logValidationError(error);
|
|
3153
|
-
return isQaModeEnabled() ? null : error;
|
|
3189
|
+
return "'messages' is required";
|
|
3154
3190
|
}
|
|
3155
3191
|
for (let i = 0; i < data.messages.length; i++) {
|
|
3156
3192
|
const message = data.messages[i];
|
|
@@ -3213,8 +3249,34 @@ function validateConnectAdditionalRequest(data) {
|
|
|
3213
3249
|
if (hasExtraProperties(data, allowedKeys)) {
|
|
3214
3250
|
return 'Request contains extra properties';
|
|
3215
3251
|
}
|
|
3216
|
-
if (data.tonProof !== undefined
|
|
3217
|
-
|
|
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
|
+
}
|
|
3218
3280
|
}
|
|
3219
3281
|
return null;
|
|
3220
3282
|
}
|
|
@@ -3245,11 +3307,11 @@ function validateSignDataPayloadText(data) {
|
|
|
3245
3307
|
return "'text' is required";
|
|
3246
3308
|
}
|
|
3247
3309
|
if (data.network !== undefined) {
|
|
3248
|
-
if (!
|
|
3310
|
+
if (!isValidNetwork(data.network)) {
|
|
3249
3311
|
return "Invalid 'network' format";
|
|
3250
3312
|
}
|
|
3251
3313
|
}
|
|
3252
|
-
if (data.from !== undefined && !
|
|
3314
|
+
if (data.from !== undefined && !isValidAddress(data.from)) {
|
|
3253
3315
|
return "Invalid 'from'";
|
|
3254
3316
|
}
|
|
3255
3317
|
return null;
|
|
@@ -3263,11 +3325,11 @@ function validateSignDataPayloadBinary(data) {
|
|
|
3263
3325
|
return "'bytes' is required";
|
|
3264
3326
|
}
|
|
3265
3327
|
if (data.network !== undefined) {
|
|
3266
|
-
if (!
|
|
3328
|
+
if (!isValidNetwork(data.network)) {
|
|
3267
3329
|
return "Invalid 'network' format";
|
|
3268
3330
|
}
|
|
3269
3331
|
}
|
|
3270
|
-
if (data.from !== undefined && !
|
|
3332
|
+
if (data.from !== undefined && !isValidAddress(data.from)) {
|
|
3271
3333
|
return "Invalid 'from'";
|
|
3272
3334
|
}
|
|
3273
3335
|
return null;
|
|
@@ -3287,15 +3349,95 @@ function validateSignDataPayloadCell(data) {
|
|
|
3287
3349
|
return "Invalid 'cell' format (must be valid base64)";
|
|
3288
3350
|
}
|
|
3289
3351
|
if (data.network !== undefined) {
|
|
3290
|
-
if (!
|
|
3352
|
+
if (!isValidNetwork(data.network)) {
|
|
3291
3353
|
return "Invalid 'network' format";
|
|
3292
3354
|
}
|
|
3293
3355
|
}
|
|
3294
|
-
if (data.from !== undefined && !
|
|
3356
|
+
if (data.from !== undefined && !isValidAddress(data.from)) {
|
|
3295
3357
|
return "Invalid 'from'";
|
|
3296
3358
|
}
|
|
3297
3359
|
return null;
|
|
3298
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
|
+
}
|
|
3299
3441
|
|
|
3300
3442
|
class TonConnect {
|
|
3301
3443
|
/**
|
|
@@ -3619,6 +3761,34 @@ class TonConnect {
|
|
|
3619
3761
|
prevAbortController === null || prevAbortController === void 0 ? void 0 : prevAbortController.abort();
|
|
3620
3762
|
});
|
|
3621
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
|
+
}
|
|
3622
3792
|
/**
|
|
3623
3793
|
* Pause bridge HTTP connection. Might be helpful, if you want to pause connections while browser tab is unfocused,
|
|
3624
3794
|
* or if you use SDK with NodeJS and want to save server resources.
|
|
@@ -3708,46 +3878,59 @@ class TonConnect {
|
|
|
3708
3878
|
}
|
|
3709
3879
|
};
|
|
3710
3880
|
if (tonProofItem) {
|
|
3881
|
+
const validationError = validateTonProofItemReply(tonProofItem);
|
|
3711
3882
|
let tonProof = undefined;
|
|
3712
|
-
|
|
3713
|
-
if (
|
|
3714
|
-
|
|
3715
|
-
tonProof = {
|
|
3716
|
-
name: 'ton_proof',
|
|
3717
|
-
proof: {
|
|
3718
|
-
timestamp: tonProofItem.proof.timestamp,
|
|
3719
|
-
domain: {
|
|
3720
|
-
lengthBytes: tonProofItem.proof.domain.lengthBytes,
|
|
3721
|
-
value: tonProofItem.proof.domain.value
|
|
3722
|
-
},
|
|
3723
|
-
payload: tonProofItem.proof.payload,
|
|
3724
|
-
signature: tonProofItem.proof.signature
|
|
3725
|
-
}
|
|
3726
|
-
};
|
|
3727
|
-
}
|
|
3728
|
-
else if ('error' in tonProofItem) {
|
|
3729
|
-
// error
|
|
3730
|
-
tonProof = {
|
|
3731
|
-
name: 'ton_proof',
|
|
3732
|
-
error: {
|
|
3733
|
-
code: tonProofItem.error.code,
|
|
3734
|
-
message: tonProofItem.error.message
|
|
3735
|
-
}
|
|
3736
|
-
};
|
|
3737
|
-
}
|
|
3738
|
-
else {
|
|
3739
|
-
throw new TonConnectError('Invalid data format');
|
|
3883
|
+
if (validationError) {
|
|
3884
|
+
if (isQaModeEnabled()) {
|
|
3885
|
+
console.error('TonProofItem validation failed: ' + validationError);
|
|
3740
3886
|
}
|
|
3741
|
-
}
|
|
3742
|
-
catch (e) {
|
|
3743
3887
|
tonProof = {
|
|
3744
3888
|
name: 'ton_proof',
|
|
3745
3889
|
error: {
|
|
3746
3890
|
code: CONNECT_ITEM_ERROR_CODES.UNKNOWN_ERROR,
|
|
3747
|
-
message:
|
|
3891
|
+
message: validationError
|
|
3748
3892
|
}
|
|
3749
3893
|
};
|
|
3750
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
|
+
}
|
|
3751
3934
|
wallet.connectItems = { tonProof };
|
|
3752
3935
|
}
|
|
3753
3936
|
this.wallet = wallet;
|