@tonconnect/sdk 3.4.0-beta.4 → 3.4.0-beta.6

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
@@ -919,180 +919,6 @@ function isExpiredPendingConnectionHttpRaw(connection) {
919
919
  return Date.now() - ((_a = connection.createdAt) !== null && _a !== void 0 ? _a : 0) > CONNECTION_HTTP_EXPIRATION_TIME;
920
920
  }
921
921
 
922
- class BridgeConnectionStorage {
923
- constructor(storage) {
924
- this.storage = storage;
925
- this.storeKey = 'ton-connect-storage_bridge-connection';
926
- }
927
- storeConnection(connection) {
928
- return __awaiter(this, void 0, void 0, function* () {
929
- if (connection.type === 'injected' || connection.type === 'wallet-connect') {
930
- return this.storage.setItem(this.storeKey, JSON.stringify(connection));
931
- }
932
- if (!isPendingConnectionHttp(connection)) {
933
- const rawSession = {
934
- sessionKeyPair: connection.session.sessionCrypto.stringifyKeypair(),
935
- walletPublicKey: connection.session.walletPublicKey,
936
- bridgeUrl: connection.session.bridgeUrl
937
- };
938
- const rawConnection = {
939
- type: 'http',
940
- connectEvent: connection.connectEvent,
941
- session: rawSession,
942
- lastWalletEventId: connection.lastWalletEventId,
943
- nextRpcRequestId: connection.nextRpcRequestId
944
- };
945
- return this.storage.setItem(this.storeKey, JSON.stringify(rawConnection));
946
- }
947
- const rawConnection = {
948
- type: 'http',
949
- connectionSource: connection.connectionSource,
950
- sessionCrypto: connection.sessionCrypto.stringifyKeypair(),
951
- createdAt: Date.now()
952
- };
953
- return this.storage.setItem(this.storeKey, JSON.stringify(rawConnection));
954
- });
955
- }
956
- removeConnection() {
957
- return __awaiter(this, void 0, void 0, function* () {
958
- return this.storage.removeItem(this.storeKey);
959
- });
960
- }
961
- getConnection() {
962
- return __awaiter(this, void 0, void 0, function* () {
963
- const stored = yield this.storage.getItem(this.storeKey);
964
- if (!stored) {
965
- return null;
966
- }
967
- const connection = JSON.parse(stored);
968
- if (connection.type === 'injected' || connection.type === 'wallet-connect') {
969
- return connection;
970
- }
971
- if (!isPendingConnectionHttpRaw(connection)) {
972
- const sessionCrypto = new protocol.SessionCrypto(connection.session.sessionKeyPair);
973
- return {
974
- type: 'http',
975
- connectEvent: connection.connectEvent,
976
- lastWalletEventId: connection.lastWalletEventId,
977
- nextRpcRequestId: connection.nextRpcRequestId,
978
- session: {
979
- sessionCrypto,
980
- bridgeUrl: connection.session.bridgeUrl,
981
- walletPublicKey: connection.session.walletPublicKey
982
- }
983
- };
984
- }
985
- if (isExpiredPendingConnectionHttpRaw(connection)) {
986
- yield this.removeConnection();
987
- return null;
988
- }
989
- return {
990
- type: 'http',
991
- sessionCrypto: new protocol.SessionCrypto(connection.sessionCrypto),
992
- connectionSource: connection.connectionSource
993
- };
994
- });
995
- }
996
- getHttpConnection() {
997
- return __awaiter(this, void 0, void 0, function* () {
998
- const connection = yield this.getConnection();
999
- if (!connection) {
1000
- throw new TonConnectError('Trying to read HTTP connection source while nothing is stored');
1001
- }
1002
- if (connection.type !== 'http') {
1003
- throw new TonConnectError(`Trying to read HTTP connection source while ${connection.type} connection is stored`);
1004
- }
1005
- return connection;
1006
- });
1007
- }
1008
- getHttpPendingConnection() {
1009
- return __awaiter(this, void 0, void 0, function* () {
1010
- const connection = yield this.getConnection();
1011
- if (!connection) {
1012
- throw new TonConnectError('Trying to read HTTP connection source while nothing is stored');
1013
- }
1014
- if (connection.type !== 'http') {
1015
- throw new TonConnectError(`Trying to read HTTP connection source while ${connection.type} connection is stored`);
1016
- }
1017
- if (!isPendingConnectionHttp(connection)) {
1018
- throw new TonConnectError('Trying to read HTTP-pending connection while http connection is stored');
1019
- }
1020
- return connection;
1021
- });
1022
- }
1023
- getInjectedConnection() {
1024
- return __awaiter(this, void 0, void 0, function* () {
1025
- const connection = yield this.getConnection();
1026
- if (!connection) {
1027
- throw new TonConnectError('Trying to read Injected bridge connection source while nothing is stored');
1028
- }
1029
- if ((connection === null || connection === void 0 ? void 0 : connection.type) !== 'injected') {
1030
- throw new TonConnectError(`Trying to read Injected bridge connection source while ${connection.type} connection is stored`);
1031
- }
1032
- return connection;
1033
- });
1034
- }
1035
- getWalletConnectConnection() {
1036
- return __awaiter(this, void 0, void 0, function* () {
1037
- const connection = yield this.getConnection();
1038
- if (!connection) {
1039
- throw new TonConnectError('Trying to read wallet connect bridge connection source while nothing is stored');
1040
- }
1041
- if ((connection === null || connection === void 0 ? void 0 : connection.type) !== 'wallet-connect') {
1042
- throw new TonConnectError(`Trying to read wallet connect bridge connection source while ${connection.type} connection is stored`);
1043
- }
1044
- return connection;
1045
- });
1046
- }
1047
- storedConnectionType() {
1048
- return __awaiter(this, void 0, void 0, function* () {
1049
- const stored = yield this.storage.getItem(this.storeKey);
1050
- if (!stored) {
1051
- return null;
1052
- }
1053
- const connection = JSON.parse(stored);
1054
- return connection.type;
1055
- });
1056
- }
1057
- storeLastWalletEventId(id) {
1058
- return __awaiter(this, void 0, void 0, function* () {
1059
- const connection = yield this.getConnection();
1060
- if (connection && connection.type === 'http' && !isPendingConnectionHttp(connection)) {
1061
- connection.lastWalletEventId = id;
1062
- return this.storeConnection(connection);
1063
- }
1064
- });
1065
- }
1066
- getLastWalletEventId() {
1067
- return __awaiter(this, void 0, void 0, function* () {
1068
- const connection = yield this.getConnection();
1069
- if (connection && 'lastWalletEventId' in connection) {
1070
- return connection.lastWalletEventId;
1071
- }
1072
- return undefined;
1073
- });
1074
- }
1075
- increaseNextRpcRequestId() {
1076
- return __awaiter(this, void 0, void 0, function* () {
1077
- const connection = yield this.getConnection();
1078
- if (connection && 'nextRpcRequestId' in connection) {
1079
- const lastId = connection.nextRpcRequestId || 0;
1080
- connection.nextRpcRequestId = lastId + 1;
1081
- return this.storeConnection(connection);
1082
- }
1083
- });
1084
- }
1085
- getNextRpcRequestId() {
1086
- return __awaiter(this, void 0, void 0, function* () {
1087
- const connection = yield this.getConnection();
1088
- if (connection && 'nextRpcRequestId' in connection) {
1089
- return connection.nextRpcRequestId || 0;
1090
- }
1091
- return 0;
1092
- });
1093
- }
1094
- }
1095
-
1096
922
  const PROTOCOL_VERSION = 2;
1097
923
 
1098
924
  /**
@@ -1262,20 +1088,45 @@ function v7Bytes(rnds, msecs, seq, buf, offset = 0) {
1262
1088
  return buf;
1263
1089
  }
1264
1090
 
1091
+ function waitForSome(promises, count) {
1092
+ return __awaiter(this, void 0, void 0, function* () {
1093
+ if (count <= 0)
1094
+ return [];
1095
+ if (count > promises.length) {
1096
+ throw new RangeError('count cannot be greater than the number of promises');
1097
+ }
1098
+ const results = new Array(promises.length);
1099
+ let settledCount = 0;
1100
+ return new Promise(resolve => {
1101
+ promises.forEach((p, index) => {
1102
+ Promise.resolve(p)
1103
+ .then(value => ({ status: 'fulfilled', value }))
1104
+ .catch(reason => ({ status: 'rejected', reason }))
1105
+ .then(result => {
1106
+ results[index] = result;
1107
+ settledCount++;
1108
+ if (settledCount === count) {
1109
+ resolve(results);
1110
+ }
1111
+ });
1112
+ });
1113
+ });
1114
+ });
1115
+ }
1116
+
1265
1117
  class BridgeProvider {
1266
1118
  static fromStorage(storage, analyticsManager) {
1267
1119
  return __awaiter(this, void 0, void 0, function* () {
1268
- const bridgeConnectionStorage = new BridgeConnectionStorage(storage);
1269
- const connection = yield bridgeConnectionStorage.getHttpConnection();
1120
+ const connection = yield storage.getHttpConnection();
1270
1121
  if (isPendingConnectionHttp(connection)) {
1271
1122
  return new BridgeProvider(storage, connection.connectionSource, analyticsManager);
1272
1123
  }
1273
1124
  return new BridgeProvider(storage, { bridgeUrl: connection.session.bridgeUrl }, analyticsManager);
1274
1125
  });
1275
1126
  }
1276
- constructor(storage, walletConnectionSource, analyticsManager) {
1127
+ constructor(connectionStorage, walletConnectionSource, analyticsManager) {
1277
1128
  var _a;
1278
- this.storage = storage;
1129
+ this.connectionStorage = connectionStorage;
1279
1130
  this.walletConnectionSource = walletConnectionSource;
1280
1131
  this.analyticsManager = analyticsManager;
1281
1132
  this.type = 'http';
@@ -1287,7 +1138,7 @@ class BridgeProvider {
1287
1138
  this.listeners = [];
1288
1139
  this.defaultOpeningDeadlineMS = 12000;
1289
1140
  this.defaultRetryTimeoutMS = 2000;
1290
- this.connectionStorage = new BridgeConnectionStorage(storage);
1141
+ this.optionalOpenGateways = 3;
1291
1142
  this.analytics = (_a = this.analyticsManager) === null || _a === void 0 ? void 0 : _a.scoped();
1292
1143
  }
1293
1144
  connect(message, options) {
@@ -1373,7 +1224,7 @@ class BridgeProvider {
1373
1224
  logDebug('Gateway is already opened, closing previous gateway');
1374
1225
  yield this.gateway.close();
1375
1226
  }
1376
- this.gateway = new BridgeGateway(this.storage, this.walletConnectionSource.bridgeUrl, storedConnection.session.sessionCrypto.sessionId, this.gatewayListener.bind(this), this.gatewayErrorsListener.bind(this), this.analyticsManager);
1227
+ this.gateway = new BridgeGateway(this.connectionStorage.storage, this.walletConnectionSource.bridgeUrl, storedConnection.session.sessionCrypto.sessionId, this.gatewayListener.bind(this), this.gatewayErrorsListener.bind(this), this.analyticsManager);
1377
1228
  if (abortController.signal.aborted) {
1378
1229
  return;
1379
1230
  }
@@ -1653,11 +1504,13 @@ class BridgeProvider {
1653
1504
  this.pendingGateways.map(bridge => bridge.close().catch());
1654
1505
  // open new gateways
1655
1506
  this.pendingGateways = this.walletConnectionSource.map(source => {
1656
- const gateway = new BridgeGateway(this.storage, source.bridgeUrl, sessionCrypto.sessionId, () => { }, () => { }, this.analyticsManager);
1507
+ const gateway = new BridgeGateway(this.connectionStorage.storage, source.bridgeUrl, sessionCrypto.sessionId, () => { }, () => { }, this.analyticsManager);
1657
1508
  gateway.setListener(message => this.pendingGatewaysListener(gateway, source.bridgeUrl, message));
1658
1509
  return gateway;
1659
1510
  });
1660
- yield Promise.allSettled(this.pendingGateways.map(bridge => callForSuccess((_options) => {
1511
+ // Wait until the specified optional gateways are opened, not necessarily all gateways
1512
+ const gatewaysToWaitFor = Math.max(this.pendingGateways.length - this.optionalOpenGateways, 1);
1513
+ yield waitForSome(this.pendingGateways.map(bridge => callForSuccess((_options) => {
1661
1514
  var _a;
1662
1515
  if (!this.pendingGateways.some(item => item === bridge)) {
1663
1516
  return bridge.close();
@@ -1671,7 +1524,7 @@ class BridgeProvider {
1671
1524
  attempts: Number.MAX_SAFE_INTEGER,
1672
1525
  delayMs: this.defaultRetryTimeoutMS,
1673
1526
  signal: options === null || options === void 0 ? void 0 : options.signal
1674
- })));
1527
+ })), gatewaysToWaitFor);
1675
1528
  return;
1676
1529
  }
1677
1530
  else {
@@ -1679,7 +1532,7 @@ class BridgeProvider {
1679
1532
  logDebug(`Gateway is already opened, closing previous gateway`);
1680
1533
  yield this.gateway.close();
1681
1534
  }
1682
- this.gateway = new BridgeGateway(this.storage, this.walletConnectionSource.bridgeUrl, sessionCrypto.sessionId, this.gatewayListener.bind(this), this.gatewayErrorsListener.bind(this), this.analyticsManager);
1535
+ this.gateway = new BridgeGateway(this.connectionStorage.storage, this.walletConnectionSource.bridgeUrl, sessionCrypto.sessionId, this.gatewayListener.bind(this), this.gatewayErrorsListener.bind(this), this.analyticsManager);
1683
1536
  return yield this.gateway.registerSession({
1684
1537
  openingDeadlineMS: options === null || options === void 0 ? void 0 : options.openingDeadlineMS,
1685
1538
  signal: options === null || options === void 0 ? void 0 : options.signal,
@@ -1866,8 +1719,7 @@ function getWindowEntries() {
1866
1719
  class InjectedProvider {
1867
1720
  static fromStorage(storage, analyticsManager) {
1868
1721
  return __awaiter(this, void 0, void 0, function* () {
1869
- const bridgeConnectionStorage = new BridgeConnectionStorage(storage);
1870
- const connection = yield bridgeConnectionStorage.getInjectedConnection();
1722
+ const connection = yield storage.getInjectedConnection();
1871
1723
  return new InjectedProvider(storage, connection.jsBridgeKey, analyticsManager);
1872
1724
  });
1873
1725
  }
@@ -1905,7 +1757,8 @@ class InjectedProvider {
1905
1757
  typeof window[injectedWalletKey] === 'object' &&
1906
1758
  'tonconnect' in window[injectedWalletKey]);
1907
1759
  }
1908
- constructor(storage, injectedWalletKey, analyticsManager) {
1760
+ constructor(connectionStorage, injectedWalletKey, analyticsManager) {
1761
+ this.connectionStorage = connectionStorage;
1909
1762
  this.injectedWalletKey = injectedWalletKey;
1910
1763
  this.type = 'injected';
1911
1764
  this.unsubscribeCallback = null;
@@ -1915,7 +1768,6 @@ class InjectedProvider {
1915
1768
  if (!InjectedProvider.isWindowContainsWallet(window, injectedWalletKey)) {
1916
1769
  throw new WalletNotInjectedError();
1917
1770
  }
1918
- this.connectionStorage = new BridgeConnectionStorage(storage);
1919
1771
  this.injectedWallet = window[injectedWalletKey].tonconnect;
1920
1772
  if (analyticsManager) {
1921
1773
  this.analytics = analyticsManager.scoped({
@@ -2131,6 +1983,205 @@ class InjectedProvider {
2131
1983
  }
2132
1984
  InjectedProvider.window = getWindow();
2133
1985
 
1986
+ class BridgeConnectionStorage {
1987
+ constructor(storage, walletsListManager) {
1988
+ this.storage = storage;
1989
+ this.walletsListManager = walletsListManager;
1990
+ this.storeKey = 'ton-connect-storage_bridge-connection';
1991
+ }
1992
+ storeConnection(connection) {
1993
+ return __awaiter(this, void 0, void 0, function* () {
1994
+ if (connection.type === 'injected' || connection.type === 'wallet-connect') {
1995
+ return this.storage.setItem(this.storeKey, JSON.stringify(connection));
1996
+ }
1997
+ if (!isPendingConnectionHttp(connection)) {
1998
+ const rawSession = {
1999
+ sessionKeyPair: connection.session.sessionCrypto.stringifyKeypair(),
2000
+ walletPublicKey: connection.session.walletPublicKey,
2001
+ bridgeUrl: connection.session.bridgeUrl
2002
+ };
2003
+ const rawConnection = {
2004
+ type: 'http',
2005
+ connectEvent: connection.connectEvent,
2006
+ session: rawSession,
2007
+ lastWalletEventId: connection.lastWalletEventId,
2008
+ nextRpcRequestId: connection.nextRpcRequestId
2009
+ };
2010
+ return this.storage.setItem(this.storeKey, JSON.stringify(rawConnection));
2011
+ }
2012
+ const rawConnection = {
2013
+ type: 'http',
2014
+ connectionSource: connection.connectionSource,
2015
+ sessionCrypto: connection.sessionCrypto.stringifyKeypair(),
2016
+ createdAt: Date.now()
2017
+ };
2018
+ return this.storage.setItem(this.storeKey, JSON.stringify(rawConnection));
2019
+ });
2020
+ }
2021
+ removeConnection() {
2022
+ return __awaiter(this, void 0, void 0, function* () {
2023
+ return this.storage.removeItem(this.storeKey);
2024
+ });
2025
+ }
2026
+ getConnection() {
2027
+ return __awaiter(this, void 0, void 0, function* () {
2028
+ try {
2029
+ const stored = yield this.storage.getItem(this.storeKey);
2030
+ if (!stored) {
2031
+ return null;
2032
+ }
2033
+ const connection = JSON.parse(stored);
2034
+ if (connection.type === 'injected' || connection.type === 'wallet-connect') {
2035
+ return connection;
2036
+ }
2037
+ if (!isPendingConnectionHttpRaw(connection)) {
2038
+ const sessionCrypto = new protocol.SessionCrypto(connection.session.sessionKeyPair);
2039
+ return yield this.actualizeBridgeConnection({
2040
+ type: 'http',
2041
+ connectEvent: connection.connectEvent,
2042
+ lastWalletEventId: connection.lastWalletEventId,
2043
+ nextRpcRequestId: connection.nextRpcRequestId,
2044
+ session: {
2045
+ sessionCrypto,
2046
+ bridgeUrl: connection.session.bridgeUrl,
2047
+ walletPublicKey: connection.session.walletPublicKey
2048
+ }
2049
+ });
2050
+ }
2051
+ if (isExpiredPendingConnectionHttpRaw(connection)) {
2052
+ yield this.removeConnection();
2053
+ return null;
2054
+ }
2055
+ return {
2056
+ type: 'http',
2057
+ sessionCrypto: new protocol.SessionCrypto(connection.sessionCrypto),
2058
+ connectionSource: connection.connectionSource
2059
+ };
2060
+ }
2061
+ catch (err) {
2062
+ logDebug('Error retrieving connection', err);
2063
+ return null;
2064
+ }
2065
+ });
2066
+ }
2067
+ getHttpConnection() {
2068
+ return __awaiter(this, void 0, void 0, function* () {
2069
+ const connection = yield this.getConnection();
2070
+ if (!connection) {
2071
+ throw new TonConnectError('Trying to read HTTP connection source while nothing is stored');
2072
+ }
2073
+ if (connection.type !== 'http') {
2074
+ throw new TonConnectError(`Trying to read HTTP connection source while ${connection.type} connection is stored`);
2075
+ }
2076
+ return connection;
2077
+ });
2078
+ }
2079
+ getHttpPendingConnection() {
2080
+ return __awaiter(this, void 0, void 0, function* () {
2081
+ const connection = yield this.getConnection();
2082
+ if (!connection) {
2083
+ throw new TonConnectError('Trying to read HTTP connection source while nothing is stored');
2084
+ }
2085
+ if (connection.type !== 'http') {
2086
+ throw new TonConnectError(`Trying to read HTTP connection source while ${connection.type} connection is stored`);
2087
+ }
2088
+ if (!isPendingConnectionHttp(connection)) {
2089
+ throw new TonConnectError('Trying to read HTTP-pending connection while http connection is stored');
2090
+ }
2091
+ return connection;
2092
+ });
2093
+ }
2094
+ getInjectedConnection() {
2095
+ return __awaiter(this, void 0, void 0, function* () {
2096
+ const connection = yield this.getConnection();
2097
+ if (!connection) {
2098
+ throw new TonConnectError('Trying to read Injected bridge connection source while nothing is stored');
2099
+ }
2100
+ if ((connection === null || connection === void 0 ? void 0 : connection.type) !== 'injected') {
2101
+ throw new TonConnectError(`Trying to read Injected bridge connection source while ${connection.type} connection is stored`);
2102
+ }
2103
+ return connection;
2104
+ });
2105
+ }
2106
+ getWalletConnectConnection() {
2107
+ return __awaiter(this, void 0, void 0, function* () {
2108
+ const connection = yield this.getConnection();
2109
+ if (!connection) {
2110
+ throw new TonConnectError('Trying to read wallet connect bridge connection source while nothing is stored');
2111
+ }
2112
+ if ((connection === null || connection === void 0 ? void 0 : connection.type) !== 'wallet-connect') {
2113
+ throw new TonConnectError(`Trying to read wallet connect bridge connection source while ${connection.type} connection is stored`);
2114
+ }
2115
+ return connection;
2116
+ });
2117
+ }
2118
+ storedConnectionType() {
2119
+ return __awaiter(this, void 0, void 0, function* () {
2120
+ const stored = yield this.storage.getItem(this.storeKey);
2121
+ if (!stored) {
2122
+ return null;
2123
+ }
2124
+ const connection = JSON.parse(stored);
2125
+ return connection.type;
2126
+ });
2127
+ }
2128
+ storeLastWalletEventId(id) {
2129
+ return __awaiter(this, void 0, void 0, function* () {
2130
+ const connection = yield this.getConnection();
2131
+ if (connection && connection.type === 'http' && !isPendingConnectionHttp(connection)) {
2132
+ connection.lastWalletEventId = id;
2133
+ return this.storeConnection(connection);
2134
+ }
2135
+ });
2136
+ }
2137
+ getLastWalletEventId() {
2138
+ return __awaiter(this, void 0, void 0, function* () {
2139
+ const connection = yield this.getConnection();
2140
+ if (connection && 'lastWalletEventId' in connection) {
2141
+ return connection.lastWalletEventId;
2142
+ }
2143
+ return undefined;
2144
+ });
2145
+ }
2146
+ increaseNextRpcRequestId() {
2147
+ return __awaiter(this, void 0, void 0, function* () {
2148
+ const connection = yield this.getConnection();
2149
+ if (connection && 'nextRpcRequestId' in connection) {
2150
+ const lastId = connection.nextRpcRequestId || 0;
2151
+ connection.nextRpcRequestId = lastId + 1;
2152
+ return this.storeConnection(connection);
2153
+ }
2154
+ });
2155
+ }
2156
+ getNextRpcRequestId() {
2157
+ return __awaiter(this, void 0, void 0, function* () {
2158
+ const connection = yield this.getConnection();
2159
+ if (connection && 'nextRpcRequestId' in connection) {
2160
+ return connection.nextRpcRequestId || 0;
2161
+ }
2162
+ return 0;
2163
+ });
2164
+ }
2165
+ actualizeBridgeConnection(connection) {
2166
+ return __awaiter(this, void 0, void 0, function* () {
2167
+ try {
2168
+ const appName = connection.connectEvent.payload.device.appName;
2169
+ const wallet = yield this.walletsListManager.getRemoteWallet(appName);
2170
+ if (wallet.bridgeUrl === connection.session.bridgeUrl) {
2171
+ return connection;
2172
+ }
2173
+ const actualizedConnection = Object.assign(Object.assign({}, connection), { session: Object.assign(Object.assign({}, connection.session), { bridgeUrl: wallet.bridgeUrl }) });
2174
+ yield this.storeConnection(actualizedConnection);
2175
+ return actualizedConnection;
2176
+ }
2177
+ catch (error) {
2178
+ logDebug('Failed to actualize bridge connection', error);
2179
+ return connection;
2180
+ }
2181
+ });
2182
+ }
2183
+ }
2184
+
2134
2185
  /**
2135
2186
  * Default storage to save protocol data, uses `localStorage` if it is available. In Safari's private mode, it uses `InMemoryStorage`. In Node.js, it throws an error.
2136
2187
  */
@@ -3081,6 +3132,7 @@ class WalletsListManager {
3081
3132
  (_a = options === null || options === void 0 ? void 0 : options.walletsListSource) !== null && _a !== void 0 ? _a : 'https://config.ton.org/wallets-v2.json';
3082
3133
  }
3083
3134
  this.cacheTTLMs = options === null || options === void 0 ? void 0 : options.cacheTTLMs;
3135
+ this.onDownloadDurationMeasured = options === null || options === void 0 ? void 0 : options.onDownloadDurationMeasured;
3084
3136
  }
3085
3137
  getWallets() {
3086
3138
  return __awaiter(this, void 0, void 0, function* () {
@@ -3119,9 +3171,24 @@ class WalletsListManager {
3119
3171
  return this.walletsListDTOCache;
3120
3172
  });
3121
3173
  }
3174
+ getRemoteWallet(appName) {
3175
+ return __awaiter(this, void 0, void 0, function* () {
3176
+ const walletsList = yield this.getWallets();
3177
+ const wallet = walletsList.find(wallet => wallet.appName === appName);
3178
+ if (!wallet) {
3179
+ throw new TonConnectError(`Wallet info not found for appName: "${appName}"`);
3180
+ }
3181
+ if (!isWalletInfoRemote(wallet)) {
3182
+ throw new TonConnectError(`Wallet "${appName}" is not remote`);
3183
+ }
3184
+ return wallet;
3185
+ });
3186
+ }
3122
3187
  fetchWalletsListFromSource() {
3123
3188
  return __awaiter(this, void 0, void 0, function* () {
3189
+ var _a, _b;
3124
3190
  let walletsList = [];
3191
+ const startTime = performance.now();
3125
3192
  try {
3126
3193
  const walletsResponse = yield fetch(this.walletsListSource);
3127
3194
  walletsList = yield walletsResponse.json();
@@ -3135,10 +3202,14 @@ class WalletsListManager {
3135
3202
  .join(', ')} config format is wrong. They were removed from the wallets list.`);
3136
3203
  walletsList = walletsList.filter(wallet => this.isCorrectWalletConfigDTO(wallet));
3137
3204
  }
3205
+ const endTime = performance.now();
3206
+ const duration = Math.round(endTime - startTime);
3207
+ (_a = this.onDownloadDurationMeasured) === null || _a === void 0 ? void 0 : _a.call(this, duration);
3138
3208
  }
3139
3209
  catch (e) {
3140
3210
  logError(e);
3141
3211
  walletsList = FALLBACK_WALLETS_LIST;
3212
+ (_b = this.onDownloadDurationMeasured) === null || _b === void 0 ? void 0 : _b.call(this, undefined);
3142
3213
  }
3143
3214
  return walletsList;
3144
3215
  });
@@ -3809,7 +3880,7 @@ class TonConnectTracker {
3809
3880
  }
3810
3881
  }
3811
3882
 
3812
- const tonConnectSdkVersion = "3.4.0-beta.2";
3883
+ const tonConnectSdkVersion = "3.4.0-beta.6";
3813
3884
 
3814
3885
  const bounceableTag = 0x11;
3815
3886
  const noBounceableTag = 0x51;
@@ -4248,10 +4319,6 @@ function validateTonProofItemReply(data) {
4248
4319
  if (!isValidObject(proof)) {
4249
4320
  return "Invalid 'proof' object";
4250
4321
  }
4251
- const allowedProofKeys = ['timestamp', 'domain', 'payload', 'signature'];
4252
- if (hasExtraProperties(proof, allowedProofKeys)) {
4253
- return 'ton_proof item contains extra properties';
4254
- }
4255
4322
  if (!isValidNumber(proof.timestamp)) {
4256
4323
  return "Invalid 'proof.timestamp'";
4257
4324
  }
@@ -4324,10 +4391,61 @@ function normalizeBase64(data) {
4324
4391
  function pascalToKebab(value) {
4325
4392
  return value.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
4326
4393
  }
4394
+ /**
4395
+ * Collects static connection metrics from the browser's Performance API.
4396
+ * TTFB is measured once at page load and doesn't change.
4397
+ * @returns An object containing static connection metrics (TTFB) or empty object if not available.
4398
+ */
4399
+ function getStaticConnectionMetrics() {
4400
+ const metrics = {};
4401
+ // Get TTFB from Navigation Timing API (static, measured once at page load)
4402
+ try {
4403
+ const navEntries = performance.getEntriesByType('navigation');
4404
+ if (navEntries.length > 0) {
4405
+ const nav = navEntries[0];
4406
+ if (nav.responseStart && nav.requestStart) {
4407
+ metrics.conn_ttfb = Math.round(nav.responseStart - nav.requestStart);
4408
+ }
4409
+ }
4410
+ }
4411
+ catch (e) {
4412
+ // Performance API not available or error occurred
4413
+ }
4414
+ return metrics;
4415
+ }
4416
+ /**
4417
+ * Collects dynamic connection metrics from the browser's Network Information API.
4418
+ * @returns An object containing dynamic connection metrics (RTT, network type) or empty object if not available.
4419
+ */
4420
+ function getDynamicConnectionMetrics() {
4421
+ const metrics = {};
4422
+ // Get RTT and network type from Network Information API (dynamic, can change)
4423
+ try {
4424
+ const navigatorWithConnection = navigator;
4425
+ const connection = navigatorWithConnection.connection ||
4426
+ navigatorWithConnection.mozConnection ||
4427
+ navigatorWithConnection.webkitConnection;
4428
+ if (connection) {
4429
+ if (connection.rtt !== undefined) {
4430
+ metrics.conn_rtt = connection.rtt;
4431
+ }
4432
+ if (connection.effectiveType) {
4433
+ metrics.conn_network_type = connection.effectiveType;
4434
+ }
4435
+ else if (connection.type) {
4436
+ metrics.conn_network_type = connection.type;
4437
+ }
4438
+ }
4439
+ }
4440
+ catch (e) {
4441
+ // Network Information API not available or error occurred
4442
+ }
4443
+ return metrics;
4444
+ }
4327
4445
 
4328
4446
  class AnalyticsManager {
4329
4447
  constructor(options = {}) {
4330
- var _a, _b, _c, _d, _e;
4448
+ var _a, _b, _c, _d, _e, _f;
4331
4449
  this.events = [];
4332
4450
  this.timeoutId = null;
4333
4451
  this.isProcessing = false;
@@ -4337,12 +4455,8 @@ class AnalyticsManager {
4337
4455
  this.currentBatchTimeoutMs = this.batchTimeoutMs;
4338
4456
  this.maxBatchSize = (_b = options.maxBatchSize) !== null && _b !== void 0 ? _b : 100;
4339
4457
  this.analyticsUrl = (_c = options.analyticsUrl) !== null && _c !== void 0 ? _c : 'https://analytics.ton.org/events';
4340
- this.enabled = (_d = options.enabled) !== null && _d !== void 0 ? _d : true;
4341
- this.baseEvent = {
4342
- subsystem: 'dapp-sdk',
4343
- version: tonConnectSdkVersion,
4344
- client_environment: (_e = options.environment) === null || _e === void 0 ? void 0 : _e.getClientEnvironment()
4345
- };
4458
+ this.mode = (_d = options.mode) !== null && _d !== void 0 ? _d : 'telemetry';
4459
+ this.baseEvent = Object.assign({ subsystem: 'dapp-sdk', version: tonConnectSdkVersion, client_environment: (_f = (_e = options.environment) === null || _e === void 0 ? void 0 : _e.getClientEnvironment) === null || _f === void 0 ? void 0 : _f.call(_e) }, getStaticConnectionMetrics());
4346
4460
  this.addWindowFocusAndBlurSubscriptions();
4347
4461
  }
4348
4462
  scoped(sharedData) {
@@ -4367,15 +4481,17 @@ class AnalyticsManager {
4367
4481
  }
4368
4482
  emit(event) {
4369
4483
  var _a;
4370
- if (!this.enabled) {
4484
+ if (this.mode === 'off') {
4371
4485
  return;
4372
4486
  }
4373
4487
  const traceId = (_a = event.trace_id) !== null && _a !== void 0 ? _a : UUIDv7();
4374
- const enhancedEvent = Object.assign(Object.assign(Object.assign({}, this.baseEvent), event), { event_id: UUIDv7(), client_timestamp: Math.floor(Date.now() / 1000), trace_id: traceId });
4488
+ const dynamicMetrics = getDynamicConnectionMetrics();
4489
+ const enhancedEvent = Object.assign(Object.assign(Object.assign(Object.assign({}, this.baseEvent), dynamicMetrics), event), { event_id: UUIDv7(), client_timestamp: Math.floor(Date.now() / 1000), trace_id: traceId });
4490
+ const filteredEvent = this.mode === 'telemetry' ? this.filterFullModeFields(enhancedEvent) : enhancedEvent;
4375
4491
  if (isQaModeEnabled()) {
4376
- logDebug(enhancedEvent);
4492
+ logDebug(filteredEvent);
4377
4493
  }
4378
- this.events.push(enhancedEvent);
4494
+ this.events.push(filteredEvent);
4379
4495
  if (this.events.length >= this.maxBatchSize) {
4380
4496
  void this.flush();
4381
4497
  return;
@@ -4517,15 +4633,33 @@ class AnalyticsManager {
4517
4633
  logError('Cannot subscribe to the document.visibilitychange: ', e);
4518
4634
  }
4519
4635
  }
4520
- setEnabled(enabled) {
4521
- this.enabled = enabled;
4522
- }
4523
- isEnabled() {
4524
- return this.enabled;
4636
+ getMode() {
4637
+ return this.mode;
4525
4638
  }
4526
4639
  getPendingEventsCount() {
4527
4640
  return this.events.length;
4528
4641
  }
4642
+ filterFullModeFields(event) {
4643
+ const filtered = Object.assign({}, event);
4644
+ for (const field of AnalyticsManager.FULL_MODE_FIELDS) {
4645
+ delete filtered[field];
4646
+ }
4647
+ // wallet_address is kept for error events, removed for non-error events
4648
+ const eventName = 'event_name' in event ? String(event.event_name) : '';
4649
+ const isErrorEvent = 'error_code' in event ||
4650
+ 'error_message' in event ||
4651
+ eventName.includes('error') ||
4652
+ eventName === 'connection-error' ||
4653
+ eventName === 'transaction-signing-failed' ||
4654
+ eventName === 'sign-data-request-failed';
4655
+ if (!isErrorEvent && 'wallet_address' in filtered) {
4656
+ delete filtered.wallet_address;
4657
+ }
4658
+ return filtered;
4659
+ }
4660
+ setWalletListDownloadDuration(duration) {
4661
+ this.baseEvent = Object.assign(Object.assign({}, this.baseEvent), { wallet_list_download_duration: duration });
4662
+ }
4529
4663
  }
4530
4664
  AnalyticsManager.HTTP_STATUS = {
4531
4665
  TOO_MANY_REQUESTS: 429,
@@ -4534,6 +4668,12 @@ AnalyticsManager.HTTP_STATUS = {
4534
4668
  };
4535
4669
  AnalyticsManager.MAX_BACKOFF_ATTEMPTS = 5;
4536
4670
  AnalyticsManager.BACKOFF_MULTIPLIER = 2;
4671
+ AnalyticsManager.FULL_MODE_FIELDS = [
4672
+ 'user_id',
4673
+ 'tg_id',
4674
+ 'locale',
4675
+ 'tma_is_premium'
4676
+ ];
4537
4677
 
4538
4678
  /**
4539
4679
  * A concrete implementation of EventDispatcher that dispatches events to the browser window.
@@ -4761,11 +4901,11 @@ function getWalletConnectOptions() {
4761
4901
  const DEFAULT_REQUEST_ID = '0';
4762
4902
  const DEFAULT_EVENT_ID = 0;
4763
4903
  class WalletConnectProvider {
4764
- constructor(storage) {
4904
+ constructor(connectionStorage) {
4905
+ this.connectionStorage = connectionStorage;
4765
4906
  this.type = 'injected';
4766
4907
  this.listeners = [];
4767
4908
  this.connector = undefined;
4768
- this.connectionStorage = new BridgeConnectionStorage(storage);
4769
4909
  const { projectId, metadata } = getWalletConnectOptions();
4770
4910
  this.config = {
4771
4911
  networks: [
@@ -5251,8 +5391,7 @@ class TonConnect {
5251
5391
  this.statusChangeSubscriptions.forEach(callback => callback(this._wallet));
5252
5392
  }
5253
5393
  constructor(options) {
5254
- var _a, _b;
5255
- this.walletsList = new WalletsListManager();
5394
+ var _a, _b, _c;
5256
5395
  this._wallet = null;
5257
5396
  this.provider = null;
5258
5397
  this.statusChangeSubscriptions = [];
@@ -5263,32 +5402,28 @@ class TonConnect {
5263
5402
  storage: (options === null || options === void 0 ? void 0 : options.storage) || new DefaultStorage()
5264
5403
  };
5265
5404
  this.walletsRequiredFeatures = options === null || options === void 0 ? void 0 : options.walletsRequiredFeatures;
5405
+ this.environment = (_a = options === null || options === void 0 ? void 0 : options.environment) !== null && _a !== void 0 ? _a : new DefaultEnvironment();
5406
+ // TODO: in production ready make flag to enable them?
5407
+ this.analytics = new AnalyticsManager({ environment: this.environment });
5266
5408
  this.walletsList = new WalletsListManager({
5267
5409
  walletsListSource: options === null || options === void 0 ? void 0 : options.walletsListSource,
5268
- cacheTTLMs: options === null || options === void 0 ? void 0 : options.walletsListCacheTTLMs
5410
+ cacheTTLMs: options === null || options === void 0 ? void 0 : options.walletsListCacheTTLMs,
5411
+ onDownloadDurationMeasured: (duration) => {
5412
+ var _a;
5413
+ (_a = this.analytics) === null || _a === void 0 ? void 0 : _a.setWalletListDownloadDuration(duration);
5414
+ }
5269
5415
  });
5270
- const eventDispatcher = (_a = options === null || options === void 0 ? void 0 : options.eventDispatcher) !== null && _a !== void 0 ? _a : new BrowserEventDispatcher();
5416
+ const eventDispatcher = (_b = options === null || options === void 0 ? void 0 : options.eventDispatcher) !== null && _b !== void 0 ? _b : new BrowserEventDispatcher();
5271
5417
  this.tracker = new TonConnectTracker({
5272
5418
  eventDispatcher,
5273
5419
  tonConnectSdkVersion: tonConnectSdkVersion
5274
5420
  });
5275
- this.environment = (_b = options === null || options === void 0 ? void 0 : options.environment) !== null && _b !== void 0 ? _b : new DefaultEnvironment();
5276
- // TODO: in production ready make flag to enable them?
5277
- this.analytics = new AnalyticsManager({ environment: this.environment });
5278
- const telegramUser = this.environment.getTelegramUser();
5279
- bindEventsTo(eventDispatcher, this.analytics.scoped({
5280
- locale: this.environment.getLocale(),
5281
- browser: this.environment.getBrowser(),
5282
- platform: this.environment.getPlatform(),
5283
- tg_id: telegramUser === null || telegramUser === void 0 ? void 0 : telegramUser.id,
5284
- tma_is_premium: telegramUser === null || telegramUser === void 0 ? void 0 : telegramUser.isPremium,
5285
- manifest_json_url: manifestUrl,
5286
- origin_url: getOriginWithPath
5287
- }));
5421
+ this.environment = (_c = options === null || options === void 0 ? void 0 : options.environment) !== null && _c !== void 0 ? _c : new DefaultEnvironment();
5422
+ this.initAnalytics(manifestUrl, eventDispatcher, options);
5288
5423
  if (!this.dappSettings.manifestUrl) {
5289
5424
  throw new DappMetadataError('Dapp tonconnect-manifest.json must be specified if window.location.origin is undefined. See more https://github.com/ton-connect/docs/blob/main/requests-responses.md#app-manifest');
5290
5425
  }
5291
- this.bridgeConnectionStorage = new BridgeConnectionStorage(this.dappSettings.storage);
5426
+ this.bridgeConnectionStorage = new BridgeConnectionStorage(this.dappSettings.storage, this.walletsList);
5292
5427
  if (!(options === null || options === void 0 ? void 0 : options.disableAutoPauseConnection)) {
5293
5428
  this.addWindowFocusAndBlurSubscriptions();
5294
5429
  }
@@ -5400,13 +5535,13 @@ class TonConnect {
5400
5535
  try {
5401
5536
  switch (bridgeConnectionType) {
5402
5537
  case 'http':
5403
- provider = yield BridgeProvider.fromStorage(this.dappSettings.storage, this.analytics);
5538
+ provider = yield BridgeProvider.fromStorage(this.bridgeConnectionStorage, this.analytics);
5404
5539
  break;
5405
5540
  case 'injected':
5406
- provider = yield InjectedProvider.fromStorage(this.dappSettings.storage, this.analytics);
5541
+ provider = yield InjectedProvider.fromStorage(this.bridgeConnectionStorage, this.analytics);
5407
5542
  break;
5408
5543
  case 'wallet-connect':
5409
- provider = yield WalletConnectProvider.fromStorage(this.dappSettings.storage);
5544
+ provider = yield WalletConnectProvider.fromStorage(this.bridgeConnectionStorage);
5410
5545
  break;
5411
5546
  default:
5412
5547
  if (embeddedWallet) {
@@ -5716,16 +5851,42 @@ class TonConnect {
5716
5851
  logError('Cannot subscribe to the document.visibilitychange: ', e);
5717
5852
  }
5718
5853
  }
5854
+ initAnalytics(manifestUrl, eventDispatcher, options) {
5855
+ var _a;
5856
+ const analyticsSettings = options === null || options === void 0 ? void 0 : options.analytics;
5857
+ const mode = (_a = analyticsSettings === null || analyticsSettings === void 0 ? void 0 : analyticsSettings.mode) !== null && _a !== void 0 ? _a : 'telemetry';
5858
+ if (mode === 'off') {
5859
+ return;
5860
+ }
5861
+ const analytics = new AnalyticsManager({
5862
+ environment: this.environment,
5863
+ mode
5864
+ });
5865
+ this.analytics = analytics;
5866
+ const telegramUser = this.environment.getTelegramUser();
5867
+ const sharedAnalyticsData = {
5868
+ browser: this.environment.getBrowser(),
5869
+ platform: this.environment.getPlatform(),
5870
+ manifest_json_url: manifestUrl,
5871
+ origin_url: getOriginWithPath,
5872
+ locale: this.environment.getLocale()
5873
+ };
5874
+ if (telegramUser) {
5875
+ sharedAnalyticsData.tg_id = telegramUser.id;
5876
+ sharedAnalyticsData.tma_is_premium = telegramUser.isPremium;
5877
+ }
5878
+ bindEventsTo(eventDispatcher, analytics.scoped(sharedAnalyticsData));
5879
+ }
5719
5880
  createProvider(wallet) {
5720
5881
  let provider;
5721
5882
  if (!Array.isArray(wallet) && isWalletConnectionSourceJS(wallet)) {
5722
- provider = new InjectedProvider(this.dappSettings.storage, wallet.jsBridgeKey, this.analytics);
5883
+ provider = new InjectedProvider(this.bridgeConnectionStorage, wallet.jsBridgeKey, this.analytics);
5723
5884
  }
5724
5885
  else if (!Array.isArray(wallet) && isWalletConnectionSourceWalletConnect(wallet)) {
5725
- provider = new WalletConnectProvider(this.dappSettings.storage);
5886
+ provider = new WalletConnectProvider(this.bridgeConnectionStorage);
5726
5887
  }
5727
5888
  else {
5728
- provider = new BridgeProvider(this.dappSettings.storage, wallet, this.analytics);
5889
+ provider = new BridgeProvider(this.bridgeConnectionStorage, wallet, this.analytics);
5729
5890
  }
5730
5891
  provider.listen(this.walletEventsListener.bind(this));
5731
5892
  return provider;