import { Vessel } from "@opensea/vessel" import { DEV_OPENSEA_WALLET_URL } from "./constants" import { mfaListener } from "./mfa/listener" let iframe: HTMLIFrameElement | undefined = undefined let vessel: Vessel | undefined = undefined let initializationPromise: Promise | undefined = undefined type InitializeOptions = { url?: string debug?: boolean retries?: number } export const initialize = async ({ url = DEV_OPENSEA_WALLET_URL, debug = false, retries = 10, }: InitializeOptions = {}): Promise => { if (initializationPromise) { return initializationPromise } // No-op when not in a browser environment if (typeof document === "undefined") { return } iframe = document.querySelector( "iframe#os-embedded-wallet-iframe", ) ?? undefined if (!iframe) { iframe = document.createElement("iframe") iframe.src = url iframe.hidden = true iframe.id = "os-embedded-wallet-iframe" // Allow clipboard write to support exportingprivate keys iframe.allow = "clipboard-write" document.body.appendChild(iframe) } vessel = new Vessel({ iframe, targetOrigin: new URL(url).origin, debug, /** * Handshake failure is usually due to hydration errors where iframe is removed from document. * So fail quickly and find the iframe or add it to the document again in a retry. */ handshakeTimeout: 1_000, }) initializationPromise = (async () => { try { await vessel.handshake() vessel.addListener("message", mfaListener) } catch (error) { initializationPromise = undefined if (retries > 0) { if (debug) { // eslint-disable-next-line no-console console.debug( `Retrying embedded wallet initialization (${retries - 1} retries left)`, ) } return initialize({ url, debug, retries: retries - 1 }) } throw error } })() return initializationPromise } export const getState = async () => { if (!vessel) { throw new Error("Wallet module not initialized") } await initializationPromise return { vessel, iframe } }