diff --git a/packages/beacon-core/src/utils/multi-tab-channel.ts b/packages/beacon-core/src/utils/multi-tab-channel.ts index 4da41ff39..963988435 100644 --- a/packages/beacon-core/src/utils/multi-tab-channel.ts +++ b/packages/beacon-core/src/utils/multi-tab-channel.ts @@ -8,6 +8,9 @@ type BCMessageType = | 'RESPONSE' | 'DISCONNECT' | BeaconMessageType + | 'INIT_REQ' + | 'INIT_RES' + | 'NEW_PEER' type BCMessage = { type: BCMessageType diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts index 9071d4494..22ca37b85 100644 --- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts +++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts @@ -180,6 +180,10 @@ export class DAppClient extends Client { private isGetActiveAccountHandled: boolean = false + private _wcPairingRequest: ExposedPromise | undefined + + private _initPromiseResolver: Function | undefined + private readonly openRequestsOtherTabs = new Set() /** * A map of requests that are currently "open", meaning we have sent them to a wallet and are still awaiting a response. @@ -452,6 +456,39 @@ export class DAppClient extends Client { ) this.initUserID().catch((err) => logger.error(err.message)) + + this.multiTabChannel.isLeader() + .then(async (isLeader) => { + if (!isLeader) { + return + } + + const wcOptions = { + projectId: this.wcProjectId, + relayUrl: this.wcRelayUrl, + metadata: { + name: this.name, + description: this.description ?? '', + url: this.appUrl ?? '', + icons: this.iconUrl ? [this.iconUrl] : [] + } + } + + this.walletConnectTransport = new DappWalletConnectTransport( + this.name, + await this.keyPair, + this.storage, + { + network: this.network.type, + opts: wcOptions + }, + () => this.multiTabChannel.isLeader() + ) + + this.initEvents() + + await this.addListener(this.walletConnectTransport) + }) } private async onElectedLeaderhandler() { @@ -491,11 +528,59 @@ export class DAppClient extends Client { case 'DISCONNECT': this._transport.isResolved() && this.disconnect() break + case 'INIT_REQ': + this.initReqHandler(message.sender) + break + case 'INIT_RES': + this.initResHandler(message.data) + break + case 'NEW_PEER': + this.newPeerHandler(message.data) + break default: logger.error('onBCMessageHandler', 'message type not recognized', message) } } + private async newPeerHandler(peer: any) { + this.hideUI(['alert']) + this.setTransport(this.walletConnectTransport) + this.walletConnectTransport?.connect() + this.setActivePeer(peer) + this._initPromiseResolver && this._initPromiseResolver(TransportType.WALLETCONNECT) + } + + private async initResHandler(request: any) { + if (!this._wcPairingRequest) { + return + } + + this._wcPairingRequest.resolve(request) + this._wcPairingRequest = undefined + } + + private async initReqHandler(sender: string) { + if (!await this.multiTabChannel.isLeader()) { + return + } + + this.walletConnectTransport?.listenForNewPeer((peer) => { + this.hideUI(['toast']) + this.newPeerHandler(peer) + this.multiTabChannel.postMessage({ + type: 'NEW_PEER', + data: peer, + recipient: sender + }) + }) + + this.multiTabChannel.postMessage({ + type: 'INIT_RES', + data: await this.walletConnectTransport?.getPairingRequestInfo(), + recipient: sender + }) + } + private async prepareRequest({ data }: any, isV3 = false) { if (!(await this.multiTabChannel.isLeader())) { return @@ -544,7 +629,7 @@ export class DAppClient extends Client { public async initInternalTransports(): Promise { const keyPair = await this.keyPair - if (this.postMessageTransport || this.p2pTransport || this.walletConnectTransport) { + if (this.postMessageTransport || this.p2pTransport || (this.walletConnectTransport && this.walletConnectTransport.connectionStatus === TransportStatus.CONNECTED)) { return } @@ -573,6 +658,11 @@ export class DAppClient extends Client { } } + // the leader tab has already initialized the transport + if (await this.multiTabChannel.isLeader()) { + return + } + this.walletConnectTransport = new DappWalletConnectTransport( this.name, keyPair, @@ -778,7 +868,19 @@ export class DAppClient extends Client { return p2pTransport.getPairingRequestInfo() }, postmessagePeerInfo: () => postMessageTransport.getPairingRequestInfo(), - walletConnectPeerInfo: () => walletConnectTransport.getPairingRequestInfo(), + walletConnectPeerInfo: async () => { + if (await this.multiTabChannel.isLeader()) { + return await walletConnectTransport.getPairingRequestInfo() + } + // the current tab is not the leader + // send a request to the leader tab and wait for + // _wcPairingRequest to resolve + this._wcPairingRequest = new ExposedPromise() + this.multiTabChannel.postMessage({ + type: 'INIT_REQ' + }) + return await this._wcPairingRequest.promise + }, networkType: this.network.type, abortedHandler: async () => { logger.log('init', 'ABORTED') @@ -805,6 +907,7 @@ export class DAppClient extends Client { .catch((emitError) => console.warn(emitError)) } } + this._initPromiseResolver = resolve }) return this._initPromise @@ -834,7 +937,7 @@ export class DAppClient extends Client { this.postMessageTransport?.disconnect(), this.walletConnectTransport?.disconnect() ]) - this.postMessageTransport = this.p2pTransport = this.walletConnectTransport = undefined + this.postMessageTransport = this.p2pTransport = undefined await this.setActivePeer(undefined) await this.setTransport(undefined) this._initPromise = undefined @@ -874,7 +977,7 @@ export class DAppClient extends Client { if (!this.debounceSetActiveAccount && transport instanceof WalletConnectTransport) { this.debounceSetActiveAccount = true this._initPromise = undefined - this.postMessageTransport = this.p2pTransport = this.walletConnectTransport = undefined + this.postMessageTransport = this.p2pTransport = undefined if (await this.multiTabChannel.isLeader()) { await transport.disconnect() this.openRequestsOtherTabs.clear() @@ -2010,7 +2113,6 @@ export class DAppClient extends Client { this._initPromise = undefined this.postMessageTransport = undefined this.p2pTransport = undefined - this.walletConnectTransport = undefined await this.setTransport() await this.setActivePeer() } @@ -2295,6 +2397,10 @@ export class DAppClient extends Client { throw await this.sendInternalError('BeaconID not defined') } + if ((await this.transport).type === TransportType.WALLETCONNECT && !(await this.multiTabChannel.isLeader())) { + return await this.makeRequestBC(requestInput) + } + const request: Optional & Pick = { id: messageId, @@ -2570,7 +2676,6 @@ export class DAppClient extends Client { } this.postMessageTransport = undefined this.p2pTransport = undefined - this.walletConnectTransport = undefined this.sendMetrics('performance-metrics/save', await this.buildPayload('disconnect', 'success')) }