1+ import { createDeferredPromise , DeferredPromise } from '@metamask/utils' ;
2+
13import {
24 GetPublicKeyParams ,
35 GetPublicKeyResponse ,
@@ -13,7 +15,7 @@ import { projectLogger as log } from './logger';
1315
1416const LEDGER_IFRAME_ID = 'LEDGER-IFRAME' ;
1517
16- const IFRAME_INIT_TIMEOUT = 4000 ;
18+ export const IFRAME_INIT_TIMEOUT = 4000 ;
1719
1820export enum IFrameMessageAction {
1921 LedgerConnectionChange = 'ledger-connection-change' ,
@@ -113,6 +115,8 @@ export class LedgerIframeBridge
113115 messageCallbacks : Record < number , ( response : IFrameMessageResponse ) => void > =
114116 { } ;
115117
118+ #iframeInitPromise?: DeferredPromise ;
119+
116120 constructor (
117121 opts : LedgerIframeBridgeOptions = {
118122 bridgeUrl : 'https://metamask.github.io/eth-ledger-bridge-keyring' ,
@@ -299,15 +303,33 @@ export class LedgerIframeBridge
299303 }
300304
301305 async #init( ) : Promise < void > {
302- await this . #setupIframe( this . #opts. bridgeUrl ) ;
303- this . eventListener = this . #eventListener. bind ( this , this . #opts. bridgeUrl ) ;
304- window . addEventListener ( 'message' , this . eventListener ) ;
306+ if ( this . #iframeInitPromise) {
307+ // if the iframe is already being initialized, we return the promise
308+ // to avoid multiple initialization attempts
309+ return this . #iframeInitPromise. promise ;
310+ }
311+
312+ this . #iframeInitPromise = createDeferredPromise ( {
313+ suppressUnhandledRejection : true ,
314+ } ) ;
315+
316+ try {
317+ await this . #setupIframe( this . #opts. bridgeUrl ) ;
318+ this . eventListener = this . #eventListener. bind ( this , this . #opts. bridgeUrl ) ;
319+ window . addEventListener ( 'message' , this . eventListener ) ;
320+ this . #iframeInitPromise. resolve ( ) ;
321+ } catch ( error ) {
322+ this . #iframeInitPromise. reject ( error ) ;
323+ throw error ;
324+ } finally {
325+ this . #iframeInitPromise = undefined ;
326+ }
305327 }
306328
307329 async #setupIframe( bridgeUrl : string ) : Promise < void > {
308330 const timeout = new Promise ( ( _ , reject ) => {
309331 setTimeout ( ( ) => {
310- reject ( new Error ( 'Ledger initialization timed out' ) ) ;
332+ reject ( new Error ( 'Bridge initialization timed out' ) ) ;
311333 } , IFRAME_INIT_TIMEOUT ) ;
312334 } ) ;
313335
@@ -322,6 +344,7 @@ export class LedgerIframeBridge
322344 this . iframe . onerror = ( ) : void => {
323345 if ( this . iframe ) {
324346 document . head . removeChild ( this . iframe ) ;
347+ this . iframe = undefined ;
325348 }
326349 this . iframeLoaded = false ;
327350 reject ( new Error ( 'Failed to load iframe' ) ) ;
0 commit comments