@qrlwallet/connect 2.0.0 → 3.0.0
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/README.md +3 -1
- package/dist/index.d.mts +251 -3
- package/dist/index.d.ts +251 -3
- package/dist/index.js +607 -13
- package/dist/index.mjs +589 -12
- package/package.json +7 -4
package/dist/index.mjs
CHANGED
|
@@ -145,15 +145,13 @@ var SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
145
145
|
var STORAGE_KEY_PREFIX = "@qrlwallet/connect";
|
|
146
146
|
var REQUEST_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
147
147
|
var WALLET_UNRESPONSIVE_MS = 30 * 1e3;
|
|
148
|
+
var RECONNECT_WALLET_PROBE_MS = 12 * 1e3;
|
|
148
149
|
var RESTRICTED_METHODS = /* @__PURE__ */ new Set([
|
|
149
150
|
"qrl_requestAccounts",
|
|
150
151
|
"qrl_sendTransaction",
|
|
151
152
|
"qrl_signTransaction",
|
|
152
|
-
"
|
|
153
|
-
"personal_sign",
|
|
153
|
+
"qrl_signMessage",
|
|
154
154
|
"qrl_signTypedData",
|
|
155
|
-
"qrl_signTypedData_v3",
|
|
156
|
-
"qrl_signTypedData_v4",
|
|
157
155
|
"wallet_addQrlChain",
|
|
158
156
|
"wallet_switchQrlChain"
|
|
159
157
|
]);
|
|
@@ -516,7 +514,10 @@ var SocketClient = class extends EventEmitter2 {
|
|
|
516
514
|
* Connect to the relay server.
|
|
517
515
|
*/
|
|
518
516
|
connect() {
|
|
519
|
-
if (this.socket
|
|
517
|
+
if (this.socket) {
|
|
518
|
+
if (!this.socket.connected) this.socket.connect();
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
520
521
|
log("Socket", `Connecting to ${this.relayUrl}`);
|
|
521
522
|
this.socket = io(this.relayUrl, {
|
|
522
523
|
path: RELAY_PATH,
|
|
@@ -534,7 +535,7 @@ var SocketClient = class extends EventEmitter2 {
|
|
|
534
535
|
const reconnectChannelId = this.channelId;
|
|
535
536
|
this.joinChannelNow(reconnectChannelId).then((result) => {
|
|
536
537
|
this.settlePendingJoin(reconnectChannelId, { result });
|
|
537
|
-
this.emit("reconnected");
|
|
538
|
+
this.emit("reconnected", result);
|
|
538
539
|
}).catch((err) => {
|
|
539
540
|
const reconnectErr = err instanceof Error ? err : new Error(String(err));
|
|
540
541
|
this.settlePendingJoin(reconnectChannelId, { error: reconnectErr });
|
|
@@ -634,7 +635,9 @@ var SocketClient = class extends EventEmitter2 {
|
|
|
634
635
|
log("Socket", `Joined channel ${channelId}`);
|
|
635
636
|
resolve({
|
|
636
637
|
bufferedMessages: response.bufferedMessages || [],
|
|
637
|
-
channelPublicKey: response.channelPublicKey ?? null
|
|
638
|
+
channelPublicKey: response.channelPublicKey ?? null,
|
|
639
|
+
participants: response.participants || [],
|
|
640
|
+
terminated: response.terminated === true
|
|
638
641
|
});
|
|
639
642
|
} else {
|
|
640
643
|
error("Socket", `Failed to join channel: ${response.error}`);
|
|
@@ -913,6 +916,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
913
916
|
this.status = "disconnected" /* DISCONNECTED */;
|
|
914
917
|
this.connectedAccounts = [];
|
|
915
918
|
this.unresponsiveTimer = null;
|
|
919
|
+
this.reconnectProbeTimer = null;
|
|
916
920
|
this.failedReconnects = 0;
|
|
917
921
|
this.walletPresent = false;
|
|
918
922
|
this.pendingRestore = null;
|
|
@@ -942,13 +946,23 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
942
946
|
this.failedReconnects = 0;
|
|
943
947
|
}
|
|
944
948
|
});
|
|
945
|
-
this.socketClient.on("reconnected", () => {
|
|
949
|
+
this.socketClient.on("reconnected", (result) => {
|
|
950
|
+
if (result?.terminated) {
|
|
951
|
+
log("ConnectionManager", "Channel terminated, observed on auto-reconnect");
|
|
952
|
+
this.handleSessionTerminated();
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
if (result) {
|
|
956
|
+
this.walletPresent = result.participants.includes("wallet");
|
|
957
|
+
}
|
|
946
958
|
if (this.keyExchange?.areKeysExchanged()) {
|
|
947
959
|
if (this.walletPresent) {
|
|
960
|
+
this.clearReconnectProbe();
|
|
948
961
|
this.setStatus("connected" /* CONNECTED */);
|
|
949
962
|
this.failedReconnects = 0;
|
|
950
963
|
} else {
|
|
951
964
|
this.setStatus("waiting" /* WAITING */);
|
|
965
|
+
this.armReconnectProbe();
|
|
952
966
|
}
|
|
953
967
|
} else {
|
|
954
968
|
this.setStatus("waiting" /* WAITING */);
|
|
@@ -975,11 +989,17 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
975
989
|
this.walletPresent = true;
|
|
976
990
|
log("ConnectionManager", "Wallet joined channel");
|
|
977
991
|
this.clearUnresponsiveTimer();
|
|
992
|
+
this.clearReconnectProbe();
|
|
978
993
|
if (this.keyExchange?.areKeysExchanged()) {
|
|
979
994
|
this.failedReconnects = 0;
|
|
980
995
|
this.setStatus("connected" /* CONNECTED */);
|
|
981
996
|
}
|
|
982
997
|
}
|
|
998
|
+
if (data.event === "close") {
|
|
999
|
+
log("ConnectionManager", "Wallet closed the channel (explicit terminate)");
|
|
1000
|
+
this.handleSessionTerminated();
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
983
1003
|
if (data.event === "disconnect" || data.event === "leave") {
|
|
984
1004
|
if (data.clientType === "wallet" || !data.clientType) {
|
|
985
1005
|
this.walletPresent = false;
|
|
@@ -995,6 +1015,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
995
1015
|
if (!this.keyExchange) return;
|
|
996
1016
|
this.keyExchange.on("keys_exchanged", () => {
|
|
997
1017
|
log("ConnectionManager", "Key exchange complete");
|
|
1018
|
+
this.clearReconnectProbe();
|
|
998
1019
|
this.setStatus("connected" /* CONNECTED */);
|
|
999
1020
|
this.walletPresent = true;
|
|
1000
1021
|
this.failedReconnects = 0;
|
|
@@ -1015,6 +1036,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
1015
1036
|
*/
|
|
1016
1037
|
async getConnectionURI(retryOnConflict = true) {
|
|
1017
1038
|
this.setStatus("connecting" /* CONNECTING */);
|
|
1039
|
+
this.clearReconnectProbe();
|
|
1018
1040
|
this.pendingRestore = null;
|
|
1019
1041
|
this.walletPresent = false;
|
|
1020
1042
|
if (this.keyExchange) {
|
|
@@ -1069,17 +1091,39 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
1069
1091
|
this.setStatus("disconnected" /* DISCONNECTED */);
|
|
1070
1092
|
return false;
|
|
1071
1093
|
}
|
|
1094
|
+
return this.joinAndSettle();
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* (Re)open the socket, join the persisted channel, drain buffered relay
|
|
1098
|
+
* messages, and settle status. Shared by the cold-restore reconnect() and
|
|
1099
|
+
* the warm resume() path whose socket was torn down by the reconnect probe.
|
|
1100
|
+
* Assumes channelId and a hydrated keyExchange are already in place, and
|
|
1101
|
+
* that the caller has reset walletPresent + set RECONNECTING status.
|
|
1102
|
+
*/
|
|
1103
|
+
async joinAndSettle() {
|
|
1072
1104
|
this.socketClient.connect();
|
|
1073
1105
|
try {
|
|
1074
|
-
const { bufferedMessages } = await this.socketClient.joinChannel(
|
|
1106
|
+
const { bufferedMessages, participants, terminated } = await this.socketClient.joinChannel(
|
|
1107
|
+
this.channelId
|
|
1108
|
+
);
|
|
1109
|
+
if (terminated) {
|
|
1110
|
+
log("ConnectionManager", "Stored session was terminated by the wallet; dropping it");
|
|
1111
|
+
this.handleSessionTerminated();
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1114
|
+
if (participants.includes("wallet")) {
|
|
1115
|
+
this.walletPresent = true;
|
|
1116
|
+
}
|
|
1075
1117
|
for (const msg of bufferedMessages) {
|
|
1076
1118
|
this.enqueueRelayMessage(msg);
|
|
1077
1119
|
}
|
|
1078
1120
|
await this.messageQueue;
|
|
1079
|
-
if (this.keyExchange?.areKeysExchanged()) {
|
|
1080
|
-
this.
|
|
1121
|
+
if (this.keyExchange?.areKeysExchanged() && this.walletPresent) {
|
|
1122
|
+
this.clearReconnectProbe();
|
|
1123
|
+
this.setStatus("connected" /* CONNECTED */);
|
|
1081
1124
|
} else {
|
|
1082
1125
|
this.setStatus("waiting" /* WAITING */);
|
|
1126
|
+
this.armReconnectProbe();
|
|
1083
1127
|
}
|
|
1084
1128
|
return true;
|
|
1085
1129
|
} catch (err) {
|
|
@@ -1088,6 +1132,65 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
1088
1132
|
return false;
|
|
1089
1133
|
}
|
|
1090
1134
|
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Nudge the connection back to life after the dApp tab / wallet app was
|
|
1137
|
+
* backgrounded. Idempotent and safe to call from visibilitychange / online
|
|
1138
|
+
* / pageshow handlers. If a restored session has not yet been hydrated,
|
|
1139
|
+
* runs the full reconnect(); otherwise just re-opens the socket, whose
|
|
1140
|
+
* own connect handler re-joins the channel and drains the relay buffer.
|
|
1141
|
+
*/
|
|
1142
|
+
resume() {
|
|
1143
|
+
if (this.status === "connected" /* CONNECTED */) return;
|
|
1144
|
+
if (this.pendingRestore && !this.keyExchange?.areKeysExchanged()) {
|
|
1145
|
+
void this.reconnect();
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
if (this.channelId && this.keyExchange?.areKeysExchanged()) {
|
|
1149
|
+
if (this.socketClient.getChannelId()) {
|
|
1150
|
+
this.socketClient.connect();
|
|
1151
|
+
} else {
|
|
1152
|
+
this.setStatus("reconnecting" /* RECONNECTING */);
|
|
1153
|
+
this.walletPresent = false;
|
|
1154
|
+
void this.joinAndSettle();
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* A session was terminated for good (wallet sent a relay 'close', or the
|
|
1160
|
+
* join ack reported a tombstone). Clear local state and surface
|
|
1161
|
+
* DISCONNECTED so the consumer drops to a fresh-pairing UI.
|
|
1162
|
+
*/
|
|
1163
|
+
handleSessionTerminated() {
|
|
1164
|
+
this.clearReconnectProbe();
|
|
1165
|
+
this.clearUnresponsiveTimer();
|
|
1166
|
+
this.walletPresent = false;
|
|
1167
|
+
this.pendingRestore = null;
|
|
1168
|
+
this.keyExchange = null;
|
|
1169
|
+
this.socketClient.leaveChannel();
|
|
1170
|
+
this.socketClient.disconnect();
|
|
1171
|
+
this.clearSession();
|
|
1172
|
+
this.setStatus("disconnected" /* DISCONNECTED */);
|
|
1173
|
+
}
|
|
1174
|
+
armReconnectProbe() {
|
|
1175
|
+
this.clearReconnectProbe();
|
|
1176
|
+
this.reconnectProbeTimer = setTimeout(() => {
|
|
1177
|
+
this.reconnectProbeTimer = null;
|
|
1178
|
+
if (this.walletPresent) return;
|
|
1179
|
+
warn(
|
|
1180
|
+
"ConnectionManager",
|
|
1181
|
+
`No wallet rejoined within ${RECONNECT_WALLET_PROBE_MS}ms; treating reconnect as dead`
|
|
1182
|
+
);
|
|
1183
|
+
this.socketClient.leaveChannel();
|
|
1184
|
+
this.socketClient.disconnect();
|
|
1185
|
+
this.setStatus("disconnected" /* DISCONNECTED */);
|
|
1186
|
+
}, RECONNECT_WALLET_PROBE_MS);
|
|
1187
|
+
}
|
|
1188
|
+
clearReconnectProbe() {
|
|
1189
|
+
if (this.reconnectProbeTimer) {
|
|
1190
|
+
clearTimeout(this.reconnectProbeTimer);
|
|
1191
|
+
this.reconnectProbeTimer = null;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1091
1194
|
/**
|
|
1092
1195
|
* Send a JSON-RPC request to the wallet (async, fire-and-forget).
|
|
1093
1196
|
*/
|
|
@@ -1152,6 +1255,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
1152
1255
|
*/
|
|
1153
1256
|
async resetForNewChannel() {
|
|
1154
1257
|
this.clearUnresponsiveTimer();
|
|
1258
|
+
this.clearReconnectProbe();
|
|
1155
1259
|
this.walletPresent = false;
|
|
1156
1260
|
await this.flushTerminate();
|
|
1157
1261
|
this.socketClient.leaveChannel();
|
|
@@ -1167,6 +1271,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
1167
1271
|
}
|
|
1168
1272
|
async disconnect() {
|
|
1169
1273
|
this.clearUnresponsiveTimer();
|
|
1274
|
+
this.clearReconnectProbe();
|
|
1170
1275
|
this.walletPresent = false;
|
|
1171
1276
|
await this.flushTerminate();
|
|
1172
1277
|
this.socketClient.leaveChannel();
|
|
@@ -1174,6 +1279,8 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
1174
1279
|
this.setStatus("disconnected" /* DISCONNECTED */);
|
|
1175
1280
|
this.clearSession();
|
|
1176
1281
|
this.connectedAccounts = [];
|
|
1282
|
+
this.pendingRestore = null;
|
|
1283
|
+
this.keyExchange = null;
|
|
1177
1284
|
}
|
|
1178
1285
|
// ── Internals ──────────────────────────────────────────────
|
|
1179
1286
|
enqueueRelayMessage(data) {
|
|
@@ -1182,6 +1289,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
|
|
|
1182
1289
|
async handleRelayMessage(data) {
|
|
1183
1290
|
if (data.clientType === "wallet") {
|
|
1184
1291
|
this.walletPresent = true;
|
|
1292
|
+
this.clearReconnectProbe();
|
|
1185
1293
|
if (this.keyExchange?.areKeysExchanged() && this.status !== "connected" /* CONNECTED */) {
|
|
1186
1294
|
this.failedReconnects = 0;
|
|
1187
1295
|
this.setStatus("connected" /* CONNECTED */);
|
|
@@ -1371,10 +1479,29 @@ function getAppStoreUrl() {
|
|
|
1371
1479
|
|
|
1372
1480
|
// src/QRLConnectProvider.ts
|
|
1373
1481
|
var requestCounter = 0;
|
|
1482
|
+
var QRL_CONNECT_PROVIDER_INFO = {
|
|
1483
|
+
name: "MyQRLWallet (Mobile)",
|
|
1484
|
+
rdns: "com.qrlwallet.connect",
|
|
1485
|
+
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAAAXNSR0IArs4c6QAAH1dJREFUeF7tnXt8XGWZ+L/PmaT3JpOkrVCKgD9wFcELwgoLArIsWnDLCnIVgZ8gF1lcylVsk5xMKuXiiiKLn7L82OWiK1ZBtgpdFRDdAu4P1EVAuaNoQbrNTJJek5nzbN+TpE0nk8yZmTMzZybv/AXNe3ue9/me9/a8zyvYn9XAJNaATGLZrehWA1gArBFMag1YACZ191vhLQDWBia1BiwAk7r7rfAWAGsDk1oDFoBJ3f1WeAuAtYFJrQELwKTufiu8BcDawKTWwKQEQF0cwGGtPQjMtv6ngA8m8ViJJ6D1TsekAEDZZugu01MeHxLlIBXmi7A7ymwxKNjfdg1ohkEV3sDhj+LxG8fhN7M7eVGkPmGodwBEXRp7MpwdE7pUZBYwq96FDpHnLQqbFF2dXsf5c29hY72NCnVrC6+6TGvxOA6HK1TlQyNGUbcCh2j1I0WNnv8IbFH0+kbhG7Nc3ixDdVUpsi7tQRXp66TDE7lSYEZVNFuPlQoZ9XhePF0UX8bL9SBi3QHwxtXMnTqFOx2Rj6J2kRu6kZq1gOqrHpzV0sWaWp8S1RUA6tLQq6wAOdvf5bG/smlAlDcyaT2m9RqeKVslFSi4bgDQk4gl38VpTkzutF/+CliOX4X+clD427kuaytVY9j11A0AvS4f8zx5QMROe8I2knHLM5MhR7/a4nJpxeoMuaK6AEBdZvSqfBPhePv1D9lC8hSnqhlxeGfc5ZXK1hxObXUBwLp2PhgTecQRZoejFltKYA0IKqqrmoQTxSUdOF9EEtY8AGbLM9nJchG5quaFiYhRFNoMsyBOD+rRbct5rtC81U5f8zbzrMuU3TxeR2RetZU5WetXYTOefqolwX21poOaB6CvnUM8kTXYxW9VbU887WzuJlHVRhRRec0D0NPOGXbrs4ieDz2LfqvZ5Yxac5qreQCS7ZwtMbnd7v6EbtEFFegvhLs43gJQkNpKT5zswEWks+ZJLl0VVSvBOM2J6oPNXRxnAahwN6RclqnKEgtAhRU/XN0Oj1H9SdzlGAtAhfsh1cEyRJaEXW2WK3DYxVesvOwrXWF/KCwAFevK3BWVGYC1Ar9WYSvKXwD7hm1A5VafwgaUx0Ukqei7BPYL01FwBAAR/XFzJx+1I0C5ezSr/LIBoDyVdvSCQXhu17UM9s5jgcbkq8Ci2oFAN6ny8fQWnpwzg409MF/SnOU0SHdYmwYWgAobfHZ1ZQFAzf1XvTCeMK7VO34plwNB/hNlapXFDlS94+llTd18ZXRic3Le6/I0iBkJSv7ZKVDJKiytgLIAAF7G0/e2dfPsTsZzHo2pXUiJSPRvmQnqoIc1uTyWreFkB3eKyKdL0/xQbjsChKHFEsooFwADm3S3eTeMvfua7GCDiMwsockVyaqKxlQPberm8Ryj5i2IXBhGQ+wIEIYWSyijXAA0bNLdZlUAAH8PvQT5x82qaMbRw9pyjAA9nXzDQS4Io1oLQBhaLKGMcgFQiRFg2Hg8M48QQ4HxZwrrHvMEAKQ6+QYWAN/qyvLxKcGeC85aLgDKPgIMXS7/sQ5yVXwKT/dvpcVr5DOKXI3QUnLHWAAC2VLJeg5USxkT9Q6fBIdchVdOAPwvv2r/xkH2WrCc9SNtNwNBbwfLNYy7DRaAQCZRDwAkPJX2kAUpPwDo3S1djNmJSbmcLCp3KEwL1IPjJao0AKI/jtuDsJK6rKjMPS6fc5Cbzf52iBCUDYBRi8abW7q4OFvoZDtHSExWmbilRSlkx3Ay7iI4zDXAqOp+EHdZZE+CS+q1wjMnl7IIR75vFpAWgFH6q9AIsL1G1RXNCS6stUBZIdpM4cYbRo7XFzN99mzWqMgHQhSmeiNAgiPEq60RQJWNip7SmuCHYfRpJcsI0WYq2eyd61rfznsckTUiNIfUCm9wUBfMvYY3xkxRQjgIG5oGae4pkMuRgvx7yVMgQTPkPgcIdQpk9rJEvxnv4sxa+/rXxTaob0oraOz9E2ci0sFQ3P9SwfYym3WPtuv5Y6UBSLkchcr9Jox7KTAbyDzRQ3MdhKU6uBWRz5ZSvn9ioWz20LtFuKrFJVVSeVXKXKqhVKnZY6s1r75sgDkDyhUxOA+V4heRDt5AWt89bxkv5likGleIkn2BxNGbm10+n13+epePxVRWopTmbmF8gWJ6WFPHWFeIpMud4skZJXReWhx9IOOxYn2Sh/f5OltLKKuqWesGgGwtbn8GCfhpQBUfCby0nljbDKa1XkdvrmxJl3jqNbbsuSfpoOWOLsfUQS+NNG9btLtsyq7jyfNofHsDU+euY4D34BVahyn/uWdx5s5lytwj2Swnk8mu49WzmbZnC/LURtL984O//OK33Zye1mAArPFMoG4BCGjzNtkk14AFYJIbwGQX3wIw2S1gkstvAZjkBjDZxa9bAMyDGX9cwJQFG0lTwEJvshvEePI/tRZpGyC25wGofL52d32y5atbAPpdTsio3BFmBAQLh7kCqa+0dLF/veiibgFIuZwLcmsIh2L10tchyaFes9AoLl5IBVa1mLoFINnOHeLImVXVbn1W7pHWD8W/xJP1IF5dAvCIS8P7lZcFeXs9dFKUZFDjAuHpP8QT3FyLvj91vwbwb1W5nIgn37FvBoSPztAlfn1gQDhlnsuG8GuobIl1NwKoy7Skx68dERPK0P7Ko4GUJ/rhVre23wg2qqk7AFJL+aTE5K6SrxSWx3DqotThUeC7zeb6ppkS1fCvrgDQi5na28IqRP6mhvsk8k33LV7QtKfHz0nwg1peC9QVAKkOrlGRq+tKqAjiMOqT/9y0Rj12+lJ+H8FmBmpSXdiKv/Bt5xxicovqtj3qQKLbRKVoYCSol6Dfbe7i1FodBWreVozx9yzhEKdRvo2ye10ubEqx1HLnFVRVv7RZWD4/x/2Gcldfavk1D8CGL/LegUZWjez517xApfZoNfKbB0TQpc0u/1hrI0FN24u5DB8TVqrIu+2XvxqWP6rO4TcVkg537OWypcqtCVx9rQIgqaW8Q2PyXYT3GX+fWhUkcE/VQEJVMg66uOm33CIrx17FjKIINWk3PVfR7EzjRypykDX+aJmVwFtk9LCmZbxUC9OhmgPALHqTnSxzRK6wOz7RMv7trRFe1y16eMu1vBbRFo5qatRbmNW+1BIOpIHHFWmw8/4Id57oyuZd+ZScz2CEW1lbrhD9LvMGPX4mw34+NTd8RdkSwm6bkgQ9KZ7gobCLDrO8mrEhf+rTwRdFJGFuedVMw8PsrVorS/VXzQk+GOW1QM3YUZ/LnLTKzwXMY8/2VwsaGLo7cGFzglujCkFN2JL5+qdcFm8Ld2gOWuyvhjSg8OtG0YWz3LEvbkZBjJqwp3VXMnvKdHpGFr5RUJxtQ34NDL+AuQXVM+IJvpc/R+VT1AQAyXYucWLyFfMKTOVVZGssRQPDdwe+3exyehTvDkTeoNRlSm+Ge3Dk70rpCJu3ihpQtPlNnSq3Rm9LNPIA9Lrs7XnyIxH2qmIX2qpL0YDxGPX0spYEN5ZSTDnyRh6AVAd/DfIfKsQi39hy9FC9lKm8Ek/o/4maOJG3qWQnX0fkIuvzEzXTCd6e4cszPekBPWTucl4InrP8KSMPQE8nv3WQd5VfFWNrGHPbW1GzkFPV14FnUNZFbmEnzBy+GHSAiDSqICObB9Xq7GE9blTVk1oTPFiNvhyvzmrpJJAO3rycmVNn0C9SHW/nkW08NUG2RB9FuL95Xx7iuW0hMjuHoyFETYMj1HYhSdhPMhxNTBahvBthrlRrJ01RD726JcH1UToUi1r3jQZDejr4uIPcX5UAV+b1Q/QnONzcAE/MdnkrELURTGRiJa3P8L6Yw+miXKQisZFmVswABBXVrzUJl0UprmjF5C/ULvyL7h2ciyMrKh3gVmGTI7q8B768p8vWKH2xCtXj6PTDwQMOwOFuVfmLkB8Xz9s0z9PVr/yZRQdGaDs00gCkOvmyiCyuKADK8+roF+Iu99eL4WdbZu9S9vEa+GdUjjB/q5QRqOqj8d34myi5SFdK9rxfh+wEZuHW67LaD3JVuXnrgIge0eTyi3o1/hE9+weMHj/FkYONrithCIo+E5dt3qEuAwUbRJkyVELuopruA9DFGlQOKaqAAjMppDSjn2pdxgP5spp7CZkBFiA4NOZLXdm/Dw7C1Eb6XofX3pPH0JIue6KsBDmwEoag6Np4G++I0gszlZC7KAsYHgEeAzm4qAIKyTR0UrksnqBzvC+/eXc4leZwhHYceYfAjAqOTIVIgwqDqvqWeNwbb+BLEy06e5ZwmBPjUUScgiopIrGqbo47tEqEokZEHAB5DCgrAMNbnS9s3qof3TXHHVazcOxbyt6eQyeIceiKrM6yFrx+/E5zZiHC4rjLfbng9l3NO7gBkcvKLpio9vcyc/cb2VwEP2XJUnaZi2310AhQGQDU04tbu7k5V1t7ExzkZbhdkP2KlaVa+bYf5Cn9it7csh/tuV6O37yMPbYMysOAGdnK97MABNdtpQBA9c1mh91yTRP8ub7Hz6iPtwYGRPXSpi5uyT69Ni9q9u7LTYp8rqy7QhaAiAFgDmc87WpKkMieHqx1mTE9wz0Sk+OiOtcPrs3tez9vOsL+TS7/k53XvKugMVlpAShcq2XJUaERwMuoHtuW4D+yhTCLQ2mQ+2Hboq0sElahUEE9T29rSXB+NvDrL6bJaZNUWV0l7AgQvNMrAYAqr3uqC9u6eTarZdLTzhXiyHV1Y/wjAopmnK28rWk563daNJs1VydrEdkleC8VmFJV+/vtIjiQ1ioBAPDSgOjCeS4vZRtDXxf/piqnBGpsLSUy0760Htu8jNVZu0YGgJsRubBsUz47AgS3lAoB8AKiC+Mur+xkDC5OUlkjyMF1NwIYQVU/mX1J3d/u7SShIkssAMHttGwpKwTA7zZv1YXZ+//+oZfHHxHZtZyLwnyvy5UNPtUL4glWZI8AqXYSErMAlM2oCym4EgAI/NbbqsdmB3E1APR6rFWRt4UJwIjBi/rz7w0qJEVGHQoN+Ty1qjllhl0E/PinYf881c+1JviGBaByjoAF92HVAVD+pAwtCIv9Eo/+woswiKc/8Rzubsjwe1F6BgdZ23ItfduVsxIn9d8saHRoGoixv6PmPjQnINIc5rTEArDDHIvt24INutAM9QCAcUXwlM2oPtrQyOLZS3mhkCuUfjzUq2hypnM1npwHNCFsv8xSqE5H0lsALAC+BiacApU6AhiHNE8fUFixPsnD+3zdvKNV/K/vat6pU/i0Il+gxKmRBcACUF4AhpzQuuNvsExuJW32XYo3/R05zdqkP80+Xsy4bMs7ii3TAmABKB8Awp8yGU20dvPP5bhU43tvtvNeiXEXxkGviMtCFgALQLkASKvoOXG4eyIffGPE27vAjA1d/v830MngyF/ywbPRZf6gmscnCg8ZYwGwAJQDgC2qem1Lgq6JpiappeylDoc7wpGKHOXH8Bm5iuL5M6XfAQ9kMtzb18gvJ3pytH8p7047sgrBj7gWdEfDAmABCB0AFb0j3sn/HW+XR29iamqd71/0KYG9TajHcacv5mEJpVdV18QGuXz2cp7PeZnFnFekOUEdvkMBwZMsABaAsAH4Q0x04WyX57K//mY7N/kFdpdp3CciHyh4zq4kM+j5rb/l3lxv75ry+1xu90TOCurJaQGwAIQHwNAzQFc3jxPxzGxfelO4B+T9xeza+JMipX+bA09nPMFXc40EySXsITF5hIARtC0AFoDQAPAjHXSxIJdhPusyZb7HE/6Xv8jfdvcJYat6+tl4YtsCO2tb1Xdk6+CfPJELg6wDLAAWgFAA8C/Ue3pVvJvrs+3bxDWdPoP/p1K6S/WOu736cibDEXO+xJ+y6+t1+UtP5fEgL2haACwAoQAAvInocXGXX2YbZMrlKFTMu1jxIj/+O2XbMRLoyrjLyWPWGkMerK8gske+UcACYAEIC4BfDogeNy/HC4gpl9tQOScM49+5DPViwvtmuzyTXXayg8WIfMUCEFzr+XQVvKSQU9aCM5yn+mCLw8dzHXqlOiSF0ByyWoZi/Xi6rCVBR3bZ61zmN6qMmR5lp7MjgB0BSh4BzJTEQ5e1ddGebWAblnJM2pHVZQvrLnLPuvXeWdkOdn4kC+XPgsyaCDwLgAUgFABQPbMlYXxydvz8EORLuZIGWV7wnn+e4WLUjtAvnK16XPbF9hcvZurcVn6uyEETDe0WAAtAyQCYAjzVM1oTfDMbgGQnNznD75qFOQUa5VL6pDl4y47voytoTK31PUWPNvWOB4EFwAJQVgBSndxUrof9hiHICcDwucMDiJibZBaAAF8fuwge705wgAsxonpGc44RoKedy5yYXF/Gly2fdHKMAGYKNKeV/8wX7tyOAHYEKHkEGHJR0EuzH3/2T2XbOcUT+Va5niASYVUTeqq4bBr9kXt9MdNnN5kQLxMHt7IAWABKBsC3f/T7LV18InukNY/S9apsNKeyAUbhwpIM3Tb7WryLS7NdIt5ymdXoSV++EO4WAAtAKACI8POB6Xrc3KuMs9rOv2Qn3xPkhMKsO39qAU88PbypmzVj6mznbBz5l3zzWguABSAUAIA/eKLHteY4le1p51REbhdh+kQL0vwmvyOFmXY56GMvvsGRuV5aTHbwMxH5cL4yLQAWgLAAwPP0tNZuvp1tdH1X05ZulB+K8KHQAFDdOOjxgXnLeDG7vrVX8fYZ0+TVINMuC4AFIDwAVH/RkuCQXO7Q2165PFg9+b4Kb8s3Lcn31QbSil4bfw4318WYVAdLcKQ7yOGbBcACEBoAZiWcUT21rZvvjFkMD/npm6nQvypMCWDkOyXZfvJr5v2ijzfBUbmeGO2/mrmZKWKiPR8QpA4LgAUgPAD8kvTphkGOmXUNfx4DwdB7vGepSIfAgiAG6pc4klDNS/V6+5ZNXLHLlzE7S9mQSLKDC0TEvHHmBBlpLAAWgJABwNOMuvFlLBvv8npSWRBD/k2FQyaapuwcQUs3Ocq5TXO4d7y3dde77BvzeEpFpgVda1gALAChAjB8b/fPafS0OQl+OsFbww19cLLncYkgB+SK8+nfMlPWgd7uDHLD7OX0jFden8ucTIZ7cOSokS61I0DQMXYoXRB9FVZiSKlr4T7AaFGHIViP6okt3Tw6kRp6Xfb2PB4Wkd1zTGnw3RzgR/mCa6U6+S7IopFYoUE7044AdgQIdQQYbcQCj2U8vaC127+xNW5M0GQn3YIszQbA5E+LLmxzR4VNz0qUWk6LbuVGQc40H4pCv2QWAAtA2QAYXsD+QVQvb3a2nQa7eLlGg94OLtKhhevOi1qPR+IxXSQuG3Lkk75raPW28E8qcmKxD2hYACwAZQVgWL0DqLpbNnFTrt2bng4ucnIAgMfDAzE9fl4WACYy9AY4POPJahWmFvrVHw2TBcACUAkA/Pu7qC5t7mJ59kJ2XADg4QEZC4A5Wc40ym8QSn63zAJgAagYAIJ2N7m4JQPgMsdTed68IVbqPoMFwAJQMQAUTcRdukICwESObrMAlKoBC0DlADAvxXSFNgJYAMKz/REbCLnEkIqrtXOAnGKbNQB6XbPLF0sdAYy/T3qKvCTmobwSf3YKZEeA0EYAcz3LcVjleST9qP4yfLg49F8iyg/iORzlCl0Er7uS2Y1TuV6FmX5IIvMz9Snq3zmI8WGQ+dYbtLCvQ9DDw8JKDSF1TYwAQlLRS+Kd3DXRmXouV4ZCARiy93FqUehtZ091+JGI7J1P/XYEsCNAKCOAonfG2zhvPEe1iQyx0HOAfEZt/v7WUvaZEhNzAj2h67UFwAIQCgC5wqIEMVSTZlwAlIcGHP277IOwIOWqy5Q+5SFFDpsovQXAAhAKAI7qlU0JbghinNlp+jq5wUMuz/53VZ7C0aNbXFKFlvuqy7QW5Tcw8TTIAmABCAUA4L/Mqe1c1wSkDfYYtpnHr1/C/IZGeRQdet1xp5+Sdhz9xGyXH5p/D1LuyNqgbwlLvJgkbFiU4J8OuwgeLzKcx1oVeduwEY6vUdUHcfhXzC6QQybt5V6oNjioCpoZpKXR4RKdIHqDSSeqyx3hcY+h4Fe5yjVl+ovjNLNo4EjgErMnlK9T7QhgR4DtI8CmrXrsrtfy2mgLN45nvUEBGCIkox6bRHJ7fvpl+zdd/G3LmQiNeb9R/hkCW/AYGHeHyZwyDN2DnGoc5IK6RlsALABDGlCex9OF8WWYcCLbfwaAlPIyyJ55R4C8lhytBP67Bp6e09bN7TvJDJJqJyExWRLkLKEoqUS1v5eZu9/I5qLylyFTvtGyDFUGK7IS5wDAS7JVFzZfy0ujW+W6OJ/P8KA4ckxkFRRMjTulGrmdo6qLWhOsygagr5OEShkBUNX+fgtAoK6rCADKy+LpwuasQFNmUZnq5DZBPhOosTWSyAfArC8yelC8m6fGjAAd3CWOnG5HgAh0aEUAEPodRxc2dYyNs5layieJiXn8ouB4PhFQ35gm7LibqU/HXd4vQ2uMHdM+RXpdkiDhv2s2UoudAgU3jQoBoJ6nn25J8K3s7cZ1VzK/cbo8BLwreKujndJsLXlbObR1OU+Mcc5rZ3/HkafLKoEFILh6KwGA/wn09OF4gqPHfBHNW18dfEaRFSLEgrc8milNVGkVXfkrOOMjLunsVvZ2cLGK3FTW1lsAgqu3EgAMtyatg3pQyzX8Ort1eh6NqV1YIkhH2V58DK6SklKq0hNz9NAmF3OnYKffepemmHIPyMdKqiRfZgtAPg1lz0nlMeDg4LmKS6mqqzb0c0qu7bk3L2fmtFl0qifnOg7xkb324mqqfC6BAU91TczjwqZlmCuVY37JJRwuDfIgMKOsLbQABFdvBUeAbcEb/MXwyc0uJsDsmJ9ezNRknL8Uh0tRFokjYtoX6S1Sc+SGvuDAP+JwX/aLkiNCGtl6W3gakXcG750iU1oAgiuukgAMtUqfHhAOncgL0/e5uYEZPX2c1ugwLwP7IGV4Bim4mnZOaRoorBN4LZPhv1ti23a3XHSCUI1Tkhm+5sTk/LJtfY5uoQUgeM9WHACzP47++6atfHbX5SY25/i/nS6mjBv7LbisoaUcNSTlc6LzH/Pr4HQVuYUQrlkGksECEEhNQ99jf1+6MmuAnVrl6bc3xThnftYLjMFbHv2U3zmJ2DHv4SPq+TfIKjeTswAEN45qAeD7rIneoxmua+nmV8FbXDspezr8MO6LRaS8i95slVgAghtJtQDwW+g/Rcobnqrb2sVt2WcEwaWITkpfn0v5IDHuVJF9io0rWpJEFoDg6qsqACPN9KM86P9XjxUNaZ6YNXXbI9QuW4NLUeWUCskvsHtsOvuqcqIipwvMGPLMrsLPAhBc6ZEAYEdzzcnpWoW3QJ8R5QmHsaepwaUrX8rtoagd9lI4DJVdBXYDP5xKdX8WgOD6jxgA2xtupkNR2vjJpdFR74sV/HZA8B4qIqUFILjSogpAcAmqm7Jq05yJxLYABDcKC0BwXdVMSgtA8K7yAejkMUTK7gsUvFU2ZSkaUHRTXGgTly2llBNm3qpsBgQRwACQ7OSnMUc+XGsOaEHkm5xp9JV1Pey7z9ejs5MWXQCGriV+VUQuroifyuS0yIpKLaJrmnblI3I+gxWteILKIg1Abwfn4sgKC0BUzKW0dgj6gybhE5LjQk5pJRefO7IAGJFSSziQmPxXrV9GKb576iinf7qu3XHzXFTWfeRqShlpAIzHYrKDPzgiC6qpJFt3KBrYguoZ8QT3TvR+cig1FVBIpAEwciQ7uVFE/sFOgwro1YglHYrfSDLj6F/NyXEls5rNjTwAPe0cKo6sFphVTUXZuovXwBAAel9LghOKL6U8OSMPQP81zM1skfsRDimPCmyp5daACplG9IBZLuUNu1KEIJEHwMjU4/L3DnKTnQYV0cNRyKJ6T3MXp0Vp8TuilpoAQG9iau96ngTZLwr9adsQXAMKKRE9Me7ycPBclUtZEwAYdaxfwsFOTB4UIV459diaStKA2e709PaX3uTCA2+NzuHXaJlqBgB1aUjBF0Wl04TEL6ljbOaya2DIbVyfUTi8mOeeyt7A4QpqBgB/LXAVzTKVR8SR99v1QKVMpLh6VPl9rFE/2tSeOxhXcaWGn6umADDi97vMS3vcjshx5v9rToDw+zB6Jaqm1eOk+POskpVkotfAHS2qSfvZ4LLLoCercXivmBfZ7S9CGtD/QTil2eWRfLGJotDomjWedS7zG5TlIJ8cHdOyZgWKgjWU0gb//TN93vM4z0SkE3eC99JKqSfkvDVtL4+4NLzPY7Eg1/nL4qjH6wy58yJTnDF91bviczlPPh8dX/8g+qlpAEYE7GlnfxEWI3K8QGsQwW2aEDSgbFb0CXW4bf16vheliy5BpasLAIyw+hWm9/Wx27aAVuercpEg00bcqEeiJNSNsEF7txzphp56VRH9oedxIw6/irv01sJ8P5c66s4m/MC159GQmsup0sCxiOyB0qZKswgzUaZplCI6l8NIQyhTzLQGBkT8J01Tah7sVv29Oqz20jzU1s3voujaUKjodQdAtgKSLnFJ00YDzQqzxGNqxqv9J48K7eiC0zvoFIetXobNoqQGlU2tL7A26tuahcpZ9wBMoJDJLHsQO4l6/K8gMuRNY40gr4psgnrWgAWgnnvXypZXAxaAvCqyCepZAxaAeu5dK1teDVgA8qrIJqhnDVgA6rl3rWx5NfC/zGV/wHfd+nEAAAAASUVORK5CYII="
|
|
1486
|
+
};
|
|
1487
|
+
var EIP6963_ANNOUNCE_EVENT = "eip6963:announceProvider";
|
|
1488
|
+
var EIP6963_REQUEST_EVENT = "eip6963:requestProvider";
|
|
1489
|
+
function generateUuid() {
|
|
1490
|
+
if (typeof globalThis !== "undefined" && globalThis.crypto?.randomUUID) {
|
|
1491
|
+
return globalThis.crypto.randomUUID();
|
|
1492
|
+
}
|
|
1493
|
+
const r = (n) => Math.floor(Math.random() * n);
|
|
1494
|
+
const hex = (n, len) => n.toString(16).padStart(len, "0");
|
|
1495
|
+
return `${hex(r(4294967296), 8)}-${hex(r(65536), 4)}-${hex(16384 | r(4096), 4)}-${hex(32768 | r(16384), 4)}-${hex(r(4294967296), 8)}${hex(r(65536), 4)}`;
|
|
1496
|
+
}
|
|
1374
1497
|
var QRLConnectProvider = class extends EventEmitter4 {
|
|
1375
1498
|
constructor(options) {
|
|
1376
1499
|
super();
|
|
1377
1500
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
1501
|
+
this.eip6963Detail = null;
|
|
1502
|
+
this.eip6963RequestListener = null;
|
|
1503
|
+
this.resumeListener = null;
|
|
1504
|
+
this.resumeDebounce = null;
|
|
1378
1505
|
this.isQRLConnect = true;
|
|
1379
1506
|
this.options = options;
|
|
1380
1507
|
if (options.debug) {
|
|
@@ -1390,6 +1517,81 @@ var QRLConnectProvider = class extends EventEmitter4 {
|
|
|
1390
1517
|
if (options.autoReconnect !== false) {
|
|
1391
1518
|
this.connectionManager.reconnect();
|
|
1392
1519
|
}
|
|
1520
|
+
this.setupResumeListeners();
|
|
1521
|
+
if (options.announceProvider !== false) {
|
|
1522
|
+
this.startEip6963Announce();
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
startEip6963Announce() {
|
|
1526
|
+
if (typeof window === "undefined" || typeof CustomEvent === "undefined") {
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1529
|
+
const overrides = this.options.providerInfo ?? {};
|
|
1530
|
+
this.eip6963Detail = Object.freeze({
|
|
1531
|
+
info: Object.freeze({
|
|
1532
|
+
uuid: generateUuid(),
|
|
1533
|
+
name: overrides.name ?? QRL_CONNECT_PROVIDER_INFO.name,
|
|
1534
|
+
icon: overrides.icon ?? QRL_CONNECT_PROVIDER_INFO.icon,
|
|
1535
|
+
rdns: overrides.rdns ?? QRL_CONNECT_PROVIDER_INFO.rdns
|
|
1536
|
+
}),
|
|
1537
|
+
provider: this
|
|
1538
|
+
});
|
|
1539
|
+
const announce = () => {
|
|
1540
|
+
if (!this.eip6963Detail) return;
|
|
1541
|
+
window.dispatchEvent(new CustomEvent(EIP6963_ANNOUNCE_EVENT, { detail: this.eip6963Detail }));
|
|
1542
|
+
};
|
|
1543
|
+
this.eip6963RequestListener = announce;
|
|
1544
|
+
window.addEventListener(EIP6963_REQUEST_EVENT, announce);
|
|
1545
|
+
announce();
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Re-open the relay socket when the dApp tab regains focus or the network
|
|
1549
|
+
* comes back. Debounced because visibilitychange + online can fire together.
|
|
1550
|
+
* No-op when already connected (ConnectionManager.resume guards that).
|
|
1551
|
+
*/
|
|
1552
|
+
setupResumeListeners() {
|
|
1553
|
+
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
1554
|
+
if (this.resumeListener) return;
|
|
1555
|
+
const resume = () => {
|
|
1556
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
|
|
1557
|
+
if (this.resumeDebounce) clearTimeout(this.resumeDebounce);
|
|
1558
|
+
this.resumeDebounce = setTimeout(() => {
|
|
1559
|
+
this.resumeDebounce = null;
|
|
1560
|
+
this.connectionManager.resume();
|
|
1561
|
+
}, 300);
|
|
1562
|
+
};
|
|
1563
|
+
this.resumeListener = resume;
|
|
1564
|
+
document.addEventListener("visibilitychange", resume);
|
|
1565
|
+
window.addEventListener("online", resume);
|
|
1566
|
+
window.addEventListener("pageshow", resume);
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Remove the foreground/online resume listeners. Called on disconnect so a
|
|
1570
|
+
* torn-down session can't be silently revived by a later tab focus; the
|
|
1571
|
+
* listeners are re-armed by getConnectionURI()/newConnection() if the same
|
|
1572
|
+
* provider is re-paired. Safe in any env.
|
|
1573
|
+
*/
|
|
1574
|
+
teardownResumeListeners() {
|
|
1575
|
+
if (typeof window !== "undefined" && typeof document !== "undefined" && this.resumeListener) {
|
|
1576
|
+
document.removeEventListener("visibilitychange", this.resumeListener);
|
|
1577
|
+
window.removeEventListener("online", this.resumeListener);
|
|
1578
|
+
window.removeEventListener("pageshow", this.resumeListener);
|
|
1579
|
+
}
|
|
1580
|
+
this.resumeListener = null;
|
|
1581
|
+
if (this.resumeDebounce) {
|
|
1582
|
+
clearTimeout(this.resumeDebounce);
|
|
1583
|
+
this.resumeDebounce = null;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Stop announcing this provider over EIP-6963. Safe to call from any env.
|
|
1588
|
+
*/
|
|
1589
|
+
stopEip6963Announce() {
|
|
1590
|
+
if (typeof window !== "undefined" && this.eip6963RequestListener) {
|
|
1591
|
+
window.removeEventListener(EIP6963_REQUEST_EVENT, this.eip6963RequestListener);
|
|
1592
|
+
}
|
|
1593
|
+
this.eip6963RequestListener = null;
|
|
1594
|
+
this.eip6963Detail = null;
|
|
1393
1595
|
}
|
|
1394
1596
|
setupConnectionListeners() {
|
|
1395
1597
|
this.connectionManager.on("status_changed", (status) => {
|
|
@@ -1447,6 +1649,7 @@ var QRLConnectProvider = class extends EventEmitter4 {
|
|
|
1447
1649
|
* Generate a connection URI for QR code display or deep link redirect.
|
|
1448
1650
|
*/
|
|
1449
1651
|
async getConnectionURI() {
|
|
1652
|
+
this.setupResumeListeners();
|
|
1450
1653
|
return this.connectionManager.getConnectionURI();
|
|
1451
1654
|
}
|
|
1452
1655
|
/**
|
|
@@ -1568,15 +1771,373 @@ var QRLConnectProvider = class extends EventEmitter4 {
|
|
|
1568
1771
|
pending.reject(new Error("Disconnected"));
|
|
1569
1772
|
}
|
|
1570
1773
|
this.pendingRequests.clear();
|
|
1774
|
+
this.teardownResumeListeners();
|
|
1571
1775
|
await this.connectionManager.disconnect();
|
|
1572
1776
|
}
|
|
1573
1777
|
};
|
|
1778
|
+
|
|
1779
|
+
// src/signing/ctx.ts
|
|
1780
|
+
var SCHEME_VERSION_MSG = "QRL-SIGN-MSG-v1";
|
|
1781
|
+
var SCHEME_VERSION_TYPED = "QRL-SIGN-TYPED-v1";
|
|
1782
|
+
var SCHEME_TAG_MSG = new TextEncoder().encode(SCHEME_VERSION_MSG);
|
|
1783
|
+
var SCHEME_TAG_TYPED = new TextEncoder().encode(SCHEME_VERSION_TYPED);
|
|
1784
|
+
var DIGEST_LEN = 64;
|
|
1785
|
+
|
|
1786
|
+
// src/signing/messageDigest.ts
|
|
1787
|
+
import { shake256 } from "@noble/hashes/sha3.js";
|
|
1788
|
+
|
|
1789
|
+
// src/signing/bytes.ts
|
|
1790
|
+
function hexToBytes(hex) {
|
|
1791
|
+
if (typeof hex !== "string") throw new Error("hex must be a string");
|
|
1792
|
+
if (!/^0x([0-9a-fA-F]{2})*$/.test(hex)) {
|
|
1793
|
+
throw new Error("hex must be 0x-prefixed even-length");
|
|
1794
|
+
}
|
|
1795
|
+
const n = (hex.length - 2) / 2;
|
|
1796
|
+
const out = new Uint8Array(n);
|
|
1797
|
+
for (let i = 0; i < n; i++) {
|
|
1798
|
+
out[i] = parseInt(hex.slice(2 + i * 2, 4 + i * 2), 16);
|
|
1799
|
+
}
|
|
1800
|
+
return out;
|
|
1801
|
+
}
|
|
1802
|
+
function bytesToHex(bytes) {
|
|
1803
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
1804
|
+
throw new Error("bytes must be a Uint8Array");
|
|
1805
|
+
}
|
|
1806
|
+
let s = "0x";
|
|
1807
|
+
for (const v of bytes) s += v.toString(16).padStart(2, "0");
|
|
1808
|
+
return s;
|
|
1809
|
+
}
|
|
1810
|
+
function concatBytes(...parts) {
|
|
1811
|
+
return concatBytesArr(parts);
|
|
1812
|
+
}
|
|
1813
|
+
function concatBytesArr(parts) {
|
|
1814
|
+
let total = 0;
|
|
1815
|
+
for (const p of parts) total += p.length;
|
|
1816
|
+
const out = new Uint8Array(total);
|
|
1817
|
+
let off = 0;
|
|
1818
|
+
for (const p of parts) {
|
|
1819
|
+
out.set(p, off);
|
|
1820
|
+
off += p.length;
|
|
1821
|
+
}
|
|
1822
|
+
return out;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
// src/signing/messageDigest.ts
|
|
1826
|
+
function computeMessageDigest(messageBytes) {
|
|
1827
|
+
if (!(messageBytes instanceof Uint8Array)) {
|
|
1828
|
+
throw new Error("messageBytes must be a Uint8Array");
|
|
1829
|
+
}
|
|
1830
|
+
return shake256(concatBytes(SCHEME_TAG_MSG, messageBytes), { dkLen: DIGEST_LEN });
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// src/signing/typedData.ts
|
|
1834
|
+
import { shake256 as shake2562 } from "@noble/hashes/sha3.js";
|
|
1835
|
+
var SLOT = 32;
|
|
1836
|
+
var ATOMIC_RE = /^(?:(address|bool|string|bytes)|(u?int)(\d+)|bytes(\d+)|(.+?)\[(\d*)\])$/;
|
|
1837
|
+
var MAX_TYPE_DEPTH = 12;
|
|
1838
|
+
function parseFieldType(type, types, depth = 0) {
|
|
1839
|
+
if (depth > MAX_TYPE_DEPTH) throw new Error(`type nesting too deep: ${type}`);
|
|
1840
|
+
if (Object.prototype.hasOwnProperty.call(types, type)) {
|
|
1841
|
+
return { kind: "ref", name: type };
|
|
1842
|
+
}
|
|
1843
|
+
const m = ATOMIC_RE.exec(type);
|
|
1844
|
+
if (!m) throw new Error(`unknown type: ${type}`);
|
|
1845
|
+
const [, atomic, intKind, intWidthStr, bytesWidthStr, innerType, sizeStr] = m;
|
|
1846
|
+
if (atomic) return { kind: atomic };
|
|
1847
|
+
if (intKind) {
|
|
1848
|
+
const width = Number(intWidthStr);
|
|
1849
|
+
if (!Number.isInteger(width) || width < 8 || width > 256 || width % 8 !== 0) {
|
|
1850
|
+
throw new Error(`invalid int width: ${type}`);
|
|
1851
|
+
}
|
|
1852
|
+
return { kind: intKind === "uint" ? "uintN" : "intN", width };
|
|
1853
|
+
}
|
|
1854
|
+
if (bytesWidthStr) {
|
|
1855
|
+
const width = Number(bytesWidthStr);
|
|
1856
|
+
if (!Number.isInteger(width) || width < 1 || width > 32) {
|
|
1857
|
+
throw new Error(`invalid bytesN width: ${type}`);
|
|
1858
|
+
}
|
|
1859
|
+
return { kind: "bytesN", width };
|
|
1860
|
+
}
|
|
1861
|
+
if (innerType !== void 0) {
|
|
1862
|
+
parseFieldType(innerType, types, depth + 1);
|
|
1863
|
+
const size = sizeStr ? Number(sizeStr) : void 0;
|
|
1864
|
+
if (sizeStr && (!Number.isInteger(size) || size <= 0)) {
|
|
1865
|
+
throw new Error(`invalid array size: ${type}`);
|
|
1866
|
+
}
|
|
1867
|
+
return { kind: "array", inner: innerType, size };
|
|
1868
|
+
}
|
|
1869
|
+
throw new Error(`unhandled type: ${type}`);
|
|
1870
|
+
}
|
|
1871
|
+
function baseTypeName(type) {
|
|
1872
|
+
return type.replace(/(\[\d*\])+$/, "");
|
|
1873
|
+
}
|
|
1874
|
+
function collectDependencies(primary, types) {
|
|
1875
|
+
if (!Object.prototype.hasOwnProperty.call(types, primary)) {
|
|
1876
|
+
throw new Error(`unknown primary type: ${primary}`);
|
|
1877
|
+
}
|
|
1878
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1879
|
+
const visit = (name, path) => {
|
|
1880
|
+
if (path.includes(name)) {
|
|
1881
|
+
throw new Error(`cyclic type reference: ${[...path, name].join(" -> ")}`);
|
|
1882
|
+
}
|
|
1883
|
+
if (visited.has(name)) return;
|
|
1884
|
+
visited.add(name);
|
|
1885
|
+
for (const f of types[name]) {
|
|
1886
|
+
const base = baseTypeName(f.type);
|
|
1887
|
+
if (Object.prototype.hasOwnProperty.call(types, base)) {
|
|
1888
|
+
visit(base, [...path, name]);
|
|
1889
|
+
}
|
|
1890
|
+
parseFieldType(f.type, types);
|
|
1891
|
+
}
|
|
1892
|
+
};
|
|
1893
|
+
visit(primary, []);
|
|
1894
|
+
return visited;
|
|
1895
|
+
}
|
|
1896
|
+
function validateTypeMap(types) {
|
|
1897
|
+
for (const [name, def] of Object.entries(types)) {
|
|
1898
|
+
if (!Array.isArray(def) || def.length === 0) {
|
|
1899
|
+
throw new Error(`empty or invalid struct: ${name}`);
|
|
1900
|
+
}
|
|
1901
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1902
|
+
for (const f of def) {
|
|
1903
|
+
if (!f || typeof f.name !== "string" || !f.name) {
|
|
1904
|
+
throw new Error(`bad field in ${name}`);
|
|
1905
|
+
}
|
|
1906
|
+
if (typeof f.type !== "string" || !f.type) {
|
|
1907
|
+
throw new Error(`bad field type in ${name}.${f.name}`);
|
|
1908
|
+
}
|
|
1909
|
+
if (seen.has(f.name)) {
|
|
1910
|
+
throw new Error(`duplicate field "${f.name}" in ${name}`);
|
|
1911
|
+
}
|
|
1912
|
+
seen.add(f.name);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
function encodeType(primary, types) {
|
|
1917
|
+
const deps = collectDependencies(primary, types);
|
|
1918
|
+
const others = [...deps].filter((n) => n !== primary).sort();
|
|
1919
|
+
return [primary, ...others].map((name) => {
|
|
1920
|
+
const fields = types[name];
|
|
1921
|
+
const inner = fields.map((f) => `${f.type} ${f.name}`).join(",");
|
|
1922
|
+
return `${name}(${inner})`;
|
|
1923
|
+
}).join("");
|
|
1924
|
+
}
|
|
1925
|
+
function typeHash(primary, types) {
|
|
1926
|
+
return shake2562(new TextEncoder().encode(encodeType(primary, types)), { dkLen: DIGEST_LEN });
|
|
1927
|
+
}
|
|
1928
|
+
function parseQAddress(addr) {
|
|
1929
|
+
if (typeof addr !== "string" || !/^Q[0-9a-fA-F]{40}$/.test(addr)) {
|
|
1930
|
+
throw new Error(`invalid Q-address: ${String(addr)}`);
|
|
1931
|
+
}
|
|
1932
|
+
return hexToBytes("0x" + addr.slice(1).toLowerCase());
|
|
1933
|
+
}
|
|
1934
|
+
function padLeft32(bytes) {
|
|
1935
|
+
if (bytes.length > SLOT) throw new Error("cannot pad: bytes > 32");
|
|
1936
|
+
const out = new Uint8Array(SLOT);
|
|
1937
|
+
out.set(bytes, SLOT - bytes.length);
|
|
1938
|
+
return out;
|
|
1939
|
+
}
|
|
1940
|
+
function padRight32(bytes) {
|
|
1941
|
+
if (bytes.length > SLOT) throw new Error("cannot pad: bytes > 32");
|
|
1942
|
+
const out = new Uint8Array(SLOT);
|
|
1943
|
+
out.set(bytes, 0);
|
|
1944
|
+
return out;
|
|
1945
|
+
}
|
|
1946
|
+
function bigIntToSlot(value, width, signed) {
|
|
1947
|
+
if (signed) {
|
|
1948
|
+
const limit = 1n << BigInt(width - 1);
|
|
1949
|
+
if (value >= limit || value < -limit) {
|
|
1950
|
+
throw new Error(`int${width} out of range: ${value}`);
|
|
1951
|
+
}
|
|
1952
|
+
} else {
|
|
1953
|
+
if (value < 0n) throw new Error(`uint${width} negative: ${value}`);
|
|
1954
|
+
const limit = 1n << BigInt(width);
|
|
1955
|
+
if (value >= limit) throw new Error(`uint${width} out of range: ${value}`);
|
|
1956
|
+
}
|
|
1957
|
+
const slotMask = (1n << 256n) - 1n;
|
|
1958
|
+
const repr = value < 0n ? value + (1n << 256n) & slotMask : value;
|
|
1959
|
+
const out = new Uint8Array(SLOT);
|
|
1960
|
+
let v = repr;
|
|
1961
|
+
for (let i = SLOT - 1; i >= 0; i--) {
|
|
1962
|
+
out[i] = Number(v & 0xffn);
|
|
1963
|
+
v >>= 8n;
|
|
1964
|
+
}
|
|
1965
|
+
return out;
|
|
1966
|
+
}
|
|
1967
|
+
function parseIntValue(v, typeLabel) {
|
|
1968
|
+
if (typeof v === "bigint") return v;
|
|
1969
|
+
if (typeof v === "string") {
|
|
1970
|
+
if (/^-?0x[0-9a-fA-F]+$/i.test(v)) {
|
|
1971
|
+
const isNegative = v.startsWith("-");
|
|
1972
|
+
const abs = BigInt(isNegative ? v.slice(1) : v);
|
|
1973
|
+
return isNegative ? -abs : abs;
|
|
1974
|
+
}
|
|
1975
|
+
if (!/^-?(0|[1-9]\d*)$/.test(v)) {
|
|
1976
|
+
throw new Error(`invalid ${typeLabel} string: ${v}`);
|
|
1977
|
+
}
|
|
1978
|
+
return BigInt(v);
|
|
1979
|
+
}
|
|
1980
|
+
if (typeof v === "number") {
|
|
1981
|
+
if (!Number.isInteger(v) || !Number.isSafeInteger(v)) {
|
|
1982
|
+
throw new Error(`unsafe ${typeLabel} number: ${v}, pass as string`);
|
|
1983
|
+
}
|
|
1984
|
+
return BigInt(v);
|
|
1985
|
+
}
|
|
1986
|
+
throw new Error(`unsupported ${typeLabel} value: ${typeof v}`);
|
|
1987
|
+
}
|
|
1988
|
+
function encodeField(type, value, types) {
|
|
1989
|
+
const parsed = parseFieldType(type, types);
|
|
1990
|
+
switch (parsed.kind) {
|
|
1991
|
+
case "ref":
|
|
1992
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1993
|
+
throw new Error(`struct field expects object: ${type}`);
|
|
1994
|
+
}
|
|
1995
|
+
return hashStruct(parsed.name, value, types);
|
|
1996
|
+
case "address":
|
|
1997
|
+
if (typeof value !== "string") {
|
|
1998
|
+
throw new Error(`address field expects string: ${typeof value}`);
|
|
1999
|
+
}
|
|
2000
|
+
return padLeft32(parseQAddress(value));
|
|
2001
|
+
case "bool":
|
|
2002
|
+
if (typeof value !== "boolean") {
|
|
2003
|
+
throw new Error(`bool field expects boolean: ${typeof value}`);
|
|
2004
|
+
}
|
|
2005
|
+
return padLeft32(new Uint8Array([value ? 1 : 0]));
|
|
2006
|
+
case "string":
|
|
2007
|
+
if (typeof value !== "string") {
|
|
2008
|
+
throw new Error(`string field expects string: ${typeof value}`);
|
|
2009
|
+
}
|
|
2010
|
+
return shake2562(new TextEncoder().encode(value), { dkLen: DIGEST_LEN });
|
|
2011
|
+
case "bytes":
|
|
2012
|
+
if (typeof value !== "string") {
|
|
2013
|
+
throw new Error(`bytes field expects 0x-hex string: ${typeof value}`);
|
|
2014
|
+
}
|
|
2015
|
+
return shake2562(hexToBytes(value), { dkLen: DIGEST_LEN });
|
|
2016
|
+
case "uintN":
|
|
2017
|
+
return bigIntToSlot(parseIntValue(value, `uint${parsed.width}`), parsed.width, false);
|
|
2018
|
+
case "intN":
|
|
2019
|
+
return bigIntToSlot(parseIntValue(value, `int${parsed.width}`), parsed.width, true);
|
|
2020
|
+
case "bytesN": {
|
|
2021
|
+
if (typeof value !== "string") {
|
|
2022
|
+
throw new Error(`bytes${parsed.width} expects 0x-hex string: ${typeof value}`);
|
|
2023
|
+
}
|
|
2024
|
+
const raw = hexToBytes(value);
|
|
2025
|
+
if (raw.length !== parsed.width) {
|
|
2026
|
+
throw new Error(`bytes${parsed.width} requires ${parsed.width} bytes, got ${raw.length}`);
|
|
2027
|
+
}
|
|
2028
|
+
return padRight32(raw);
|
|
2029
|
+
}
|
|
2030
|
+
case "array": {
|
|
2031
|
+
if (!Array.isArray(value)) {
|
|
2032
|
+
throw new Error(`array field expects array: ${typeof value}`);
|
|
2033
|
+
}
|
|
2034
|
+
if (parsed.size !== void 0 && value.length !== parsed.size) {
|
|
2035
|
+
throw new Error(`fixed array ${type} requires length ${parsed.size}, got ${value.length}`);
|
|
2036
|
+
}
|
|
2037
|
+
const chunks = value.map((v) => encodeField(parsed.inner, v, types));
|
|
2038
|
+
return shake2562(concatBytesArr(chunks), { dkLen: DIGEST_LEN });
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
function hashStruct(primary, data, types) {
|
|
2043
|
+
const fields = types[primary];
|
|
2044
|
+
if (!fields) throw new Error(`unknown struct: ${primary}`);
|
|
2045
|
+
const expected = new Set(fields.map((f) => f.name));
|
|
2046
|
+
for (const k of Object.keys(data)) {
|
|
2047
|
+
if (!expected.has(k)) throw new Error(`unknown field in ${primary}: ${k}`);
|
|
2048
|
+
}
|
|
2049
|
+
const parts = [typeHash(primary, types)];
|
|
2050
|
+
for (const f of fields) {
|
|
2051
|
+
if (!(f.name in data)) throw new Error(`missing field ${primary}.${f.name}`);
|
|
2052
|
+
parts.push(encodeField(f.type, data[f.name], types));
|
|
2053
|
+
}
|
|
2054
|
+
return shake2562(concatBytesArr(parts), { dkLen: DIGEST_LEN });
|
|
2055
|
+
}
|
|
2056
|
+
var RESERVED_DOMAIN_FIELDS = {
|
|
2057
|
+
name: "string",
|
|
2058
|
+
version: "string",
|
|
2059
|
+
chainId: "uint256",
|
|
2060
|
+
verifyingContract: "address",
|
|
2061
|
+
salt: "bytes32"
|
|
2062
|
+
};
|
|
2063
|
+
function validateDomainTypes(types) {
|
|
2064
|
+
const def = types.QRLDomain;
|
|
2065
|
+
if (!def) throw new Error("QRLDomain type is required");
|
|
2066
|
+
let hasName = false;
|
|
2067
|
+
for (const f of def) {
|
|
2068
|
+
const expected = RESERVED_DOMAIN_FIELDS[f.name];
|
|
2069
|
+
if (!expected) {
|
|
2070
|
+
throw new Error(
|
|
2071
|
+
`QRLDomain field "${f.name}" not in reserved set (name, version, chainId, verifyingContract, salt)`
|
|
2072
|
+
);
|
|
2073
|
+
}
|
|
2074
|
+
if (f.type !== expected) {
|
|
2075
|
+
throw new Error(`QRLDomain field "${f.name}" must be type "${expected}", got "${f.type}"`);
|
|
2076
|
+
}
|
|
2077
|
+
if (f.name === "name") hasName = true;
|
|
2078
|
+
}
|
|
2079
|
+
if (!hasName) throw new Error("QRLDomain.name is required");
|
|
2080
|
+
}
|
|
2081
|
+
function validatePayloadReachability(primary, types) {
|
|
2082
|
+
const reachable = /* @__PURE__ */ new Set();
|
|
2083
|
+
for (const root of ["QRLDomain", primary]) {
|
|
2084
|
+
for (const t of collectDependencies(root, types)) reachable.add(t);
|
|
2085
|
+
}
|
|
2086
|
+
for (const k of Object.keys(types)) {
|
|
2087
|
+
if (!reachable.has(k)) throw new Error(`unused referenced type: ${k}`);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
function computeTypedDataDigest(payload) {
|
|
2091
|
+
if (!payload || typeof payload !== "object") {
|
|
2092
|
+
throw new Error("invalid typed data payload");
|
|
2093
|
+
}
|
|
2094
|
+
validateTypeMap(payload.types);
|
|
2095
|
+
validateDomainTypes(payload.types);
|
|
2096
|
+
validatePayloadReachability(payload.primaryType, payload.types);
|
|
2097
|
+
const domainHash = hashStruct("QRLDomain", payload.domain, payload.types);
|
|
2098
|
+
const messageHash = hashStruct(payload.primaryType, payload.message, payload.types);
|
|
2099
|
+
return shake2562(concatBytes(SCHEME_TAG_TYPED, domainHash, messageHash), { dkLen: DIGEST_LEN });
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
// src/signing/verify.ts
|
|
2103
|
+
import * as mldsa from "@theqrl/mldsa87";
|
|
2104
|
+
function bytesOrHex(v) {
|
|
2105
|
+
if (v instanceof Uint8Array) return v;
|
|
2106
|
+
if (typeof v === "string") return hexToBytes(v);
|
|
2107
|
+
throw new Error("expected Uint8Array or 0x-hex string");
|
|
2108
|
+
}
|
|
2109
|
+
function verifyMessage({
|
|
2110
|
+
signature,
|
|
2111
|
+
publicKey,
|
|
2112
|
+
messageBytes
|
|
2113
|
+
}) {
|
|
2114
|
+
try {
|
|
2115
|
+
const sig = bytesOrHex(signature);
|
|
2116
|
+
const pk = bytesOrHex(publicKey);
|
|
2117
|
+
const msg = bytesOrHex(messageBytes);
|
|
2118
|
+
const digest = computeMessageDigest(msg);
|
|
2119
|
+
return mldsa.cryptoSignVerify(sig, digest, pk, SCHEME_TAG_MSG);
|
|
2120
|
+
} catch {
|
|
2121
|
+
return false;
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
function verifyTypedData({ signature, publicKey, payload }) {
|
|
2125
|
+
try {
|
|
2126
|
+
const sig = bytesOrHex(signature);
|
|
2127
|
+
const pk = bytesOrHex(publicKey);
|
|
2128
|
+
const digest = computeTypedDataDigest(payload);
|
|
2129
|
+
return mldsa.cryptoSignVerify(sig, digest, pk, SCHEME_TAG_TYPED);
|
|
2130
|
+
} catch {
|
|
2131
|
+
return false;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
1574
2134
|
export {
|
|
1575
2135
|
BLOB_LEN,
|
|
1576
2136
|
CID_LEN,
|
|
1577
2137
|
ConnectionManager,
|
|
1578
2138
|
ConnectionStatus,
|
|
1579
2139
|
DEFAULT_RELAY_URL,
|
|
2140
|
+
DIGEST_LEN,
|
|
1580
2141
|
FP_LEN,
|
|
1581
2142
|
KeyExchange,
|
|
1582
2143
|
KeyExchangeMessageType,
|
|
@@ -1584,16 +2145,32 @@ export {
|
|
|
1584
2145
|
PROTOCOL_VERSION,
|
|
1585
2146
|
QRLConnectProvider as QRLConnect,
|
|
1586
2147
|
QRLConnectProvider,
|
|
2148
|
+
QRL_CONNECT_PROVIDER_INFO,
|
|
1587
2149
|
RESTRICTED_METHODS,
|
|
2150
|
+
SCHEME_TAG_MSG,
|
|
2151
|
+
SCHEME_TAG_TYPED,
|
|
2152
|
+
SCHEME_VERSION_MSG,
|
|
2153
|
+
SCHEME_VERSION_TYPED,
|
|
1588
2154
|
SocketClient,
|
|
1589
2155
|
UNRESTRICTED_METHODS,
|
|
2156
|
+
bytesToHex,
|
|
1590
2157
|
cidFromString,
|
|
1591
2158
|
cidRandom,
|
|
1592
2159
|
cidToString,
|
|
1593
2160
|
computeFingerprint,
|
|
2161
|
+
computeMessageDigest,
|
|
2162
|
+
computeTypedDataDigest,
|
|
2163
|
+
concatBytes,
|
|
2164
|
+
encodeField,
|
|
2165
|
+
encodeType,
|
|
1594
2166
|
fingerprintEquals,
|
|
1595
2167
|
generateConnectionURI,
|
|
1596
2168
|
getAppStoreUrl,
|
|
2169
|
+
hashStruct,
|
|
2170
|
+
hexToBytes,
|
|
1597
2171
|
isMobileBrowser,
|
|
1598
|
-
parseConnectionURI
|
|
2172
|
+
parseConnectionURI,
|
|
2173
|
+
typeHash,
|
|
2174
|
+
verifyMessage,
|
|
2175
|
+
verifyTypedData
|
|
1599
2176
|
};
|