@tezos-x/octez.connect-dapp 5.0.0-beta.6 → 5.0.1

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.
@@ -16,17 +16,20 @@ exports.DAppClient = void 0;
16
16
  const bs58check_1 = __importDefault(require("bs58check"));
17
17
  const octez_connect_types_1 = require("@tezos-x/octez.connect-types");
18
18
  const octez_connect_core_1 = require("@tezos-x/octez.connect-core");
19
+ const octez_connect_blockchain_tezos_1 = require("@tezos-x/octez.connect-blockchain-tezos");
19
20
  const octez_connect_utils_1 = require("@tezos-x/octez.connect-utils");
21
+ const octez_connect_transport_postmessage_1 = require("@tezos-x/octez.connect-transport-postmessage");
22
+ const octez_connect_ui_1 = require("@tezos-x/octez.connect-ui");
23
+ const octez_connect_transport_walletconnect_1 = require("@tezos-x/octez.connect-transport-walletconnect");
20
24
  const beacon_message_events_1 = require("../beacon-message-events");
21
25
  const events_1 = require("../events");
22
26
  const tzkt_blockexplorer_1 = require("../utils/tzkt-blockexplorer");
23
27
  const DappPostMessageTransport_1 = require("../transports/DappPostMessageTransport");
24
28
  const DappP2PTransport_1 = require("../transports/DappP2PTransport");
25
29
  const DappWalletConnectTransport_1 = require("../transports/DappWalletConnectTransport");
26
- const octez_connect_transport_postmessage_1 = require("@tezos-x/octez.connect-transport-postmessage");
27
- const octez_connect_ui_1 = require("@tezos-x/octez.connect-ui");
28
- const octez_connect_transport_walletconnect_1 = require("@tezos-x/octez.connect-transport-walletconnect");
29
30
  const logger = new octez_connect_core_1.Logger('DAppClient');
31
+ // Surfaced when a permission response yields zero account records.
32
+ const EMPTY_PERMISSION_ACCOUNTS_MESSAGE = 'Wallet permission response did not include any accounts. Please re-pair the wallet and try again.';
30
33
  /**
31
34
  * @publicapi
32
35
  *
@@ -40,6 +43,12 @@ class DAppClient extends octez_connect_core_1.Client {
40
43
  var _a, _b, _c, _d, _e, _f, _g, _h;
41
44
  super(Object.assign({ storage: config && config.storage ? config.storage : new octez_connect_core_1.LocalStorage() }, config));
42
45
  this.events = new events_1.BeaconEventHandler();
46
+ /**
47
+ * WalletConnect is opt-in: only enabled when `walletConnectOptions` is provided
48
+ * and `disableWalletConnect` is not set. When false, no WC transport is built,
49
+ * listened to, or offered for pairing, and no default projectId is applied.
50
+ */
51
+ this.isWalletConnectEnabled = false;
43
52
  this.isGetActiveAccountHandled = false;
44
53
  this.openRequestsOtherTabs = new Set();
45
54
  /**
@@ -60,8 +69,14 @@ class DAppClient extends octez_connect_core_1.Client {
60
69
  this.debounceSetActiveAccount = false;
61
70
  this.multiTabChannel = new octez_connect_core_1.MultiTabChannel('octez.connect-sdk-channel', this.onBCMessageHandler.bind(this), this.onElectedLeaderhandler.bind(this));
62
71
  this.description = config.description;
63
- this.wcProjectId = ((_a = config.walletConnectOptions) === null || _a === void 0 ? void 0 : _a.projectId) || '24469fd0a06df227b6e5f7dc7de0ff4f';
64
- this.wcRelayUrl = (_b = config.walletConnectOptions) === null || _b === void 0 ? void 0 : _b.relayUrl;
72
+ // WalletConnect is opt-in: only when walletConnectOptions is provided and not
73
+ // explicitly disabled. Don't apply the default projectId otherwise (#32).
74
+ this.isWalletConnectEnabled =
75
+ Boolean(config.walletConnectOptions) && !config.disableWalletConnect;
76
+ this.wcProjectId = this.isWalletConnectEnabled
77
+ ? ((_a = config.walletConnectOptions) === null || _a === void 0 ? void 0 : _a.projectId) || '24469fd0a06df227b6e5f7dc7de0ff4f'
78
+ : undefined;
79
+ this.wcRelayUrl = this.isWalletConnectEnabled ? (_b = config.walletConnectOptions) === null || _b === void 0 ? void 0 : _b.relayUrl : undefined;
65
80
  this.featuredWallets = config.featuredWallets;
66
81
  this.events = new events_1.BeaconEventHandler(config.eventHandlers, (_c = config.disableDefaultEvents) !== null && _c !== void 0 ? _c : false);
67
82
  this.blockExplorer = (_d = config.blockExplorer) !== null && _d !== void 0 ? _d : new tzkt_blockexplorer_1.TzktBlockExplorer();
@@ -73,8 +88,14 @@ class DAppClient extends octez_connect_core_1.Client {
73
88
  this.appMetadataManager = new octez_connect_core_1.AppMetadataManager(this.storage);
74
89
  this.storageValidator = new octez_connect_core_1.StorageValidator(this.storage);
75
90
  this.enableAppSwitching =
76
- config.enableAppSwitching === undefined ? true : !!config.enableAppSwitching;
91
+ config.enableAppSwitching === undefined ? true : Boolean(config.enableAppSwitching);
77
92
  this.enableMetrics = config.enableMetrics ? true : false;
93
+ this.requiredMinimumVersion = (0, octez_connect_core_1.resolveRequiredMinimumVersion)(config.requiredMinimumVersion);
94
+ // Tezos is the default chain: the wrapped-only pipeline routes every
95
+ // request/response through the registry handler, so registration is no
96
+ // longer a consumer obligation. addBlockchain stays public — a later
97
+ // registration under 'tezos' overrides this default.
98
+ this.addBlockchain(new octez_connect_blockchain_tezos_1.TezosBlockchain());
78
99
  // Subscribe to storage changes and update the active account if it changes on other tabs
79
100
  this.storage.subscribeToStorageChanged((event) => __awaiter(this, void 0, void 0, function* () {
80
101
  if (event.eventType === 'storageCleared') {
@@ -94,7 +115,7 @@ class DAppClient extends octez_connect_core_1.Client {
94
115
  return;
95
116
  }
96
117
  if (event.key === this.storage.getPrefixedKey(octez_connect_types_1.StorageKey.ENABLE_METRICS)) {
97
- this.enableMetrics = !!(yield this.storage.get(octez_connect_types_1.StorageKey.ENABLE_METRICS));
118
+ this.enableMetrics = Boolean(yield this.storage.get(octez_connect_types_1.StorageKey.ENABLE_METRICS));
98
119
  return;
99
120
  }
100
121
  if (event.key === this.storage.getPrefixedKey(octez_connect_types_1.StorageKey.BEACON_SDK_SECRET_SEED)) {
@@ -124,15 +145,31 @@ class DAppClient extends octez_connect_core_1.Client {
124
145
  this.events.emit(events_1.BeaconEvent.INVALID_ACCOUNT_DEACTIVATED);
125
146
  return undefined;
126
147
  }));
127
- this.handleResponse = (message, connectionInfo) => __awaiter(this, void 0, void 0, function* () {
148
+ this.handleResponse = (wireMessage, connectionInfo) => __awaiter(this, void 0, void 0, function* () {
128
149
  var _a;
129
- const typedMessage = (0, octez_connect_core_1.usesWrappedMessages)(message.version)
130
- ? message.message
131
- : message;
132
- let appMetadata = (0, octez_connect_core_1.usesWrappedMessages)(message.version)
133
- ? (_a = typedMessage.blockchainData) === null || _a === void 0 ? void 0 : _a.appMetadata
134
- : typedMessage.appMetadata;
135
- if (!appMetadata && (0, octez_connect_core_1.usesWrappedMessages)(message.version)) {
150
+ // Negotiated wire: a flat arrival is the legacy v2 dialect (from a
151
+ // v4.8.x wallet answering a flat request) and is already the shape the
152
+ // pipeline consumes. Wrapped arrivals carry Tezos payloads that are
153
+ // normalized back to those same flat shapes — the wire dialect stays
154
+ // invisible to integrators. Non-Tezos wrapped payloads keep the
155
+ // pass-through of the generic permissionRequest/request API.
156
+ const isWrapped = (0, octez_connect_core_1.usesWrappedMessages)(wireMessage.version);
157
+ // Issue #33: a V3-versioned message can arrive without its wrapped payload.
158
+ // Drop it safely instead of dereferencing an undefined payload, which would
159
+ // throw an unhandled rejection inside the transport subscription callback.
160
+ if (isWrapped && !wireMessage.message) {
161
+ logger.log('handleResponse', 'Received wrapped message with undefined payload; dropping', wireMessage);
162
+ return;
163
+ }
164
+ const normalized = isWrapped
165
+ ? this.normalizeWrappedTezosMessage(wireMessage)
166
+ : wireMessage;
167
+ const message = normalized !== null && normalized !== void 0 ? normalized : wireMessage;
168
+ const typedMessage = normalized !== null && normalized !== void 0 ? normalized : wireMessage.message;
169
+ let appMetadata = normalized
170
+ ? typedMessage.appMetadata
171
+ : (_a = typedMessage.blockchainData) === null || _a === void 0 ? void 0 : _a.appMetadata;
172
+ if (!appMetadata && !normalized) {
136
173
  const storedMetadata = yield Promise.all([
137
174
  this.storage.get(octez_connect_types_1.StorageKey.TRANSPORT_P2P_PEERS_DAPP),
138
175
  this.storage.get(octez_connect_types_1.StorageKey.TRANSPORT_WALLETCONNECT_PEERS_DAPP),
@@ -153,10 +190,15 @@ class DAppClient extends octez_connect_core_1.Client {
153
190
  }
154
191
  }
155
192
  if (this.openRequestsOtherTabs.has(message.id)) {
193
+ // Relay the ORIGINAL wrapped envelope, not the normalized flat
194
+ // message: the receiving tab funnels this straight back into its own
195
+ // handleResponse, whose wrapped-only contract would drop a flat
196
+ // arrival (version '4' with no `.message` payload) and leave the
197
+ // other tab's request pending forever.
156
198
  this.multiTabChannel.postMessage({
157
199
  type: 'RESPONSE',
158
200
  data: {
159
- message,
201
+ message: wireMessage,
160
202
  connectionInfo
161
203
  },
162
204
  id: message.id
@@ -278,7 +320,7 @@ class DAppClient extends octez_connect_core_1.Client {
278
320
  }
279
321
  }))
280
322
  .catch((err) => logger.error(err.message));
281
- this.sendMetrics('enable-metrics?' + this.addQueryParam('version', octez_connect_core_1.SDK_VERSION), undefined, (res) => {
323
+ this.sendMetrics(`enable-metrics?${this.addQueryParam('version', octez_connect_core_1.SDK_VERSION)}`, undefined, (res) => {
282
324
  if (!res.ok) {
283
325
  res.status === 426
284
326
  ? console.error('Metrics are no longer supported for this version, please upgrade.')
@@ -402,22 +444,32 @@ class DAppClient extends octez_connect_core_1.Client {
402
444
  yield this.addListener(this.postMessageTransport);
403
445
  this.p2pTransport = new DappP2PTransport_1.DappP2PTransport(this.name, keyPair, this.storage, this.matrixNodes, this.iconUrl, this.appUrl);
404
446
  yield this.addListener(this.p2pTransport);
405
- const wcOptions = {
406
- projectId: this.wcProjectId,
407
- relayUrl: this.wcRelayUrl,
408
- metadata: {
409
- name: this.name,
410
- description: (_a = this.description) !== null && _a !== void 0 ? _a : '',
411
- url: (_b = this.appUrl) !== null && _b !== void 0 ? _b : '',
412
- icons: this.iconUrl ? [this.iconUrl] : []
447
+ // WalletConnect is opt-in (#32): skip building/listening the transport when
448
+ // no walletConnectOptions were provided or it was explicitly disabled.
449
+ if (this.isWalletConnectEnabled) {
450
+ const wcOptions = {
451
+ projectId: this.wcProjectId,
452
+ relayUrl: this.wcRelayUrl,
453
+ metadata: {
454
+ name: this.name,
455
+ description: (_a = this.description) !== null && _a !== void 0 ? _a : '',
456
+ url: (_b = this.appUrl) !== null && _b !== void 0 ? _b : '',
457
+ icons: this.iconUrl ? [this.iconUrl] : []
458
+ }
459
+ };
460
+ this.walletConnectTransport = new DappWalletConnectTransport_1.DappWalletConnectTransport(this.name, keyPair, this.storage, {
461
+ network: this.network.type,
462
+ opts: wcOptions
463
+ }, this.checkIfBCLeaderExists.bind(this));
464
+ // Apply proposal networks requested before the transport existed
465
+ // (requestPermissions runs before init creates the transport).
466
+ if (this.pendingWcProposalNetworks) {
467
+ this.walletConnectTransport.setProposalNetworks(this.pendingWcProposalNetworks);
468
+ this.pendingWcProposalNetworks = undefined;
413
469
  }
414
- };
415
- this.walletConnectTransport = new DappWalletConnectTransport_1.DappWalletConnectTransport(this.name, keyPair, this.storage, {
416
- network: this.network.type,
417
- opts: wcOptions
418
- }, this.checkIfBCLeaderExists.bind(this));
419
- this.initEvents();
420
- yield this.addListener(this.walletConnectTransport);
470
+ this.initEvents();
471
+ yield this.addListener(this.walletConnectTransport);
472
+ }
421
473
  });
422
474
  }
423
475
  initEvents() {
@@ -531,7 +583,9 @@ class DAppClient extends octez_connect_core_1.Client {
531
583
  }
532
584
  };
533
585
  yield this.initInternalTransports();
534
- if (!this.postMessageTransport || !this.p2pTransport || !this.walletConnectTransport) {
586
+ if (!this.postMessageTransport ||
587
+ !this.p2pTransport ||
588
+ (this.isWalletConnectEnabled && !this.walletConnectTransport)) {
535
589
  return;
536
590
  }
537
591
  this.postMessageTransport.connect().then().catch(console.error);
@@ -544,9 +598,20 @@ class DAppClient extends octez_connect_core_1.Client {
544
598
  else if (origin === octez_connect_types_1.Origin.P2P) {
545
599
  resolve(yield _super.init.call(this, this.p2pTransport));
546
600
  }
547
- else if (origin === octez_connect_types_1.Origin.WALLETCONNECT) {
601
+ else if (origin === octez_connect_types_1.Origin.WALLETCONNECT && this.walletConnectTransport) {
548
602
  resolve(yield _super.init.call(this, this.walletConnectTransport));
549
603
  }
604
+ else {
605
+ // The persisted active account was paired over WalletConnect but WC
606
+ // is now opt-out/disabled (#32), so there is no matching transport to
607
+ // restore. Without this branch none of the conditions above call
608
+ // resolve() and the init promise created above never settles, so
609
+ // init() (and every later call awaiting it) hangs forever. Resolve on
610
+ // the always-available P2P transport so the SDK stays usable and the
611
+ // stale WC account can be re-paired.
612
+ logger.warn('init', 'Active account was paired over WalletConnect but WC is disabled; falling back to the P2P transport');
613
+ resolve(yield _super.init.call(this, this.p2pTransport));
614
+ }
550
615
  }
551
616
  else {
552
617
  const p2pTransport = this.p2pTransport;
@@ -582,8 +647,7 @@ class DAppClient extends octez_connect_core_1.Client {
582
647
  resolve(octez_connect_types_1.TransportType.P2P);
583
648
  })
584
649
  .catch(console.error);
585
- walletConnectTransport
586
- .listenForNewPeer((peer) => {
650
+ walletConnectTransport === null || walletConnectTransport === void 0 ? void 0 : walletConnectTransport.listenForNewPeer((peer) => {
587
651
  logger.log('init', 'walletconnect transport peer connected', peer);
588
652
  this.analytics.track('event', 'DAppClient', 'WalletConnect Wallet connected', {
589
653
  peerName: peer.name
@@ -595,8 +659,7 @@ class DAppClient extends octez_connect_core_1.Client {
595
659
  this.setTransport(this.walletConnectTransport).catch(console.error);
596
660
  stopListening();
597
661
  resolve(octez_connect_types_1.TransportType.WALLETCONNECT);
598
- })
599
- .catch(console.error);
662
+ }).catch(console.error);
600
663
  octez_connect_transport_postmessage_1.PostMessageTransport.getAvailableExtensions()
601
664
  .then((extensions) => __awaiter(this, void 0, void 0, function* () {
602
665
  this.analytics.track('event', 'DAppClient', 'Extensions detected', { extensions });
@@ -612,7 +675,7 @@ class DAppClient extends octez_connect_core_1.Client {
612
675
  yield Promise.all([
613
676
  postMessageTransport.disconnect(),
614
677
  // p2pTransport.disconnect(), do not abort connection manually
615
- walletConnectTransport.disconnect()
678
+ walletConnectTransport === null || walletConnectTransport === void 0 ? void 0 : walletConnectTransport.disconnect()
616
679
  ]);
617
680
  this.postMessageTransport = this.walletConnectTransport = this.p2pTransport = undefined;
618
681
  this._activeAccount.isResolved() && this.clearActiveAccount();
@@ -647,7 +710,10 @@ class DAppClient extends octez_connect_core_1.Client {
647
710
  resolve(yield serializer.serialize(yield p2pTransport.getPairingRequestInfo()));
648
711
  }));
649
712
  const walletConnectPeerInfo = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
650
- resolve((yield walletConnectTransport.getPairingRequestInfo()).uri);
713
+ // When WC is disabled there is no transport to pair through (#32).
714
+ resolve(walletConnectTransport
715
+ ? (yield walletConnectTransport.getPairingRequestInfo()).uri
716
+ : '');
651
717
  }));
652
718
  const postmessagePeerInfo = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
653
719
  resolve(yield serializer.serialize(yield postMessageTransport.getPairingRequestInfo()));
@@ -713,7 +779,6 @@ class DAppClient extends octez_connect_core_1.Client {
713
779
  */
714
780
  setActiveAccount(account) {
715
781
  return __awaiter(this, void 0, void 0, function* () {
716
- var _a;
717
782
  if (!this.isGetActiveAccountHandled) {
718
783
  console.warn(`An active account has been received, but no active subscription was found for BeaconEvent.ACTIVE_ACCOUNT_SET.
719
784
  For more information, visit: https://octez-connect.tezos.com/guides/migration-guide`);
@@ -753,7 +818,9 @@ class DAppClient extends octez_connect_core_1.Client {
753
818
  errorType: octez_connect_types_1.BeaconErrorType.ABORTED_ERROR,
754
819
  id,
755
820
  senderId: '',
756
- version: '2'
821
+ // SDK-synthesized rejection consumed in-process (never hits the
822
+ // wire); stamped with the wrapped baseline for consistency.
823
+ version: (0, octez_connect_core_1.negotiateEnvelopeVersion)(undefined)
757
824
  });
758
825
  });
759
826
  this.openRequests.clear();
@@ -784,9 +851,18 @@ class DAppClient extends octez_connect_core_1.Client {
784
851
  else if (origin === octez_connect_types_1.Origin.P2P) {
785
852
  yield this.setTransport(this.p2pTransport);
786
853
  }
787
- else if (origin === octez_connect_types_1.Origin.WALLETCONNECT) {
854
+ else if (origin === octez_connect_types_1.Origin.WALLETCONNECT && this.walletConnectTransport) {
788
855
  yield this.setTransport(this.walletConnectTransport);
789
- (_a = this.walletConnectTransport) === null || _a === void 0 ? void 0 : _a.forceUpdate('INIT');
856
+ this.walletConnectTransport.forceUpdate('INIT');
857
+ }
858
+ else if (origin === octez_connect_types_1.Origin.WALLETCONNECT) {
859
+ // WalletConnect is opt-out/disabled (#32) but this persisted account was
860
+ // paired over WC. There is no WC transport to bind; setting the transport
861
+ // to `undefined` would leave the client unable to send any request. Fall
862
+ // back to the always-available P2P transport so the SDK stays usable
863
+ // (the stale WC account can be re-paired).
864
+ logger.warn('setActiveAccount', 'Active account was paired over WalletConnect but WC is disabled; falling back to the P2P transport');
865
+ yield this.setTransport(this.p2pTransport);
790
866
  }
791
867
  if (this._transport.isResolved()) {
792
868
  const transport = yield this.transport;
@@ -867,7 +943,7 @@ class DAppClient extends octez_connect_core_1.Client {
867
943
  });
868
944
  }
869
945
  addQueryParam(paramName, paramValue) {
870
- return paramName + '=' + paramValue;
946
+ return `${paramName}=${paramValue}`;
871
947
  }
872
948
  buildPayload(action, status) {
873
949
  return __awaiter(this, void 0, void 0, function* () {
@@ -1080,6 +1156,7 @@ class DAppClient extends octez_connect_core_1.Client {
1080
1156
  }
1081
1157
  permissionRequest(input) {
1082
1158
  return __awaiter(this, void 0, void 0, function* () {
1159
+ var _a;
1083
1160
  logger.log('permissionRequest', input);
1084
1161
  const blockchain = this.blockchains.get(input.blockchainIdentifier);
1085
1162
  if (!blockchain) {
@@ -1094,21 +1171,32 @@ class DAppClient extends octez_connect_core_1.Client {
1094
1171
  logger.time(false, logId);
1095
1172
  this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'start'));
1096
1173
  logger.log('RESPONSE V3', response, connectionInfo);
1097
- const partialAccountInfos = yield blockchain.getAccountInfosFromPermissionResponse(response.message);
1098
- const accountInfo = {
1099
- accountIdentifier: partialAccountInfos[0].accountId,
1174
+ // The version stamp lives on the wrapper envelope, not the inner payload;
1175
+ // reading response.message.version would always miss and collapse to '0',
1176
+ // misrouting v4 responses when no peer is persisted yet.
1177
+ const walletPeerV3 = yield this.getPeer();
1178
+ const walletPeerVersionV3 = (_a = walletPeerV3 === null || walletPeerV3 === void 0 ? void 0 : walletPeerV3.version) !== null && _a !== void 0 ? _a : response.version;
1179
+ const partialAccountInfos = yield blockchain.getAccountInfosFromPermissionResponse(response.message, walletPeerVersionV3);
1180
+ const blockchainDataScopes = response.message.blockchainData.scopes;
1181
+ const accountInfos = partialAccountInfos.map((p) => ({
1182
+ accountIdentifier: p.accountId,
1100
1183
  senderId: response.senderId,
1101
1184
  origin: {
1102
1185
  type: connectionInfo.origin,
1103
1186
  id: connectionInfo.id
1104
1187
  },
1105
- address: partialAccountInfos[0].address, // Store all addresses
1106
- publicKey: partialAccountInfos[0].publicKey,
1107
- scopes: response.message.blockchainData.scopes,
1188
+ address: p.address,
1189
+ publicKey: p.publicKey,
1190
+ network: p.network,
1191
+ scopes: blockchainDataScopes !== null && blockchainDataScopes !== void 0 ? blockchainDataScopes : p.scopes,
1108
1192
  connectedAt: new Date().getTime(),
1109
1193
  chainData: response.message.blockchainData
1110
- };
1111
- yield this.accountManager.addAccount(accountInfo);
1194
+ }));
1195
+ yield this.accountManager.addAccounts(accountInfos);
1196
+ const accountInfo = accountInfos[0];
1197
+ if (!accountInfo) {
1198
+ throw new Error(EMPTY_PERMISSION_ACCOUNTS_MESSAGE);
1199
+ }
1112
1200
  yield this.setActiveAccount(accountInfo);
1113
1201
  yield blockchain.handleResponse({
1114
1202
  request,
@@ -1121,7 +1209,7 @@ class DAppClient extends octez_connect_core_1.Client {
1121
1209
  yield this.notifySuccess(request, {
1122
1210
  account: accountInfo,
1123
1211
  output: {
1124
- address: partialAccountInfos[0].address,
1212
+ address: accountInfo.address,
1125
1213
  network: { type: 'substrate' },
1126
1214
  scopes: []
1127
1215
  },
@@ -1178,6 +1266,7 @@ class DAppClient extends octez_connect_core_1.Client {
1178
1266
  */
1179
1267
  requestPermissions(input) {
1180
1268
  return __awaiter(this, void 0, void 0, function* () {
1269
+ var _a, _b;
1181
1270
  if (input === null || input === void 0 ? void 0 : input.network) {
1182
1271
  throw new Error('[BEACON] the "network" property is no longer accepted in input. Please provide it when instantiating DAppClient.');
1183
1272
  }
@@ -1189,6 +1278,39 @@ class DAppClient extends octez_connect_core_1.Client {
1189
1278
  ? input.scopes
1190
1279
  : [octez_connect_types_1.PermissionScope.OPERATION_REQUEST, octez_connect_types_1.PermissionScope.SIGN]
1191
1280
  };
1281
+ // Dedupe by the normalized CAIP-2 chainId so equivalent input entries
1282
+ // ('NetX…' and 'tezos:NetX…') collapse to one canonical wire entry.
1283
+ if ((input === null || input === void 0 ? void 0 : input.networks) && input.networks.length > 0) {
1284
+ const dedupedNetworks = Array.from(new Map(input.networks.map((n) => {
1285
+ const chainId = (0, octez_connect_core_1.normalizeTezosCaip2)(n.chainId);
1286
+ return [chainId, Object.assign(Object.assign({}, n), { chainId })];
1287
+ })).values());
1288
+ request.networks = dedupedNetworks;
1289
+ // Multi-network over WalletConnect travels via the session proposal,
1290
+ // not the wire payload (WC peers have no beacon version handshake).
1291
+ // Map each requested chain id to its named network for the proposal;
1292
+ // ids without a static genesis mapping are skipped here (the
1293
+ // NetworksUnsupportedBeaconError guard above reports them when the
1294
+ // dApp opted into requiredMinimumVersion '4').
1295
+ const proposalNetworkTypes = [];
1296
+ for (const { chainId } of dedupedNetworks) {
1297
+ const networkType = (0, octez_connect_core_1.networkTypeFromTezosCaip2)(chainId);
1298
+ if (networkType === undefined) {
1299
+ logger.debug('requestPermissions', `No static network mapping for "${chainId}"; excluded from the WalletConnect proposal`);
1300
+ continue;
1301
+ }
1302
+ proposalNetworkTypes.push(networkType);
1303
+ }
1304
+ if (this.walletConnectTransport) {
1305
+ this.walletConnectTransport.setProposalNetworks(proposalNetworkTypes);
1306
+ }
1307
+ else {
1308
+ // The WalletConnect transport is created lazily inside init() (run
1309
+ // by makeRequest below, before the pairing proposal is built); stash
1310
+ // the networks and apply them right after creation.
1311
+ this.pendingWcProposalNetworks = proposalNetworkTypes;
1312
+ }
1313
+ }
1192
1314
  this.analytics.track('event', 'DAppClient', 'Permission requested');
1193
1315
  this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'start'));
1194
1316
  const logId = `makeRequest ${Date.now()}`;
@@ -1201,10 +1323,60 @@ class DAppClient extends octez_connect_core_1.Client {
1201
1323
  this.sendMetrics('performance-metrics/save', yield this.buildPayload('connect', 'success'));
1202
1324
  logger.log('requestPermissions', '######## MESSAGE #######');
1203
1325
  logger.log('requestPermissions', message);
1204
- const accountInfo = yield this.onNewAccount(message, connectionInfo);
1205
- logger.log('requestPermissions', '######## ACCOUNT INFO #######');
1206
- logger.log('requestPermissions', JSON.stringify(accountInfo));
1207
- yield this.accountManager.addAccount(accountInfo);
1326
+ // walletPeer.version is the negotiated pairing version; fall back to the
1327
+ // response envelope version when no peer is persisted yet.
1328
+ const walletPeer = yield this.getPeer();
1329
+ const walletPeerVersion = (_a = walletPeer === null || walletPeer === void 0 ? void 0 : walletPeer.version) !== null && _a !== void 0 ? _a : message.version;
1330
+ // Gate the freshly-paired wallet against the dApp's required minimum (a
1331
+ // no-op under the default, permissive minimum). Throws
1332
+ // VersionUnsupportedBeaconError when the wallet is too old.
1333
+ this.assertWalletVersionMeetsMinimum(walletPeerVersion);
1334
+ const isV4Session = (0, octez_connect_core_1.isMultiNetworkVersion)(walletPeerVersion);
1335
+ const messageAccounts = message.accounts;
1336
+ const multiNetworkAccounts = isV4Session &&
1337
+ messageAccounts &&
1338
+ typeof messageAccounts === 'object' &&
1339
+ !Array.isArray(messageAccounts)
1340
+ ? messageAccounts
1341
+ : null;
1342
+ let accountInfo;
1343
+ if (isV4Session) {
1344
+ const requestedNetworks = ((_b = request.networks) !== null && _b !== void 0 ? _b : []).map((n) => (0, octez_connect_core_1.normalizeTezosCaip2)(n.chainId));
1345
+ const servedChainIds = multiNetworkAccounts
1346
+ ? Object.keys(multiNetworkAccounts).map(octez_connect_core_1.normalizeTezosCaip2)
1347
+ : [];
1348
+ const isMinimumVersionV4OrHigher = (0, octez_connect_core_1.isMultiNetworkVersion)(this.requiredMinimumVersion);
1349
+ // Reject silently-degraded responses: a v4 session with >=2 networks
1350
+ // requested but no accounts fanout returned.
1351
+ if (requestedNetworks.length >= 2 && isMinimumVersionV4OrHigher) {
1352
+ const missing = requestedNetworks.filter((c) => !servedChainIds.includes(c));
1353
+ if (missing.length > 0) {
1354
+ throw new octez_connect_core_1.NetworksUnsupportedBeaconError({
1355
+ requestedNetworks,
1356
+ unsupportedNetworks: missing
1357
+ });
1358
+ }
1359
+ }
1360
+ }
1361
+ if (isV4Session && multiNetworkAccounts) {
1362
+ const builtAccounts = yield this.buildAccountInfosFromV4Fanout(message, multiNetworkAccounts, walletPeerVersion !== null && walletPeerVersion !== void 0 ? walletPeerVersion : '0', connectionInfo);
1363
+ yield this.accountManager.addAccounts(builtAccounts);
1364
+ accountInfo = builtAccounts[0];
1365
+ if (!accountInfo) {
1366
+ throw new Error(EMPTY_PERMISSION_ACCOUNTS_MESSAGE);
1367
+ }
1368
+ yield this.setActiveAccount(accountInfo);
1369
+ logger.log('requestPermissions', '######## MULTI-NETWORK ACCOUNTS #######', builtAccounts.length);
1370
+ }
1371
+ else {
1372
+ accountInfo = yield this.onNewAccount(message, connectionInfo);
1373
+ logger.log('requestPermissions', '######## ACCOUNT INFO #######');
1374
+ logger.log('requestPermissions', JSON.stringify(accountInfo));
1375
+ yield this.accountManager.addAccount(accountInfo);
1376
+ }
1377
+ if (!accountInfo) {
1378
+ throw new Error(EMPTY_PERMISSION_ACCOUNTS_MESSAGE);
1379
+ }
1208
1380
  const output = Object.assign(Object.assign({}, message), { walletKey: accountInfo.walletKey, address: accountInfo.address, accountInfo });
1209
1381
  yield this.notifySuccess(request, {
1210
1382
  account: accountInfo,
@@ -1230,11 +1402,13 @@ class DAppClient extends octez_connect_core_1.Client {
1230
1402
  requestProofOfEventChallenge(input) {
1231
1403
  return __awaiter(this, void 0, void 0, function* () {
1232
1404
  const activeAccount = yield this.getActiveAccount();
1233
- if (!activeAccount)
1405
+ if (!activeAccount) {
1234
1406
  throw new Error('Please request permissions before doing a proof of event challenge');
1407
+ }
1235
1408
  if (activeAccount.walletType !== 'abstracted_account' &&
1236
- activeAccount.verificationType !== 'proof_of_event')
1409
+ activeAccount.verificationType !== 'proof_of_event') {
1237
1410
  throw new Error('This wallet is not an abstracted account and thus cannot perform proof of event');
1411
+ }
1238
1412
  const request = {
1239
1413
  type: octez_connect_types_1.BeaconMessageType.ProofOfEventChallengeRequest,
1240
1414
  contractAddress: activeAccount.address,
@@ -1271,8 +1445,9 @@ class DAppClient extends octez_connect_core_1.Client {
1271
1445
  requestSimulatedProofOfEventChallenge(input) {
1272
1446
  return __awaiter(this, void 0, void 0, function* () {
1273
1447
  const activeAccount = yield this.getActiveAccount();
1274
- if (!activeAccount)
1448
+ if (!activeAccount) {
1275
1449
  throw new Error('Please request permissions before doing a proof of event challenge');
1450
+ }
1276
1451
  if (activeAccount.walletType !== 'abstracted_account' &&
1277
1452
  activeAccount.verificationType !== 'proof_of_event') {
1278
1453
  throw new Error('This wallet is not an abstracted account and thus cannot perform a simulated proof of event');
@@ -1422,9 +1597,10 @@ class DAppClient extends octez_connect_core_1.Client {
1422
1597
  if (!activeAccount) {
1423
1598
  throw yield this.sendInternalError('No active account!');
1424
1599
  }
1600
+ const resolvedNetwork = yield this.resolveOperationNetwork(input.network, activeAccount);
1425
1601
  const request = {
1426
1602
  type: octez_connect_types_1.BeaconMessageType.OperationRequest,
1427
- network: activeAccount.network || this.network,
1603
+ network: resolvedNetwork,
1428
1604
  operationDetails: input.operationDetails,
1429
1605
  sourceAddress: activeAccount.address || ''
1430
1606
  };
@@ -1624,7 +1800,10 @@ class DAppClient extends octez_connect_core_1.Client {
1624
1800
  // if the account we requested is not available, we remove it locally
1625
1801
  let accountInfo;
1626
1802
  if (operationRequest.sourceAddress && operationRequest.network) {
1627
- const accountIdentifier = yield (0, octez_connect_core_1.getAccountIdentifier)(operationRequest.sourceAddress, operationRequest.network);
1803
+ const networkForId = typeof operationRequest.network === 'string'
1804
+ ? (0, octez_connect_core_1.networkFromTezosCaip2)((0, octez_connect_core_1.normalizeTezosCaip2)(operationRequest.network))
1805
+ : operationRequest.network;
1806
+ const accountIdentifier = yield (0, octez_connect_core_1.getAccountIdentifier)(operationRequest.sourceAddress, networkForId);
1628
1807
  accountInfo = yield this.getAccount(accountIdentifier);
1629
1808
  if (accountInfo) {
1630
1809
  yield this.removeAccount(accountInfo.accountIdentifier);
@@ -1736,14 +1915,18 @@ class DAppClient extends octez_connect_core_1.Client {
1736
1915
  web: { app: browser, type: 'web' }
1737
1916
  };
1738
1917
  const defaultType = () => {
1739
- if ((0, octez_connect_ui_1.isBrowser)(window) && browser)
1918
+ if ((0, octez_connect_ui_1.isBrowser)(window) && browser) {
1740
1919
  return { app: browser, type: 'web' };
1741
- if ((0, octez_connect_ui_1.isDesktop)(window) && desktop)
1920
+ }
1921
+ if ((0, octez_connect_ui_1.isDesktop)(window) && desktop) {
1742
1922
  return { app: desktop, type: 'desktop' };
1743
- if ((0, octez_connect_ui_1.isBrowser)(window) && extension)
1923
+ }
1924
+ if ((0, octez_connect_ui_1.isBrowser)(window) && extension) {
1744
1925
  return { app: extension, type: 'extension' };
1745
- if (mobile)
1926
+ }
1927
+ if (mobile) {
1746
1928
  return { app: mobile, type: 'mobile' };
1929
+ }
1747
1930
  return { app: undefined, type: undefined };
1748
1931
  };
1749
1932
  const { app, type } = storageWallet ? appTypeMap[storageWallet.type] : defaultType();
@@ -1765,6 +1948,85 @@ class DAppClient extends octez_connect_core_1.Client {
1765
1948
  return walletInfo;
1766
1949
  });
1767
1950
  }
1951
+ // Throws VersionUnsupportedBeaconError when a wallet that reported a version
1952
+ // is below requiredMinimumVersion (or reports a malformed one). A peer that
1953
+ // never reported a version (legacy pairing predating versioning) is treated
1954
+ // as unknown and allowed through, so raising the minimum never retroactively
1955
+ // breaks an already-paired session on a pure read.
1956
+ assertWalletVersionMeetsMinimum(walletVersion) {
1957
+ if (walletVersion === undefined) {
1958
+ return;
1959
+ }
1960
+ const min = this.requiredMinimumVersion;
1961
+ let cmp;
1962
+ try {
1963
+ cmp = (0, octez_connect_core_1.compareBeaconVersion)(walletVersion, min);
1964
+ }
1965
+ catch (_a) {
1966
+ // A present-but-malformed version cannot be shown to meet the minimum.
1967
+ throw new octez_connect_core_1.VersionUnsupportedBeaconError(min, walletVersion);
1968
+ }
1969
+ if (cmp < 0) {
1970
+ throw new octez_connect_core_1.VersionUnsupportedBeaconError(min, walletVersion);
1971
+ }
1972
+ }
1973
+ // Distinct CAIP-2 chain ids across all session accounts. Reads the account
1974
+ // store, so callers should avoid it on paths that can decide without it.
1975
+ getSessionChainIds() {
1976
+ return __awaiter(this, void 0, void 0, function* () {
1977
+ const allAccounts = yield this.accountManager.getAccounts();
1978
+ return Array.from(new Set(allAccounts
1979
+ .map((a) => { var _a; return (_a = a.network) === null || _a === void 0 ? void 0 : _a.chainId; })
1980
+ .filter((c) => typeof c === 'string' && c.length > 0)));
1981
+ });
1982
+ }
1983
+ // Resolves the network for an outgoing operation. Returns the supplied
1984
+ // CAIP-2 string (validated and confirmed in-session), or the active
1985
+ // account's Network when there is no ambiguity. Throws on a multi-network
1986
+ // session when no network is supplied.
1987
+ resolveOperationNetwork(inputNetwork, activeAccount) {
1988
+ return __awaiter(this, void 0, void 0, function* () {
1989
+ var _a;
1990
+ if (inputNetwork) {
1991
+ if (!(0, octez_connect_core_1.isValidTezosCaip2)(inputNetwork)) {
1992
+ throw new octez_connect_core_1.NetworksUnsupportedBeaconError({
1993
+ requestedNetworks: [inputNetwork],
1994
+ unsupportedNetworks: [inputNetwork],
1995
+ customMessage: `Malformed CAIP-2 string: "${inputNetwork}". Expected format: tezos:<NetID>.`
1996
+ });
1997
+ }
1998
+ // Fast path: the active account is already on the requested network, so
1999
+ // it is trivially in-session — skip the account-store read.
2000
+ if (inputNetwork === ((_a = activeAccount.network) === null || _a === void 0 ? void 0 : _a.chainId)) {
2001
+ return inputNetwork;
2002
+ }
2003
+ const knownChainIds = yield this.getSessionChainIds();
2004
+ // Tolerate pre-multi-network sessions with no recorded chain ids: such a
2005
+ // wallet (v2/v3) expects a Network object, not a bare CAIP-2 string it
2006
+ // cannot interpret, so return the active account's Network rather than the
2007
+ // string. (There is exactly one network in a session with no chain ids.)
2008
+ if (knownChainIds.length === 0) {
2009
+ return activeAccount.network || this.network;
2010
+ }
2011
+ if (!knownChainIds.includes(inputNetwork)) {
2012
+ throw new octez_connect_core_1.NetworksUnsupportedBeaconError({
2013
+ requestedNetworks: [inputNetwork],
2014
+ unsupportedNetworks: [inputNetwork]
2015
+ });
2016
+ }
2017
+ return inputNetwork;
2018
+ }
2019
+ // No explicit network: a session spanning more than one chain is ambiguous.
2020
+ const sessionChainIds = yield this.getSessionChainIds();
2021
+ if (sessionChainIds.length > 1) {
2022
+ throw new octez_connect_core_1.NetworksUnsupportedBeaconError({
2023
+ requestedNetworks: [],
2024
+ unsupportedNetworks: []
2025
+ });
2026
+ }
2027
+ return activeAccount.network || this.network;
2028
+ });
2029
+ }
1768
2030
  getPeer(account) {
1769
2031
  return __awaiter(this, void 0, void 0, function* () {
1770
2032
  var _a, _b, _c, _d, _e, _f;
@@ -1786,12 +2048,84 @@ class DAppClient extends octez_connect_core_1.Client {
1786
2048
  peer = yield this._activePeer.promise;
1787
2049
  logger.log('getPeer', 'Active peer', peer);
1788
2050
  }
2051
+ // NB: getPeer() is a pure read used during session resumption, error
2052
+ // handling and wallet-info lookups, so it must not throw on an
2053
+ // under-minimum peer (that would wipe a resumed session or mask the real
2054
+ // wallet error). The version gate is enforced at the pairing response and
2055
+ // at request-send time instead — see assertWalletVersionMeetsMinimum.
1789
2056
  return peer;
1790
2057
  });
1791
2058
  }
2059
+ // Build the wrapped Tezos wire message for a flat request input. The v4
2060
+ // multi-network `networks` field is stripped for peers negotiated below
2061
+ // v4 — a v3 peer must never see v4 payload fields.
2062
+ wrapTezosRequest(flat, envelope, account) {
2063
+ var _a;
2064
+ if (flat.type === octez_connect_types_1.BeaconMessageType.PermissionRequest) {
2065
+ const blockchainData = Object.assign({}, flat);
2066
+ delete blockchainData.type;
2067
+ if (!(0, octez_connect_core_1.isMultiNetworkVersion)(envelope.version)) {
2068
+ delete blockchainData.networks;
2069
+ }
2070
+ return (0, octez_connect_core_1.wrapBeaconMessage)(envelope, {
2071
+ blockchainIdentifier: 'tezos',
2072
+ type: octez_connect_types_1.BeaconMessageType.PermissionRequest,
2073
+ blockchainData
2074
+ });
2075
+ }
2076
+ const payloadMeta = DAppClient.FLAT_REQUEST_PAYLOAD_TYPES[flat.type];
2077
+ if (!payloadMeta) {
2078
+ throw new Error(`Cannot send a "${flat.type}" message: not a Tezos request type`);
2079
+ }
2080
+ const payload = Object.assign({}, flat);
2081
+ delete payload.type;
2082
+ return (0, octez_connect_core_1.wrapBeaconMessage)(envelope, {
2083
+ blockchainIdentifier: 'tezos',
2084
+ type: octez_connect_types_1.BeaconMessageType.BlockchainRequest,
2085
+ accountId: (_a = account === null || account === void 0 ? void 0 : account.accountIdentifier) !== null && _a !== void 0 ? _a : '',
2086
+ blockchainData: Object.assign({ type: payloadMeta.type, scope: payloadMeta.scope }, payload)
2087
+ });
2088
+ }
2089
+ // Normalize a wrapped Tezos wire message back to the flat shape the
2090
+ // pre-fork pipeline (request* methods, onNewAccount, integrator events)
2091
+ // consumes. Returns undefined for non-Tezos payloads, which keep the
2092
+ // wrapped pass-through of the generic permissionRequest/request API.
2093
+ normalizeWrappedTezosMessage(wrapper) {
2094
+ var _a, _b, _c, _d, _e, _f;
2095
+ const inner = wrapper.message;
2096
+ const envelope = { id: wrapper.id, version: wrapper.version, senderId: wrapper.senderId };
2097
+ // Chain-agnostic control messages.
2098
+ if (inner.type === octez_connect_types_1.BeaconMessageType.Acknowledge || inner.type === octez_connect_types_1.BeaconMessageType.Disconnect) {
2099
+ return Object.assign(Object.assign({}, envelope), { type: inner.type });
2100
+ }
2101
+ if (inner.type === octez_connect_types_1.BeaconMessageType.Error) {
2102
+ return Object.assign(Object.assign({}, envelope), { type: octez_connect_types_1.BeaconMessageType.Error, errorType: (_b = (_a = inner.error) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : octez_connect_types_1.BeaconErrorType.UNKNOWN_ERROR, errorData: (_c = inner.error) === null || _c === void 0 ? void 0 : _c.data, description: inner.description });
2103
+ }
2104
+ if (inner.blockchainIdentifier === undefined ||
2105
+ !DAppClient.TEZOS_IDENTIFIERS.includes(inner.blockchainIdentifier)) {
2106
+ return undefined;
2107
+ }
2108
+ if (inner.type === octez_connect_types_1.BeaconMessageType.PermissionResponse ||
2109
+ inner.type === octez_connect_types_1.BeaconMessageType.ChangeAccountRequest) {
2110
+ return Object.assign(Object.assign(Object.assign({}, envelope), { type: inner.type }), ((_d = inner.blockchainData) !== null && _d !== void 0 ? _d : {}));
2111
+ }
2112
+ if (inner.type === octez_connect_types_1.BeaconMessageType.BlockchainResponse) {
2113
+ const payloadType = (_e = inner.blockchainData) === null || _e === void 0 ? void 0 : _e.type;
2114
+ const flatType = typeof payloadType === 'string'
2115
+ ? DAppClient.TEZOS_PAYLOAD_TO_FLAT_TYPE[payloadType]
2116
+ : undefined;
2117
+ if (!flatType) {
2118
+ return undefined;
2119
+ }
2120
+ const payload = Object.assign({}, ((_f = inner.blockchainData) !== null && _f !== void 0 ? _f : {}));
2121
+ delete payload.type;
2122
+ return Object.assign(Object.assign(Object.assign({}, envelope), { type: flatType }), payload);
2123
+ }
2124
+ return undefined;
2125
+ }
1792
2126
  makeRequest(requestInput, skipResponse, otherTabMessageId) {
1793
2127
  return __awaiter(this, void 0, void 0, function* () {
1794
- var _a, _b, _c, _d;
2128
+ var _a, _b, _c, _d, _e;
1795
2129
  const messageId = otherTabMessageId !== null && otherTabMessageId !== void 0 ? otherTabMessageId : (yield (0, octez_connect_utils_1.generateGUID)());
1796
2130
  if (this._initPromise && this.isInitPending) {
1797
2131
  if (this._initPromiseReject) {
@@ -1823,25 +2157,44 @@ class DAppClient extends octez_connect_core_1.Client {
1823
2157
  if (!this.beaconId) {
1824
2158
  throw yield this.sendInternalError('octez.connect ID not defined');
1825
2159
  }
1826
- const request = Object.assign({ id: messageId, version: '2', senderId: yield (0, octez_connect_core_1.getSenderId)(yield this.beaconId) }, requestInput);
2160
+ const account = yield this.getActiveAccount();
2161
+ const peer = yield this.getPeer(account);
2162
+ // Enforce the dApp's required minimum before a request leaves the SDK.
2163
+ // No-op under the default (permissive) minimum; otherwise rejects an
2164
+ // under-minimum wallet with VersionUnsupportedBeaconError.
2165
+ this.assertWalletVersionMeetsMinimum(peer === null || peer === void 0 ? void 0 : peer.version);
2166
+ // The wire dialect is negotiated per peer — min(peer.version,
2167
+ // BEACON_VERSION), floor '2'. A wrapped-capable peer (>= 3) receives the
2168
+ // flat requestInput (the unchanged public shape) mapped onto the wrapped
2169
+ // Tezos payload; a legacy peer receives the flat v2 message it has
2170
+ // always spoken. Integrators never see either dialect.
2171
+ const negotiatedVersion = (0, octez_connect_core_1.negotiateEnvelopeVersion)(peer === null || peer === void 0 ? void 0 : peer.version);
2172
+ const requestSenderId = yield (0, octez_connect_core_1.getSenderId)(yield this.beaconId);
2173
+ let request;
2174
+ if ((0, octez_connect_core_1.usesWrappedMessages)(negotiatedVersion)) {
2175
+ const wrapped = this.wrapTezosRequest(requestInput, { id: messageId, version: negotiatedVersion, senderId: requestSenderId }, account);
2176
+ yield ((_c = this.blockchains.get('tezos')) === null || _c === void 0 ? void 0 : _c.validateRequest(wrapped.message));
2177
+ request = wrapped;
2178
+ }
2179
+ else {
2180
+ request = Object.assign({ id: messageId, version: negotiatedVersion, senderId: requestSenderId }, requestInput);
2181
+ }
1827
2182
  let exposed;
1828
2183
  if (!skipResponse) {
1829
2184
  exposed = new octez_connect_utils_1.ExposedPromise();
1830
2185
  this.addOpenRequest(request.id, exposed);
1831
2186
  }
1832
- const account = yield this.getActiveAccount();
1833
- const peer = yield this.getPeer(account);
1834
2187
  const payload = yield new octez_connect_core_1.Serializer(this.getPeerProtocolVersion(peer)).serialize(request);
1835
2188
  const walletInfo = yield this.getWalletInfo(peer, account);
1836
2189
  logger.log('makeRequest', 'sending message', request);
1837
2190
  try {
1838
2191
  // Hook for performance measurement
1839
- if ((_c = window.__beaconPerf) === null || _c === void 0 ? void 0 : _c.onBeforeSend) {
2192
+ if ((_d = window.__beaconPerf) === null || _d === void 0 ? void 0 : _d.onBeforeSend) {
1840
2193
  window.__beaconPerf.onBeforeSend();
1841
2194
  }
1842
2195
  ;
1843
2196
  (yield this.transport).send(payload, peer);
1844
- if (request.type !== octez_connect_types_1.BeaconMessageType.PermissionRequest ||
2197
+ if (requestInput.type !== octez_connect_types_1.BeaconMessageType.PermissionRequest ||
1845
2198
  (this._activeAccount.isResolved() && (yield this._activeAccount.promise))) {
1846
2199
  this.tryToAppSwitch();
1847
2200
  }
@@ -1868,7 +2221,7 @@ class DAppClient extends octez_connect_core_1.Client {
1868
2221
  if (!otherTabMessageId) {
1869
2222
  this.events
1870
2223
  .emit(beacon_message_events_1.messageEvents[requestInput.type].sent, {
1871
- walletInfo: Object.assign(Object.assign({}, walletInfo), { name: (_d = walletInfo.name) !== null && _d !== void 0 ? _d : 'Wallet' }),
2224
+ walletInfo: Object.assign(Object.assign({}, walletInfo), { name: (_e = walletInfo.name) !== null && _e !== void 0 ? _e : 'Wallet' }),
1872
2225
  extraInfo: {
1873
2226
  resetCallback: () => __awaiter(this, void 0, void 0, function* () {
1874
2227
  this.disconnect();
@@ -1919,16 +2272,22 @@ class DAppClient extends octez_connect_core_1.Client {
1919
2272
  if (!this.beaconId) {
1920
2273
  throw yield this.sendInternalError('octez.connect ID not defined');
1921
2274
  }
2275
+ const account = yield this.getActiveAccount();
2276
+ const peer = yield this.getPeer(account);
2277
+ // Enforce the dApp's required minimum before a request leaves the SDK.
2278
+ this.assertWalletVersionMeetsMinimum(peer === null || peer === void 0 ? void 0 : peer.version);
1922
2279
  const request = {
1923
2280
  id: messageId,
1924
- version: '3',
2281
+ // The envelope carries the version negotiated against the peer:
2282
+ // min(peer.version, BEACON_VERSION), floor '3'. A v3 wallet treats any
2283
+ // version >= 3 as a wrapped message; a v4 wallet reaches its
2284
+ // multi-network branch only when it declared v4 at pairing.
2285
+ version: (0, octez_connect_core_1.negotiateEnvelopeVersion)(peer === null || peer === void 0 ? void 0 : peer.version),
1925
2286
  senderId: yield (0, octez_connect_core_1.getSenderId)(yield this.beaconId),
1926
2287
  message: requestInput
1927
2288
  };
1928
2289
  const exposed = new octez_connect_utils_1.ExposedPromise();
1929
2290
  this.addOpenRequest(request.id, exposed);
1930
- const account = yield this.getActiveAccount();
1931
- const peer = yield this.getPeer(account);
1932
2291
  const payload = yield new octez_connect_core_1.Serializer(this.getPeerProtocolVersion(peer)).serialize(request);
1933
2292
  const walletInfo = yield this.getWalletInfo(peer, account);
1934
2293
  logger.log('makeRequest', 'sending message', request);
@@ -2084,7 +2443,7 @@ class DAppClient extends octez_connect_core_1.Client {
2084
2443
  payload
2085
2444
  ].join(' ');
2086
2445
  const bytes = (0, octez_connect_utils_1.toHex)(constructedString);
2087
- const payloadBytes = '05' + '01' + bytes.length.toString(16).padStart(8, '0') + bytes;
2446
+ const payloadBytes = `05` + `01${bytes.length.toString(16).padStart(8, '0')}${bytes}`;
2088
2447
  const signature = yield (0, octez_connect_utils_1.signMessage)(payloadBytes, {
2089
2448
  secretKey: Buffer.from(keypair.secretKey)
2090
2449
  });
@@ -2112,6 +2471,59 @@ class DAppClient extends octez_connect_core_1.Client {
2112
2471
  return notificationResponse.json();
2113
2472
  });
2114
2473
  }
2474
+ // Delegate the per-network unpacking to the Tezos blockchain plug-in (the
2475
+ // single owner of the v4 fanout shape) and enrich each returned record with
2476
+ // the envelope-level AccountInfo metadata that the parser doesn't see.
2477
+ buildAccountInfosFromV4Fanout(message, fanout, peerVersion, connectionInfo) {
2478
+ return __awaiter(this, void 0, void 0, function* () {
2479
+ var _a, _b;
2480
+ // Keyed by the CAIP-2 namespace `'tezos'`, which is also the wire-format
2481
+ // `blockchainIdentifier` carried by PermissionRequestV3/Response. The
2482
+ // dApp must have registered `new TezosBlockchain()` via `addBlockchain`
2483
+ // before requesting permissions on the v4 path.
2484
+ const tezosBlockchain = this.blockchains.get('tezos');
2485
+ if (!tezosBlockchain) {
2486
+ throw new Error('Tezos blockchain handler is required to parse v4 permission responses');
2487
+ }
2488
+ // Normalize the legacy `pubkey`/`pubKey` aliases and prefix any raw key
2489
+ // before handing the fanout to the parser. The parser keeps publicKey
2490
+ // strings verbatim, so prefixing here keeps stored records canonical.
2491
+ const envelopePublicKey = (_a = message.publicKey) !== null && _a !== void 0 ? _a : message.pubkey;
2492
+ const prefixedEnvelopePk = envelopePublicKey ? (0, octez_connect_utils_1.prefixPublicKey)(envelopePublicKey) : undefined;
2493
+ const prefixedFanout = Object.fromEntries(Object.entries(fanout).map(([cid, raw]) => [
2494
+ cid,
2495
+ Object.assign(Object.assign({}, raw), { publicKey: (raw === null || raw === void 0 ? void 0 : raw.publicKey) ? (0, octez_connect_utils_1.prefixPublicKey)(raw.publicKey) : undefined })
2496
+ ]));
2497
+ const synthesizedResponse = {
2498
+ blockchainIdentifier: 'tezos',
2499
+ type: octez_connect_types_1.BeaconMessageType.PermissionResponse,
2500
+ blockchainData: Object.assign(Object.assign({}, message), { publicKey: prefixedEnvelopePk, accounts: prefixedFanout })
2501
+ };
2502
+ const partials = yield tezosBlockchain.getAccountInfosFromPermissionResponse(synthesizedResponse, peerVersion);
2503
+ const walletKey = (_b = (yield this.storage.get(octez_connect_types_1.StorageKey.LAST_SELECTED_WALLET))) === null || _b === void 0 ? void 0 : _b.key;
2504
+ return Promise.all(partials.map((p) => __awaiter(this, void 0, void 0, function* () {
2505
+ var _a, _b;
2506
+ if (!p.publicKey && !p.address) {
2507
+ throw new Error('PublicKey or Address must be defined for multi-network account');
2508
+ }
2509
+ const address = p.address || (yield (0, octez_connect_utils_1.getAddressFromPublicKey)(p.publicKey));
2510
+ // The parser keys by the address it saw; re-derive accountId if we
2511
+ // synthesized the address from a publicKey above.
2512
+ const accountIdentifier = p.address || !p.network ? p.accountId : yield (0, octez_connect_core_1.getAccountIdentifier)(address, p.network);
2513
+ return Object.assign({ accountIdentifier, senderId: message.senderId, origin: {
2514
+ type: connectionInfo.origin,
2515
+ id: connectionInfo.id
2516
+ }, walletKey,
2517
+ address, publicKey: p.publicKey || undefined,
2518
+ // The v4 fanout parser always sets a Network (networkFromTezosCaip2);
2519
+ // the `?? this.network` fallback keeps the type `Network` (required by
2520
+ // AccountInfo) without an `as AccountInfo` assertion.
2521
+ network: (_a = p.network) !== null && _a !== void 0 ? _a : this.network, scopes: message.scopes, threshold: message.threshold, notification: message.notification, connectedAt: new Date().getTime(), walletType: (_b = message.walletType) !== null && _b !== void 0 ? _b : 'implicit', verificationType: message.verificationType }, (message.verificationType === 'proof_of_event'
2522
+ ? { hasVerifiedChallenge: false }
2523
+ : {}));
2524
+ })));
2525
+ });
2526
+ }
2115
2527
  onNewAccount(message, connectionInfo) {
2116
2528
  return __awaiter(this, void 0, void 0, function* () {
2117
2529
  var _a, _b, _c;
@@ -2147,4 +2559,39 @@ class DAppClient extends octez_connect_core_1.Client {
2147
2559
  }
2148
2560
  }
2149
2561
  exports.DAppClient = DAppClient;
2562
+ /**
2563
+ * This method handles sending of requests to the DApp. It makes sure that the DAppClient is initialized and connected
2564
+ * to the transport. After that rate limits and permissions will be checked, an ID is attached and the request is sent
2565
+ * to the DApp over the transport.
2566
+ *
2567
+ * @param requestInput The BeaconMessage to be sent to the wallet
2568
+ * @param account The account that the message will be sent to
2569
+ * @param skipResponse If true, the function return as soon as the message is sent
2570
+ */
2571
+ // Maps each flat Tezos request type to its wrapped blockchainData
2572
+ // discriminator + scope (the pre-fork flat wire strings, kept verbatim;
2573
+ // mirrored by the wallet-side flat-output normalization).
2574
+ DAppClient.FLAT_REQUEST_PAYLOAD_TYPES = {
2575
+ [octez_connect_types_1.BeaconMessageType.OperationRequest]: { type: 'operation_request', scope: 'operation_request' },
2576
+ [octez_connect_types_1.BeaconMessageType.SignPayloadRequest]: { type: 'sign_payload_request', scope: 'sign' },
2577
+ [octez_connect_types_1.BeaconMessageType.BroadcastRequest]: { type: 'broadcast_request', scope: 'broadcast' },
2578
+ [octez_connect_types_1.BeaconMessageType.ProofOfEventChallengeRequest]: {
2579
+ type: 'proof_of_event_challenge_request',
2580
+ scope: 'proof_of_event'
2581
+ },
2582
+ [octez_connect_types_1.BeaconMessageType.SimulatedProofOfEventChallengeRequest]: {
2583
+ type: 'simulated_proof_of_event_challenge_request',
2584
+ scope: 'proof_of_event'
2585
+ }
2586
+ };
2587
+ // Wrapped Tezos response blockchainData discriminators → the flat
2588
+ // BeaconMessageType the request* methods (and integrators) consume.
2589
+ DAppClient.TEZOS_PAYLOAD_TO_FLAT_TYPE = {
2590
+ operation_response: octez_connect_types_1.BeaconMessageType.OperationResponse,
2591
+ sign_payload_response: octez_connect_types_1.BeaconMessageType.SignPayloadResponse,
2592
+ broadcast_response: octez_connect_types_1.BeaconMessageType.BroadcastResponse,
2593
+ proof_of_event_challenge_response: octez_connect_types_1.BeaconMessageType.ProofOfEventChallengeResponse,
2594
+ simulated_proof_of_event_challenge_response: octez_connect_types_1.BeaconMessageType.SimulatedProofOfEventChallengeResponse
2595
+ };
2596
+ DAppClient.TEZOS_IDENTIFIERS = ['tezos', 'xtz'];
2150
2597
  //# sourceMappingURL=DAppClient.js.map