diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 46615dc..3c3c24d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,7 @@ on: branches: - develop - main + - beta - hotfix\/v[0-9]+.[0-9]+.[0-9]+ jobs: diff --git a/package.json b/package.json index c5f789c..6f13536 100644 --- a/package.json +++ b/package.json @@ -42,10 +42,15 @@ "import": "./dist/argentMobile.js", "require": "./dist/argentMobile.cjs" }, - "./argentCompound": { - "types": "./dist/argentCompound.d.ts", - "import": "./dist/argentCompound.js", - "require": "./dist/argentCompound.cjs" + "./argent": { + "types": "./dist/argent.d.ts", + "import": "./dist/argent.js", + "require": "./dist/argent.cjs" + }, + "./argentX": { + "types": "./dist/argentX.d.ts", + "import": "./dist/argentX.js", + "require": "./dist/argentX.cjs" }, "./braavos": { "types": "./dist/braavos.d.ts", @@ -61,14 +66,14 @@ "main": "./dist/starknetkit.cjs", "module": "./dist/starknetkit.js", "types": "./dist/starknetkit.d.ts", - "files": [ - "dist" - ], "scripts": { "build": "vite build", "check": "svelte-check --tsconfig ./tsconfig.json", "dev": "vite build --watch" }, + "files": [ + "dist" + ], "dependencies": { "@argent/x-ui": "^1.70.1", "@starknet-io/get-starknet": "^4.0.0", diff --git a/src/connectors/argent/argentMobile/index.ts b/src/connectors/argent/argentMobile/index.ts index 0382e02..1aa5547 100644 --- a/src/connectors/argent/argentMobile/index.ts +++ b/src/connectors/argent/argentMobile/index.ts @@ -27,11 +27,12 @@ import { type ConnectorData, type ConnectorIcons, } from "../../connector" -import { InjectedConnector, InjectedConnectorOptions } from "../../injected" +import { InjectedConnectorOptions } from "../../injected" import { DEFAULT_ARGENT_MOBILE_ICON, DEFAULT_PROJECT_ID } from "./constants" import { isInArgentMobileAppBrowser } from "../helpers" import type { StarknetAdapter } from "./modal/starknet/adapter" import { ArgentX } from "../../injected/argentX" +import { getModalWallet } from "../../../helpers/mapModalWallets" export interface ArgentMobileConnectorOptions { dappName: string @@ -41,7 +42,7 @@ export interface ArgentMobileConnectorOptions { url: string icons?: string[] rpcUrl?: string - isCompoundConnector?: boolean + onlyQR?: boolean } export class ArgentMobileBaseConnector extends Connector { @@ -78,7 +79,7 @@ export class ArgentMobileBaseConnector extends Connector { } get name(): string { - return this._options.isCompoundConnector ? "Argent" : "Argent (mobile)" // TODO ditch isCompoundConnector + return "Argent (mobile)" } get icon(): ConnectorIcons { @@ -95,8 +96,14 @@ export class ArgentMobileBaseConnector extends Connector { return this._wallet } - async connect(): Promise { - await this.ensureWallet() + async connect( + props: + | { + onlyQRCode?: boolean + } + | undefined, + ): Promise { + await this.ensureWallet({ onlyQRCode: props?.onlyQRCode }) if (!this._wallet) { throw new ConnectorNotFoundError() @@ -188,7 +195,13 @@ export class ArgentMobileBaseConnector extends Connector { this._wallet = null } - private async ensureWallet(): Promise { + private async ensureWallet( + props: + | { + onlyQRCode?: boolean + } + | undefined, + ): Promise { const { getStarknetWindowObject } = await import("./modal") const { chainId, projectId, dappName, description, url, icons, rpcUrl } = this._options @@ -201,6 +214,7 @@ export class ArgentMobileBaseConnector extends Connector { : publicRPCNode.testnet) const options = { + onlyQRCode: props?.onlyQRCode, chainId: chainId ?? constants.NetworkName.SN_MAIN, name: dappName, projectId: projectId ?? DEFAULT_PROJECT_ID, @@ -208,6 +222,7 @@ export class ArgentMobileBaseConnector extends Connector { url, icons, rpcUrl: providerRpcUrl, + modalWallet: getModalWallet(this), } if (projectId === DEFAULT_PROJECT_ID) { diff --git a/src/connectors/argent/argentMobile/modal/argentModal.ts b/src/connectors/argent/argentMobile/modal/argentModal.ts index 3516782..8f32dd2 100644 --- a/src/connectors/argent/argentMobile/modal/argentModal.ts +++ b/src/connectors/argent/argentMobile/modal/argentModal.ts @@ -1,4 +1,8 @@ import { getDevice } from "./getDevice" +import Modal from "../../../../modal/Modal.svelte" +import { Layout, ModalWallet } from "../../../../types/modal" +import { getModalTarget } from "../../../../helpers/modal" +import { StarknetkitConnector } from "../../../connector" const device = getDevice() @@ -7,6 +11,7 @@ export interface RequestArguments { params?: unknown[] | object } +// TODO - SK-47 - remove this const overlayStyle = { position: "fixed", top: "0", @@ -25,6 +30,7 @@ const overlayStyle = { fontFamily: "'Barlow', sans-serif", } +// TODO - SK-47 - remove this const iframeStyle = { width: "840px", height: "540px", @@ -40,6 +46,17 @@ const iframeStyle = { transform: "translate(-50%,-50%)", } +const iframeStyleOnlyQR = { + width: "245px", + height: "245px", + borderRadius: "40px", + zIndex: "99999", + backgroundColor: "white", + border: "none", + outline: "none", +} + +// TODO - SK-47 - remove this const overlayHtml = `
@@ -52,12 +69,20 @@ const overlayHtml = `
` +const overlayHtmlOnlyQR = ` +
+ +
+` + interface Urls { readonly desktop: string readonly ios: string readonly android: string } +type ModalWalletExtended = ModalWallet & { dappName: string } + class ArgentModal { public bridgeUrl = "https://login.argent.xyz" public mobileUrl = "argent://" @@ -67,21 +92,66 @@ class ArgentModal { private overlay?: HTMLDivElement private popupWindow?: Window private closingTimeout?: NodeJS.Timeout + private standaloneConnectorModal?: Modal + + public showWalletConnectModal( + wcUri: string, + modalWallet: ModalWalletExtended, + ) { + const wcParam = encodeURIComponent(wcUri) + const href = encodeURIComponent(window.location.href) + + this.showModal( + { + desktop: `${this.bridgeUrl}?wc=${wcParam}&href=${href}&device=desktop&onlyQR=true`, + ios: `${this.mobileUrl}app/wc?uri=${wcParam}&href=${href}&device=mobile`, + android: `${this.mobileUrl}app/wc?uri=${wcParam}&href=${href}&device=mobile`, + }, + modalWallet, + ) + } - public showWalletConnectModal(wcUri: string) { + public getWalletConnectQR(wcUri: string) { const wcParam = encodeURIComponent(wcUri) const href = encodeURIComponent(window.location.href) - this.showModal({ - desktop: `${this.bridgeUrl}?wc=${wcParam}&href=${href}&device=desktop`, + this.getQR({ + desktop: `${this.bridgeUrl}?wc=${wcParam}&href=${href}&device=desktop&onlyQR=true`, ios: `${this.mobileUrl}app/wc?uri=${wcParam}&href=${href}&device=mobile`, android: `${this.mobileUrl}app/wc?uri=${wcParam}&href=${href}&device=mobile`, }) } + private getQR(urls: Urls) { + const overlay = document.createElement("div") + const shadow = document.querySelector("#starknetkit-modal-container") + + if (shadow?.shadowRoot) { + const slot = shadow.shadowRoot.querySelector(".qr-code-slot") + + if (slot) { + slot.innerHTML = overlayHtmlOnlyQR + document.body.appendChild(overlay) + this.overlay = overlay + + const iframe = slot.querySelector("iframe") as HTMLIFrameElement + iframe.setAttribute("src", urls.desktop) + + for (const [key, value] of Object.entries(iframeStyleOnlyQR)) { + iframe.style[key as any] = value + } + } else { + throw new Error("Cannot find QR code slot") + } + } else { + throw new Error("Cannot find modal") + } + } + + // TODO - SK-47 - handle this public showApprovalModal(_: RequestArguments): void { if (device === "desktop") { - this.showModal({ + this.showModalOld({ desktop: `${this.bridgeUrl}?action=sign`, ios: "", android: "", @@ -95,26 +165,50 @@ class ArgentModal { Additionally when there is a signing request triggered by the dapp it will hit the deep link with an incomplete URI, this should be ignored and not considered valid as it's only used for automatically redirecting the users to approve or reject a signing request. */ - this.showModal({ + this.showModalOld({ desktop: `${this.bridgeUrl}?action=sign&device=desktop&href=${href}`, ios: `${this.mobileUrl}app/wc/request?href=${href}&device=mobile`, android: `${this.mobileUrl}app/wc/request?href=${href}&device=mobile`, }) } - public closeModal(success?: "animateSuccess") { + // TODO - SK-47 - remove this + public closeModal(success?: boolean) { + const modal = this.standaloneConnectorModal if (success) { - this.overlay - ?.querySelector("iframe") - ?.contentWindow?.postMessage("argent-login.success", "*") - this.popupWindow?.postMessage("argent-login.success", "*") - this.closingTimeout = setTimeout(this.close, 3400) + modal?.$set({ layout: Layout.success }) + setTimeout(() => modal?.$destroy(), 3000) } else { - this.close() + modal?.$set({ layout: Layout.failure }) } } - private showModal(urls: Urls) { + private showModal(urls: Urls, modalWallet: ModalWalletExtended) { + this.standaloneConnectorModal = new Modal({ + target: getModalTarget(), + props: { + layout: Layout.qrCode, + dappName: modalWallet.dappName, + showBackButton: false, + selectedWallet: modalWallet, + callback: async (wallet: ModalWallet | null) => { + try { + const connector = wallet?.connector as StarknetkitConnector + + this.standaloneConnectorModal?.$destroy() + await connector?.connect() + } catch (err) { + this.standaloneConnectorModal?.$set({ layout: Layout.failure }) + } + }, + }, + }) + + this.getQR(urls) + } + + // TODO - SK-47 - remove this + private showModalOld(urls: Urls) { clearTimeout(this.closingTimeout) if (this.overlay || this.popupWindow) { this.close() @@ -161,6 +255,7 @@ class ArgentModal { closeButton.addEventListener("click", () => this.closeModal()) } + // TODO - SK-47 - remove this private close = () => { this.overlay?.remove() this.popupWindow?.close() diff --git a/src/connectors/argent/argentMobile/modal/login.ts b/src/connectors/argent/argentMobile/modal/login.ts index 8fe03b9..498f6a8 100644 --- a/src/connectors/argent/argentMobile/modal/login.ts +++ b/src/connectors/argent/argentMobile/modal/login.ts @@ -1,14 +1,13 @@ import SignClient from "@walletconnect/sign-client" import type { SignClientTypes } from "@walletconnect/types" - import { RpcProvider, constants } from "starknet" - -// Using NetworkName as a value. -const Network: typeof constants.NetworkName = constants.NetworkName - import type { NamespaceAdapter, NamespaceAdapterOptions } from "./adapter" import { argentModal } from "./argentModal" import { resetWalletConnect } from "../../../../helpers/resetWalletConnect" +import { ModalWallet } from "../../../../types/modal" + +// Using NetworkName as a value. +const Network: typeof constants.NetworkName = constants.NetworkName export interface IArgentLoginOptions { projectId?: string @@ -22,6 +21,8 @@ export interface IArgentLoginOptions { mobileUrl?: string modalType?: "overlay" | "window" walletConnect?: SignClientTypes.Options + onlyQRCode?: boolean + modalWallet?: ModalWallet } export const login = async ( @@ -37,6 +38,8 @@ export const login = async ( url, icons, walletConnect, + onlyQRCode, + modalWallet, }: IArgentLoginOptions, Adapter: new (options: NamespaceAdapterOptions) => TAdapter, ): Promise => { @@ -101,13 +104,20 @@ export const login = async ( // Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing). if (uri) { - argentModal.showWalletConnectModal(uri) + if (onlyQRCode) { + argentModal.getWalletConnectQR(uri) + } else { + argentModal.showWalletConnectModal(uri, { + ...modalWallet, + dappName: name || "", + } as ModalWallet & { dappName: string }) + } argentModal.wcUri = uri // Await session approval from the wallet. const session = await approval() adapter.updateSession(session) - argentModal.closeModal("animateSuccess") + argentModal.closeModal(true) } return adapter diff --git a/src/connectors/argent/argentMobile/modal/starknet/adapter.ts b/src/connectors/argent/argentMobile/modal/starknet/adapter.ts index ec2df94..64f93c5 100644 --- a/src/connectors/argent/argentMobile/modal/starknet/adapter.ts +++ b/src/connectors/argent/argentMobile/modal/starknet/adapter.ts @@ -182,7 +182,7 @@ export class StarknetAdapter const chainId = this.formatChainId(this.chainId) argentModal.showApprovalModal(request) const response = await this.client.request({ topic, chainId, request }) - argentModal.closeModal("animateSuccess") + argentModal.closeModal(true) return response } catch (error) { argentModal.closeModal() diff --git a/src/connectors/argent/index.ts b/src/connectors/argent/index.ts index 6360f17..3e61276 100644 --- a/src/connectors/argent/index.ts +++ b/src/connectors/argent/index.ts @@ -1,12 +1,11 @@ +import { injectedWalletIcons } from "../injected" import { ArgentX } from "../injected/argentX" +import { getInjectedArgentX } from "./helpers/getInjectedArgentX" import { ArgentMobileBaseConnector, ArgentMobileConnectorOptions, isInArgentMobileAppBrowser, } from "./argentMobile" - -import { getInjectedArgentX } from "./helpers/getInjectedArgentX" -import { DEFAULT_ARGENT_MOBILE_ICON } from "./argentMobile/constants" import { StarknetkitCompoundConnector } from "../connector" /** @@ -16,19 +15,12 @@ function hasInjectedArgentX(): boolean { return Boolean(getInjectedArgentX()) } -type ArgentCompoundSettings = ArgentMobileConnectorOptions +type ArgentSettings = ArgentMobileConnectorOptions -// TODO think about naming -// - ArgentUnified -// - ArgentOneButton -// - ArgentCompound -// - Argent +const ArgentIcon = + "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgaWQ9IkhpdG8gaW4gY29udGFpbmVyIj4KPHBhdGggaWQ9IlZlY3RvciIgZD0iTTE4Ljc5OTkgNS45MkgxMy4yMDAzQzEzLjAxMzIgNS45MiAxMi44NjMyIDYuMDc0MDIgMTIuODU5MiA2LjI2NTQ3QzEyLjc0NiAxMS42NDcgOS45OTQ3NyAxNi43NTQ2IDUuMjU5MyAyMC4zNzI0QzUuMTA4OTUgMjAuNDg3MiA1LjA3NDcgMjAuNzAzIDUuMTg0NjIgMjAuODU4NEw4LjQ2MDg3IDI1LjQ5NDNDOC41NzIzMyAyNS42NTIgOC43ODk1IDI1LjY4NzcgOC45NDIzNyAyNS41NzE4QzExLjkwMzMgMjMuMzI0NCAxNC4yODUgMjAuNjEzNCAxNi4wMDAxIDE3LjYwODVDMTcuNzE1MiAyMC42MTM0IDIwLjA5NyAyMy4zMjQ0IDIzLjA1OCAyNS41NzE4QzIzLjIxMDcgMjUuNjg3NyAyMy40Mjc5IDI1LjY1MiAyMy41Mzk1IDI1LjQ5NDNMMjYuODE1NyAyMC44NTg0QzI2LjkyNTUgMjAuNzAzIDI2Ljg5MTIgMjAuNDg3MiAyNi43NDEgMjAuMzcyNEMyMi4wMDU0IDE2Ljc1NDYgMTkuMjU0MiAxMS42NDcgMTkuMTQxMiA2LjI2NTQ3QzE5LjEzNzEgNi4wNzQwMiAxOC45ODcgNS45MiAxOC43OTk5IDUuOTJaIiBmaWxsPSIjRkY4NzVCIi8+CjwvZz4KPC9zdmc+Cg==" -// TODO -// - get qr code -// - get full modal for both ux flows? - -export class ArgentCompound +export class Argent extends StarknetkitCompoundConnector implements StarknetkitCompoundConnector { @@ -36,18 +28,22 @@ export class ArgentCompound readonly argentMobile?: ArgentMobileBaseConnector readonly connector: ArgentX | ArgentMobileBaseConnector - readonly fallbackConnector: ArgentX | ArgentMobileBaseConnector + readonly fallbackConnector: ArgentMobileBaseConnector | null + + get name() { + return "Argent" + } + get icon() { + return ArgentIcon + } - constructor(settings: ArgentCompoundSettings) { + constructor(settings: ArgentSettings) { super() this.argentX = new ArgentX({ - name: "Argent", - icon: DEFAULT_ARGENT_MOBILE_ICON, - isCompoundConnector: true, + icon: injectedWalletIcons.argentX, }) this.argentMobile = new ArgentMobileBaseConnector({ - isCompoundConnector: true, ...settings, }) @@ -56,28 +52,7 @@ export class ArgentCompound this.fallbackConnector = this.argentMobile } else { this.connector = this.argentMobile - this.fallbackConnector = this.argentX + this.fallbackConnector = null } } - - getArgentXConnector() { - return this.argentX - } - - getArgentMobileConnector() { - return this.argentMobile - } } - -// -// // Second way -// const compoundConnector = new ArgentCompound({ -// url: typeof window !== "undefined" ? window.location.href : "", -// dappName: "Example dapp", -// }) -// -// compoundConnector.getConector() -// -// compoundConnector.getArgentX() -// compoundConnector.getArgentMobile() -// compoundConnector.getFallback() diff --git a/src/connectors/connector.ts b/src/connectors/connector.ts index 81ff31e..c329c89 100644 --- a/src/connectors/connector.ts +++ b/src/connectors/connector.ts @@ -30,6 +30,7 @@ export interface ConnectorEvents { export type ConnectOptions = { silent_mode: boolean + onlyQRCode?: boolean } export abstract class Connector extends EventEmitter { @@ -58,20 +59,17 @@ export abstract class Connector extends EventEmitter { abstract request( call: RequestFnCall, ): Promise - - // getConnector() { TODO? - // return this - // } } export abstract class StarknetkitConnector extends Connector { /** Connector StarknetWindowObject */ abstract get wallet(): StarknetWindowObject - abstract isCompoundConnector?: boolean // TODO I don't need this prolly } export abstract class StarknetkitCompoundConnector { readonly isCompoundConnector = true abstract connector: StarknetkitConnector - abstract fallbackConnector: StarknetkitConnector + abstract fallbackConnector: StarknetkitConnector | null + abstract get name(): string + abstract get icon(): ConnectorIcons } diff --git a/src/connectors/injected/index.ts b/src/connectors/injected/index.ts index 1ef05f8..42b01d4 100644 --- a/src/connectors/injected/index.ts +++ b/src/connectors/injected/index.ts @@ -35,16 +35,21 @@ export interface InjectedConnectorOptions { name?: string /** Wallet icons. */ icon?: ConnectorIcons - isCompoundConnector?: boolean } // Icons used when the injected wallet is not installed // Icons from media kits -const walletIcons = { - argentX: - "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA2NS4xOTUwOCA1Ny43MzU2MiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjUuMTk1MDggNTcuNzM1NjI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojRkY4NzVCO30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTQwLjk4NTkyLDBIMjQuMjA4ODhjLTAuNTYsMC0xLjAxMDAxLDAuNDUxMDItMS4wMjE5NywxLjAxMjAyCgljLTAuMzM4OTksMTUuNzU1LTguNTgyMDMsMzAuNzA4OTgtMjIuNzcwMDIsNDEuMzAwOTljLTAuNDUwMDEsMC4zMzcwMS0wLjU1Mjk4LDAuOTY3OTktMC4yMjQsMS40MjNsOS44MTU5OCwxMy41NzMKCWMwLjMzNDA1LDAuNDYyMDEsMC45ODUwNSwwLjU2NTk4LDEuNDQyOTksMC4yMjY5OWM4Ljg3MTAzLTYuNTc5MDEsMTYuMDA3MDItMTQuNTE3LDIxLjE0NjA2LTIzLjMxNQoJYzUuMTM4LDguNzk4LDEyLjI3Mzk5LDE2LjczNTk5LDIxLjE0NiwyMy4zMTVjMC40NTY5NywwLjMzODk5LDEuMTA3OTcsMC4yMzUwMiwxLjQ0MTk2LTAuMjI2OTlsOS44MTYwNC0xMy41NzMKCWMwLjMyODk4LTAuNDU1MDIsMC4yMjY5OS0xLjA4Ni0wLjIyNC0xLjQyM0M1MC41ODk4NiwzMS43MjEwMSw0Mi4zNDY4OCwxNi43NjcwMyw0Mi4wMDc4OSwxLjAxMjAyCglDNDEuOTk1ODcsMC40NTEwMiw0MS41NDY4OSwwLDQwLjk4NTkyLDAiLz4KPC9zdmc+Cg==", - braavos: - "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCIgdmlld0JveD0iMCAwIDUwMCA1MDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0zMjMuNDQgNDEuMzg4NkMzMjQuMTk4IDQyLjY3MjggMzIzLjE5NSA0NC4yNjAzIDMyMS43MDQgNDQuMjYwM0MyOTEuNTEgNDQuMjYwMyAyNjYuOTY1IDY4LjE2NTYgMjY2LjM4OSA5Ny44NzFDMjU2LjA1IDk1Ljk0MDcgMjQ1LjMzNyA5NS43OTU2IDIzNC43NTQgOTcuNTc4N0MyMzQuMDIzIDY4LjAwOSAyMDkuNTQgNDQuMjYwMyAxNzkuNDQ1IDQ0LjI2MDNDMTc3Ljk1MyA0NC4yNjAzIDE3Ni45NDkgNDIuNjcxNiAxNzcuNzA3IDQxLjM4NjVDMTkyLjMyMyAxNi42MzMgMjE5LjQ4MyAwIDI1MC41NzMgMEMyODEuNjY0IDAgMzA4LjgyNCAxNi42MzM5IDMyMy40NCA0MS4zODg2WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzIzMjRfNjE4NjkpIi8+CjxwYXRoIGQ9Ik00MTguNzU2IDIyNi44OTRDNDI2LjM3IDIyOS4yIDQzMy41ODEgMjIyLjUxNyA0MzEuMDM2IDIxNC45NzlDNDA0LjUwNyAxMzYuNDAxIDMxNi41MzUgMTA0LjM1OCAyNTAuMTU5IDEwNC4zNThDMTgzLjY3NCAxMDQuMzU4IDkzLjczOTEgMTM3LjQxOCA2OS4zMDUxIDIxNS4zMzFDNjYuOTU3NCAyMjIuODE4IDc0LjE0NjUgMjI5LjI3NSA4MS42NDc5IDIyNi45NzdMMjQ0LjI1IDE3Ny4xNTFDMjQ3LjU2OSAxNzYuMTM0IDI1MS4xMTYgMTc2LjEyOCAyNTQuNDM5IDE3Ny4xMzVMNDE4Ljc1NiAyMjYuODk0WiIgZmlsbD0idXJsKCNwYWludDFfbGluZWFyXzIzMjRfNjE4NjkpIi8+CjxwYXRoIGQ9Ik02OS43MTY1IDIzOS40MjZMMjQ0LjM3IDE4Ni40NTZDMjQ3LjY2OSAxODUuNDU2IDI1MS4xOTEgMTg1LjQ1MyAyNTQuNDkyIDE4Ni40NDhMNDMwLjIzMiAyMzkuNDUyQzQ0NC43NiAyNDMuODMzIDQ1NC43MDEgMjU3LjIxNiA0NTQuNzAxIDI3Mi4zOVY0MzAuNDgxQzQ1NC4wMjggNDY5LjA3IDQxOS4zNjIgNTAwIDM4MC43ODYgNTAwSDMxNi43MTJDMzEwLjM3OSA1MDAgMzA1LjI1IDQ5NC44NzcgMzA1LjI1IDQ4OC41NDNWNDMzLjExNUMzMDUuMjUgNDExLjI4OSAzMTguMTY3IDM5MS41MzUgMzM4LjE1NSAzODIuNzkyQzM2NC45NDkgMzcxLjA3MSAzOTYuNjQ2IDM1NS4yMTggNDAyLjYwOCAzMjMuNDA2QzQwNC41MzIgMzEzLjEzOCAzOTcuODM3IDMwMy4yMzQgMzg3LjU5NSAzMDEuMTk4QzM2MS42OTkgMjk2LjA1MSAzMzIuOTg5IDI5OC4wMzkgMzA4LjcxMSAzMDguODk4QzI4MS4xNSAzMjEuMjI1IDI3My45NCAzNDEuNzMxIDI3MS4yNzEgMzY5LjI3TDI2OC4wMzYgMzk4LjkzOEMyNjcuMDQ3IDQwOC4wMDUgMjU4LjU0NiA0MTQuOTUyIDI0OS40MjkgNDE0Ljk1MkMyMzkuOTk4IDQxNC45NTIgMjMyLjkyNiA0MDcuNzY5IDIzMS45MDMgMzk4LjM4OEwyMjguNzI4IDM2OS4yN0MyMjYuNDQyIDM0NS42ODEgMjIyLjI5OCAzMjIuNzY3IDE5Ny45MTIgMzExLjg2QzE3MC4wOTUgMjk5LjQxOSAxNDIuMTQxIDI5NS4yODcgMTEyLjQwNCAzMDEuMTk4QzEwMi4xNjIgMzAzLjIzNCA5NS40NjcgMzEzLjEzOCA5Ny4zOTEzIDMyMy40MDZDMTAzLjQwNSAzNTUuNDk1IDEzNC44NTQgMzcwLjk4NSAxNjEuODQ0IDM4Mi43OTJDMTgxLjgzMyAzOTEuNTM1IDE5NC43NSA0MTEuMjg5IDE5NC43NSA0MzMuMTE1VjQ4OC41MzNDMTk0Ljc1IDQ5NC44NjcgMTg5LjYyMiA1MDAgMTgzLjI4OSA1MDBIMTE5LjIxNEM4MC42Mzc0IDUwMCA0NS45NzE2IDQ2OS4wNyA0NS4yOTc5IDQzMC40ODFWMjcyLjM0OUM0NS4yOTc5IDI1Ny4xOTQgNTUuMjE0MiAyNDMuODI0IDY5LjcxNjUgMjM5LjQyNloiIGZpbGw9InVybCgjcGFpbnQyX2xpbmVhcl8yMzI0XzYxODY5KSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzIzMjRfNjE4NjkiIHgxPSIyNDUuOTg2IiB5MT0iLTI3IiB4Mj0iNDI1LjQ5NiIgeTI9IjUwMi4zNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0Y1RDQ1RSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRjk2MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzIzMjRfNjE4NjkiIHgxPSIyNDUuOTg2IiB5MT0iLTI3IiB4Mj0iNDI1LjQ5NiIgeTI9IjUwMi4zNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0Y1RDQ1RSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRjk2MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDJfbGluZWFyXzIzMjRfNjE4NjkiIHgxPSIyNDUuOTg2IiB5MT0iLTI3IiB4Mj0iNDI1LjQ5NiIgeTI9IjUwMi4zNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0Y1RDQ1RSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRjk2MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4=", +export const injectedWalletIcons = { + argentX: { + dark: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIzLjgxMyA1Ljk2NjFIMTYuMTE5MUMxNS44NjIgNS45NjYxIDE1LjY1NiA2LjE4NTMzIDE1LjY1MDQgNi40NTc4MkMxNS40OTQ5IDE0LjExNzQgMTEuNzE0NyAyMS4zODcyIDUuMjA4MTcgMjYuNTM2NUM1LjAwMTYgMjYuNjk5OCA0Ljk1NDU0IDI3LjAwNyA1LjEwNTU2IDI3LjIyODJMOS42MDcxNSAzMy44MjY1QzkuNzYwMjkgMzQuMDUxMSAxMC4wNTg3IDM0LjEwMTcgMTAuMjY4NyAzMy45MzY4QzE0LjMzNzEgMzAuNzM4MSAxNy42MDk1IDI2Ljg3OTUgMTkuOTY2MSAyMi42MDI1QzIyLjMyMjYgMjYuODc5NSAyNS41OTUyIDMwLjczODEgMjkuNjYzNiAzMy45MzY4QzI5Ljg3MzQgMzQuMTAxNyAzMC4xNzE4IDM0LjA1MTEgMzAuMzI1MiAzMy44MjY1TDM0LjgyNjggMjcuMjI4MkMzNC45Nzc2IDI3LjAwNyAzNC45MzA1IDI2LjY5OTggMzQuNzI0MSAyNi41MzY1QzI4LjIxNzQgMjEuMzg3MiAyNC40MzcyIDE0LjExNzQgMjQuMjgxOSA2LjQ1NzgyQzI0LjI3NjMgNi4xODUzMyAyNC4wNzAxIDUuOTY2MSAyMy44MTMgNS45NjYxWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTM0LjU4OTQgMTAuNzg2NEwzMy43NjI4IDguMjE1ODFDMzMuNTk0NyA3LjY5Mzk1IDMzLjE4NTIgNy4yODc0NCAzMi42NjQgNy4xMjY3MkwzMC4wOTgxIDYuMzMyNTlDMjkuNzQ0IDYuMjIyOTEgMjkuNzQwMiA1LjcxOTAyIDMwLjA5MzQgNS42MDQ2M0wzMi42NDYxIDQuNzcyNjdDMzMuMTY0NiA0LjYwMzQ1IDMzLjU2OTQgNC4xOTAzMiAzMy43MjkxIDMuNjY2NTZMMzQuNTE3IDEuMDgzNzJDMzQuNjI2IDAuNzI2MzYgMzUuMTI2NiAwLjcyMjU3MSAzNS4yNDEyIDEuMDc4MDZMMzYuMDY3NyAzLjY0ODU4QzM2LjIzNTggNC4xNzA0NSAzNi42NDUzIDQuNTc2OTggMzcuMTY2NSA0LjczODY0TDM5LjczMjQgNS41MzE4M0M0MC4wODY1IDUuNjQxNSA0MC4wOTAzIDYuMTQ1NCAzOS43MzcyIDYuMjYwNzRMMzcuMTg0NCA3LjA5MjY5QzM2LjY2NTkgNy4yNjA5NiAzNi4yNjExIDcuNjc0MSAzNi4xMDE1IDguMTk4ODFMMzUuMzEzNSAxMC43ODA3QzM1LjIwNDUgMTEuMTM4MSAzNC43MDM5IDExLjE0MTggMzQuNTg5NCAxMC43ODY0WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==", + light: + "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDEiIHZpZXdCb3g9IjAgMCA0MCA0MSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIzLjgxMyA2LjQ2NjFIMTYuMTE5MUMxNS44NjIgNi40NjYxIDE1LjY1NiA2LjY4NTMzIDE1LjY1MDQgNi45NTc4MkMxNS40OTQ5IDE0LjYxNzQgMTEuNzE0NyAyMS44ODcyIDUuMjA4MTcgMjcuMDM2NUM1LjAwMTYgMjcuMTk5OCA0Ljk1NDU0IDI3LjUwNyA1LjEwNTU2IDI3LjcyODJMOS42MDcxNSAzNC4zMjY1QzkuNzYwMjkgMzQuNTUxMSAxMC4wNTg3IDM0LjYwMTcgMTAuMjY4NyAzNC40MzY4QzE0LjMzNzEgMzEuMjM4MSAxNy42MDk1IDI3LjM3OTUgMTkuOTY2MSAyMy4xMDI1QzIyLjMyMjYgMjcuMzc5NSAyNS41OTUyIDMxLjIzODEgMjkuNjYzNiAzNC40MzY4QzI5Ljg3MzQgMzQuNjAxNyAzMC4xNzE4IDM0LjU1MTEgMzAuMzI1MiAzNC4zMjY1TDM0LjgyNjggMjcuNzI4MkMzNC45Nzc2IDI3LjUwNyAzNC45MzA1IDI3LjE5OTggMzQuNzI0MSAyNy4wMzY1QzI4LjIxNzQgMjEuODg3MiAyNC40MzcyIDE0LjYxNzQgMjQuMjgxOSA2Ljk1NzgyQzI0LjI3NjMgNi42ODUzMyAyNC4wNzAxIDYuNDY2MSAyMy44MTMgNi40NjYxWiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTM0LjU4OTQgMTEuMjg2NEwzMy43NjI4IDguNzE1ODFDMzMuNTk0NyA4LjE5Mzk1IDMzLjE4NTIgNy43ODc0NCAzMi42NjQgNy42MjY3MkwzMC4wOTgxIDYuODMyNTlDMjkuNzQ0IDYuNzIyOTEgMjkuNzQwMiA2LjIxOTAyIDMwLjA5MzQgNi4xMDQ2M0wzMi42NDYxIDUuMjcyNjdDMzMuMTY0NiA1LjEwMzQ1IDMzLjU2OTQgNC42OTAzMiAzMy43MjkxIDQuMTY2NTZMMzQuNTE3IDEuNTgzNzJDMzQuNjI2IDEuMjI2MzYgMzUuMTI2NiAxLjIyMjU3IDM1LjI0MTIgMS41NzgwNkwzNi4wNjc3IDQuMTQ4NThDMzYuMjM1OCA0LjY3MDQ1IDM2LjY0NTMgNS4wNzY5OCAzNy4xNjY1IDUuMjM4NjRMMzkuNzMyNCA2LjAzMTgzQzQwLjA4NjUgNi4xNDE1IDQwLjA5MDMgNi42NDU0IDM5LjczNzIgNi43NjA3NEwzNy4xODQ0IDcuNTkyNjlDMzYuNjY1OSA3Ljc2MDk2IDM2LjI2MTEgOC4xNzQxIDM2LjEwMTUgOC42OTg4MUwzNS4zMTM1IDExLjI4MDdDMzUuMjA0NSAxMS42MzgxIDM0LjcwMzkgMTEuNjQxOCAzNC41ODk0IDExLjI4NjRaIiBmaWxsPSJibGFjayIvPgo8L3N2Zz4K", + }, + braavos: { + dark: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCIgdmlld0JveD0iMCAwIDUwMCA1MDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0zMjMuNDQgNDEuMzg4NkMzMjQuMTk4IDQyLjY3MjggMzIzLjE5NSA0NC4yNjAzIDMyMS43MDQgNDQuMjYwM0MyOTEuNTEgNDQuMjYwMyAyNjYuOTY1IDY4LjE2NTYgMjY2LjM4OSA5Ny44NzFDMjU2LjA1IDk1Ljk0MDcgMjQ1LjMzNyA5NS43OTU2IDIzNC43NTQgOTcuNTc4N0MyMzQuMDIzIDY4LjAwOSAyMDkuNTQgNDQuMjYwMyAxNzkuNDQ1IDQ0LjI2MDNDMTc3Ljk1MyA0NC4yNjAzIDE3Ni45NDkgNDIuNjcxNiAxNzcuNzA3IDQxLjM4NjVDMTkyLjMyMyAxNi42MzMgMjE5LjQ4MyAwIDI1MC41NzMgMEMyODEuNjY0IDAgMzA4LjgyNCAxNi42MzM5IDMyMy40NCA0MS4zODg2WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzIzMjRfNjE4NjkpIi8+CjxwYXRoIGQ9Ik00MTguNzU2IDIyNi44OTRDNDI2LjM3IDIyOS4yIDQzMy41ODEgMjIyLjUxNyA0MzEuMDM2IDIxNC45NzlDNDA0LjUwNyAxMzYuNDAxIDMxNi41MzUgMTA0LjM1OCAyNTAuMTU5IDEwNC4zNThDMTgzLjY3NCAxMDQuMzU4IDkzLjczOTEgMTM3LjQxOCA2OS4zMDUxIDIxNS4zMzFDNjYuOTU3NCAyMjIuODE4IDc0LjE0NjUgMjI5LjI3NSA4MS42NDc5IDIyNi45NzdMMjQ0LjI1IDE3Ny4xNTFDMjQ3LjU2OSAxNzYuMTM0IDI1MS4xMTYgMTc2LjEyOCAyNTQuNDM5IDE3Ny4xMzVMNDE4Ljc1NiAyMjYuODk0WiIgZmlsbD0idXJsKCNwYWludDFfbGluZWFyXzIzMjRfNjE4NjkpIi8+CjxwYXRoIGQ9Ik02OS43MTY1IDIzOS40MjZMMjQ0LjM3IDE4Ni40NTZDMjQ3LjY2OSAxODUuNDU2IDI1MS4xOTEgMTg1LjQ1MyAyNTQuNDkyIDE4Ni40NDhMNDMwLjIzMiAyMzkuNDUyQzQ0NC43NiAyNDMuODMzIDQ1NC43MDEgMjU3LjIxNiA0NTQuNzAxIDI3Mi4zOVY0MzAuNDgxQzQ1NC4wMjggNDY5LjA3IDQxOS4zNjIgNTAwIDM4MC43ODYgNTAwSDMxNi43MTJDMzEwLjM3OSA1MDAgMzA1LjI1IDQ5NC44NzcgMzA1LjI1IDQ4OC41NDNWNDMzLjExNUMzMDUuMjUgNDExLjI4OSAzMTguMTY3IDM5MS41MzUgMzM4LjE1NSAzODIuNzkyQzM2NC45NDkgMzcxLjA3MSAzOTYuNjQ2IDM1NS4yMTggNDAyLjYwOCAzMjMuNDA2QzQwNC41MzIgMzEzLjEzOCAzOTcuODM3IDMwMy4yMzQgMzg3LjU5NSAzMDEuMTk4QzM2MS42OTkgMjk2LjA1MSAzMzIuOTg5IDI5OC4wMzkgMzA4LjcxMSAzMDguODk4QzI4MS4xNSAzMjEuMjI1IDI3My45NCAzNDEuNzMxIDI3MS4yNzEgMzY5LjI3TDI2OC4wMzYgMzk4LjkzOEMyNjcuMDQ3IDQwOC4wMDUgMjU4LjU0NiA0MTQuOTUyIDI0OS40MjkgNDE0Ljk1MkMyMzkuOTk4IDQxNC45NTIgMjMyLjkyNiA0MDcuNzY5IDIzMS45MDMgMzk4LjM4OEwyMjguNzI4IDM2OS4yN0MyMjYuNDQyIDM0NS42ODEgMjIyLjI5OCAzMjIuNzY3IDE5Ny45MTIgMzExLjg2QzE3MC4wOTUgMjk5LjQxOSAxNDIuMTQxIDI5NS4yODcgMTEyLjQwNCAzMDEuMTk4QzEwMi4xNjIgMzAzLjIzNCA5NS40NjcgMzEzLjEzOCA5Ny4zOTEzIDMyMy40MDZDMTAzLjQwNSAzNTUuNDk1IDEzNC44NTQgMzcwLjk4NSAxNjEuODQ0IDM4Mi43OTJDMTgxLjgzMyAzOTEuNTM1IDE5NC43NSA0MTEuMjg5IDE5NC43NSA0MzMuMTE1VjQ4OC41MzNDMTk0Ljc1IDQ5NC44NjcgMTg5LjYyMiA1MDAgMTgzLjI4OSA1MDBIMTE5LjIxNEM4MC42Mzc0IDUwMCA0NS45NzE2IDQ2OS4wNyA0NS4yOTc5IDQzMC40ODFWMjcyLjM0OUM0NS4yOTc5IDI1Ny4xOTQgNTUuMjE0MiAyNDMuODI0IDY5LjcxNjUgMjM5LjQyNloiIGZpbGw9InVybCgjcGFpbnQyX2xpbmVhcl8yMzI0XzYxODY5KSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzIzMjRfNjE4NjkiIHgxPSIyNDUuOTg2IiB5MT0iLTI3IiB4Mj0iNDI1LjQ5NiIgeTI9IjUwMi4zNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0Y1RDQ1RSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRjk2MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzIzMjRfNjE4NjkiIHgxPSIyNDUuOTg2IiB5MT0iLTI3IiB4Mj0iNDI1LjQ5NiIgeTI9IjUwMi4zNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0Y1RDQ1RSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRjk2MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDJfbGluZWFyXzIzMjRfNjE4NjkiIHgxPSIyNDUuOTg2IiB5MT0iLTI3IiB4Mj0iNDI1LjQ5NiIgeTI9IjUwMi4zNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0Y1RDQ1RSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRjk2MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4=", + light: + "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCIgdmlld0JveD0iMCAwIDUwMCA1MDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0zMjMuNDQgNDEuMzg4NkMzMjQuMTk4IDQyLjY3MjggMzIzLjE5NSA0NC4yNjAzIDMyMS43MDQgNDQuMjYwM0MyOTEuNTEgNDQuMjYwMyAyNjYuOTY1IDY4LjE2NTYgMjY2LjM4OSA5Ny44NzFDMjU2LjA1IDk1Ljk0MDcgMjQ1LjMzNyA5NS43OTU2IDIzNC43NTQgOTcuNTc4N0MyMzQuMDIzIDY4LjAwOSAyMDkuNTQgNDQuMjYwMyAxNzkuNDQ1IDQ0LjI2MDNDMTc3Ljk1MyA0NC4yNjAzIDE3Ni45NDkgNDIuNjcxNiAxNzcuNzA3IDQxLjM4NjVDMTkyLjMyMyAxNi42MzMgMjE5LjQ4MyAwIDI1MC41NzMgMEMyODEuNjY0IDAgMzA4LjgyNCAxNi42MzM5IDMyMy40NCA0MS4zODg2WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzIzMjRfNjE4NjkpIi8+CjxwYXRoIGQ9Ik00MTguNzU2IDIyNi44OTRDNDI2LjM3IDIyOS4yIDQzMy41ODEgMjIyLjUxNyA0MzEuMDM2IDIxNC45NzlDNDA0LjUwNyAxMzYuNDAxIDMxNi41MzUgMTA0LjM1OCAyNTAuMTU5IDEwNC4zNThDMTgzLjY3NCAxMDQuMzU4IDkzLjczOTEgMTM3LjQxOCA2OS4zMDUxIDIxNS4zMzFDNjYuOTU3NCAyMjIuODE4IDc0LjE0NjUgMjI5LjI3NSA4MS42NDc5IDIyNi45NzdMMjQ0LjI1IDE3Ny4xNTFDMjQ3LjU2OSAxNzYuMTM0IDI1MS4xMTYgMTc2LjEyOCAyNTQuNDM5IDE3Ny4xMzVMNDE4Ljc1NiAyMjYuODk0WiIgZmlsbD0idXJsKCNwYWludDFfbGluZWFyXzIzMjRfNjE4NjkpIi8+CjxwYXRoIGQ9Ik02OS43MTY1IDIzOS40MjZMMjQ0LjM3IDE4Ni40NTZDMjQ3LjY2OSAxODUuNDU2IDI1MS4xOTEgMTg1LjQ1MyAyNTQuNDkyIDE4Ni40NDhMNDMwLjIzMiAyMzkuNDUyQzQ0NC43NiAyNDMuODMzIDQ1NC43MDEgMjU3LjIxNiA0NTQuNzAxIDI3Mi4zOVY0MzAuNDgxQzQ1NC4wMjggNDY5LjA3IDQxOS4zNjIgNTAwIDM4MC43ODYgNTAwSDMxNi43MTJDMzEwLjM3OSA1MDAgMzA1LjI1IDQ5NC44NzcgMzA1LjI1IDQ4OC41NDNWNDMzLjExNUMzMDUuMjUgNDExLjI4OSAzMTguMTY3IDM5MS41MzUgMzM4LjE1NSAzODIuNzkyQzM2NC45NDkgMzcxLjA3MSAzOTYuNjQ2IDM1NS4yMTggNDAyLjYwOCAzMjMuNDA2QzQwNC41MzIgMzEzLjEzOCAzOTcuODM3IDMwMy4yMzQgMzg3LjU5NSAzMDEuMTk4QzM2MS42OTkgMjk2LjA1MSAzMzIuOTg5IDI5OC4wMzkgMzA4LjcxMSAzMDguODk4QzI4MS4xNSAzMjEuMjI1IDI3My45NCAzNDEuNzMxIDI3MS4yNzEgMzY5LjI3TDI2OC4wMzYgMzk4LjkzOEMyNjcuMDQ3IDQwOC4wMDUgMjU4LjU0NiA0MTQuOTUyIDI0OS40MjkgNDE0Ljk1MkMyMzkuOTk4IDQxNC45NTIgMjMyLjkyNiA0MDcuNzY5IDIzMS45MDMgMzk4LjM4OEwyMjguNzI4IDM2OS4yN0MyMjYuNDQyIDM0NS42ODEgMjIyLjI5OCAzMjIuNzY3IDE5Ny45MTIgMzExLjg2QzE3MC4wOTUgMjk5LjQxOSAxNDIuMTQxIDI5NS4yODcgMTEyLjQwNCAzMDEuMTk4QzEwMi4xNjIgMzAzLjIzNCA5NS40NjcgMzEzLjEzOCA5Ny4zOTEzIDMyMy40MDZDMTAzLjQwNSAzNTUuNDk1IDEzNC44NTQgMzcwLjk4NSAxNjEuODQ0IDM4Mi43OTJDMTgxLjgzMyAzOTEuNTM1IDE5NC43NSA0MTEuMjg5IDE5NC43NSA0MzMuMTE1VjQ4OC41MzNDMTk0Ljc1IDQ5NC44NjcgMTg5LjYyMiA1MDAgMTgzLjI4OSA1MDBIMTE5LjIxNEM4MC42Mzc0IDUwMCA0NS45NzE2IDQ2OS4wNyA0NS4yOTc5IDQzMC40ODFWMjcyLjM0OUM0NS4yOTc5IDI1Ny4xOTQgNTUuMjE0MiAyNDMuODI0IDY5LjcxNjUgMjM5LjQyNloiIGZpbGw9InVybCgjcGFpbnQyX2xpbmVhcl8yMzI0XzYxODY5KSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzIzMjRfNjE4NjkiIHgxPSIyNDUuOTg2IiB5MT0iLTI3IiB4Mj0iNDI1LjQ5NiIgeTI9IjUwMi4zNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0Y1RDQ1RSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRjk2MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzIzMjRfNjE4NjkiIHgxPSIyNDUuOTg2IiB5MT0iLTI3IiB4Mj0iNDI1LjQ5NiIgeTI9IjUwMi4zNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0Y1RDQ1RSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRjk2MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDJfbGluZWFyXzIzMjRfNjE4NjkiIHgxPSIyNDUuOTg2IiB5MT0iLTI3IiB4Mj0iNDI1LjQ5NiIgeTI9IjUwMi4zNzYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0Y1RDQ1RSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRjk2MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4=", + }, } export class InjectedConnector extends Connector { @@ -61,22 +66,22 @@ export class InjectedConnector extends Connector { } get name(): string { - console.log("HERE", this._options) this.ensureWallet() return this._options.name ?? this._wallet?.name ?? this._options.id } get icon(): ConnectorIcons { this.ensureWallet() - const deafultIcon = { + const defaultIcon = { dark: - walletIcons[this.id as keyof typeof walletIcons] || - WALLET_NOT_FOUND_ICON_DARK, + injectedWalletIcons[this.id as keyof typeof injectedWalletIcons] + ?.dark || WALLET_NOT_FOUND_ICON_DARK, light: - walletIcons[this.id as keyof typeof walletIcons] || - WALLET_NOT_FOUND_ICON_LIGHT, + injectedWalletIcons[this.id as keyof typeof injectedWalletIcons] + ?.light || WALLET_NOT_FOUND_ICON_LIGHT, } - return this._options.icon || this._wallet?.icon || deafultIcon + + return this._options.icon || this._wallet?.icon || defaultIcon } available(): boolean { diff --git a/src/connectors/webwallet/constants.ts b/src/connectors/webwallet/constants.ts index 7b8c7c6..b7483df 100644 --- a/src/connectors/webwallet/constants.ts +++ b/src/connectors/webwallet/constants.ts @@ -1,19 +1,21 @@ export const DEFAULT_WEBWALLET_URL = "https://web.argent.xyz" -export const DEFAULT_WEBWALLET_ICON = ` - - ` +export const DEFAULT_WEBWALLET_ICON = ` + + + + + + + + + + + + + + +` export const TESTNET_WHITELIST_URL = "https://static.hydrogen.argent47.net/webwallet/iframe_whitelist_testnet.json" diff --git a/src/helpers/connector.ts b/src/helpers/connector.ts new file mode 100644 index 0000000..1c01d94 --- /dev/null +++ b/src/helpers/connector.ts @@ -0,0 +1,33 @@ +import { + StarknetkitCompoundConnector, + StarknetkitConnector, +} from "../connectors" + +export function extractConnector( + connector: StarknetkitConnector | StarknetkitCompoundConnector, + useFallback: boolean = false, +) { + if ((connector as StarknetkitCompoundConnector).isCompoundConnector) { + return useFallback + ? (connector as StarknetkitCompoundConnector).fallbackConnector + : (connector as StarknetkitCompoundConnector).connector + } + return connector as StarknetkitConnector +} + +export function findConnectorById( + connectors: (StarknetkitConnector | StarknetkitCompoundConnector)[], + id: string | null, +) { + const connector = connectors.find((c) => { + if (!c) { + return false + } + return extractConnector(c)?.id === id + }) + + if (!connector) { + return null + } + return extractConnector(connector) +} diff --git a/src/helpers/defaultConnectors.ts b/src/helpers/defaultConnectors.ts index 6e4e64d..e71ad81 100644 --- a/src/helpers/defaultConnectors.ts +++ b/src/helpers/defaultConnectors.ts @@ -2,10 +2,14 @@ import { StarknetkitCompoundConnector, StarknetkitConnector, } from "../connectors" -import { type ArgentMobileConnectorOptions } from "../connectors/argent/argentMobile" +import { + ArgentMobileConnector, + type ArgentMobileConnectorOptions, +} from "../connectors/argent/argentMobile" import { WebWalletConnector } from "../connectors/webwallet" import { Braavos } from "../connectors/injected/braavos" -import { ArgentCompound } from "../connectors/argent" +import { Argent } from "../connectors/argent" +import { ArgentX } from "../connectors/injected/argentX" export const defaultConnectors = ({ argentMobileOptions, @@ -24,7 +28,7 @@ export const defaultConnectors = ({ | StarknetkitCompoundConnector )[] = [] - defaultConnectors.push(new ArgentCompound(argentMobileOptions)) + defaultConnectors.push(new Argent(argentMobileOptions)) if (!isSafari) { defaultConnectors.push(new Braavos()) diff --git a/src/helpers/mapModalWallets.ts b/src/helpers/mapModalWallets.ts index 20b1d32..94cd0c4 100644 --- a/src/helpers/mapModalWallets.ts +++ b/src/helpers/mapModalWallets.ts @@ -8,8 +8,7 @@ import { import { ARGENT_X_ICON } from "../connectors/injected/constants" import type { ModalWallet, StoreVersion } from "../types/modal" import { isInArgentMobileAppBrowser } from "../connectors/argent/helpers" -import { DEFAULT_ARGENT_MOBILE_ICON } from "../connectors/argent/argentMobile/constants" -import { findConnectorById, getConnector } from "../main" +import { extractConnector, findConnectorById } from "./connector" interface SetConnectorsExpandedParams { availableConnectors: (StarknetkitConnector | StarknetkitCompoundConnector)[] @@ -19,6 +18,42 @@ interface SetConnectorsExpandedParams { customOrder: boolean } +export function getModalWallet( + connectorOrCompoundConnector: + | StarknetkitConnector + | StarknetkitCompoundConnector, + discoveryWallets?: WalletProvider[], +): ModalWallet { + const connector = extractConnector( + connectorOrCompoundConnector, + ) as StarknetkitConnector + + const isCompoundConnector = ( + connectorOrCompoundConnector as StarknetkitCompoundConnector + ).isCompoundConnector + + return { + name: isCompoundConnector + ? connectorOrCompoundConnector.name + : connector.name, + id: connector.id, + icon: isCompoundConnector + ? connectorOrCompoundConnector.icon + : connector.icon, + connector: connectorOrCompoundConnector, + installed: true, + title: + "title" in connector && isString(connector.title) + ? connector.title + : undefined, + subtitle: + "subtitle" in connector && isString(connector.subtitle) + ? connector.subtitle + : undefined, + downloads: discoveryWallets?.find((d) => d.id === connector.id)?.downloads, + } +} + export const mapModalWallets = ({ availableConnectors, installedWallets, @@ -32,43 +67,35 @@ export const mapModalWallets = ({ return [] } - const allInstalledWallets = installedWallets.map( - ( - w, // TODO this logic - ) => findConnectorById(availableConnectors, w.id), + const allInstalledWallets = installedWallets.map((w) => + findConnectorById(availableConnectors, w.id), ) - // console.log("customOrder", customOrder) - const orderedByInstall = customOrder ? availableConnectors : [ ...availableConnectors.filter((c) => - allInstalledWallets.includes(getConnector(c)), + allInstalledWallets.includes(extractConnector(c)), ), ...availableConnectors.filter( - (c) => !allInstalledWallets.includes(getConnector(c)), + (c) => !allInstalledWallets.includes(extractConnector(c)), ), ] - // console.log( - // "availableConnectors, orderedByInstall", - // availableConnectors, - // orderedByInstall, - // ) - const connectors = orderedByInstall .map((_c) => { - const c = getConnector(_c) + const isCompoundConnector = (_c as StarknetkitCompoundConnector) + .isCompoundConnector + const c = extractConnector(_c) - const installed = installedWallets.find((w) => w.id === c.id) + const installed = installedWallets.find((w) => w.id === c?.id) if (installed) { let icon let name - if (_c.isCompoundConnector) { - icon = DEFAULT_ARGENT_MOBILE_ICON // TODO - name = "Argent" // TODO + if (isCompoundConnector) { + icon = _c.icon + name = _c.name } else { icon = installed.id === "argentX" @@ -84,8 +111,9 @@ export const mapModalWallets = ({ name, id: installed.id, icon, - connector: c, - isCompoundConnector: Boolean(_c?.isCompoundConnector), + connector: _c, + installed: true, + downloads: discoveryWallets.find((d) => d.id === c?.id)?.downloads, } } @@ -93,7 +121,7 @@ export const mapModalWallets = ({ .filter((w) => Boolean(w.downloads[storeVersion as keyof typeof w.downloads]), ) - .find((d) => d.id === c.id) + .find((d) => d.id === c?.id) if (discovery) { const { downloads } = discovery @@ -104,8 +132,10 @@ export const mapModalWallets = ({ name: discovery.name, id: discovery.id, icon: { light: discoveryIcon, dark: discoveryIcon }, - connector: c, + connector: _c, + installed: false, download: downloads[storeVersion as keyof typeof downloads], + downloads: downloads, } } @@ -113,20 +143,9 @@ export const mapModalWallets = ({ return null } - return { - name: c.name, // TODO - id: c.id, - icon: c.icon, - connector: c, - title: "title" in c && isString(c.title) ? c.title : undefined, - subtitle: - "subtitle" in c && isString(c.subtitle) ? c.subtitle : undefined, - isCompoundConnector: Boolean(_c?.isCompoundConnector), - } + return getModalWallet(_c, discoveryWallets) }) .filter((c): c is ModalWallet => c !== null) - // console.log("connectors", connectors) - return connectors } diff --git a/src/helpers/modal.ts b/src/helpers/modal.ts new file mode 100644 index 0000000..742bf7b --- /dev/null +++ b/src/helpers/modal.ts @@ -0,0 +1,28 @@ +import css from "../theme.css" + +export const getModalTarget = (): ShadowRoot => { + const modalId = "starknetkit-modal-container" + const existingElement = document.getElementById(modalId) + + if (existingElement) { + if (existingElement.shadowRoot) { + // element already exists, use the existing as target + return existingElement.shadowRoot + } + // element exists but shadowRoot cannot be accessed + // delete the element and create new + existingElement.remove() + } + + const element = document.createElement("div") + // set id for future retrieval + element.id = modalId + document.body.appendChild(element) + const target = element.attachShadow({ mode: "open" }) + + const styleElement = document.createElement("style") + styleElement.textContent = css + target.appendChild(styleElement) + + return target +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index a09994f..29c46d1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,18 @@ import type { DisconnectOptions } from "@starknet-io/get-starknet" import sn from "@starknet-io/get-starknet-core" import type { StarknetWindowObject } from "@starknet-io/types-js" + import { - Connector, - ConnectorData, - StarknetkitCompoundConnector, - StarknetkitConnector, -} from "./connectors" + ConnectOptions, + ConnectOptionsWithConnectors, + Layout, + ModalResult, + ModalWallet, +} from "./types/modal" import { DEFAULT_WEBWALLET_URL } from "./connectors/webwallet/constants" + +import { Connector, ConnectorData, StarknetkitConnector } from "./connectors" +import { ArgentMobileBaseConnector } from "./connectors/argent/argentMobile" import { defaultConnectors } from "./helpers/defaultConnectors" import { getStoreVersionFromBrowser } from "./helpers/getStoreVersionFromBrowser" import { @@ -15,40 +20,14 @@ import { setStarknetLastConnectedWallet, } from "./helpers/lastConnected" import { mapModalWallets } from "./helpers/mapModalWallets" -import Modal from "./modal/Modal.svelte" -// import css from "@argent/x-ui/styles/tailwind.css?inline" -import css from "./theme.css?inline" +import { extractConnector, findConnectorById } from "./helpers/connector" +import { getModalTarget } from "./helpers/modal" -import type { - ConnectOptions, - ConnectOptionsWithConnectors, - ModalResult, - ModalWallet, -} from "./types/modal" +import Modal from "./modal/Modal.svelte" +import { ModalInstance } from "./modal/Modal" let selectedConnector: StarknetkitConnector | null = null -export function getConnector( // TODO Maybe just add getConnector to both, StarknetkitConnector and StarknetkitCompoundConnector, then use .getConnector() where needed - connector: StarknetkitConnector | StarknetkitCompoundConnector, -) { - if (connector.isCompoundConnector) { - return (connector as StarknetkitCompoundConnector).connector - } - return connector -} - -export function findConnectorById( - connectors: (StarknetkitConnector | StarknetkitCompoundConnector)[], - id: string | null, -) { - const connector = connectors.find((c) => getConnector(c).id === id) - - if (!connector) { - return null - } - return getConnector(connector) -} - /** * * @param [modalMode="canAsk"] - Choose connection behavior: @@ -109,6 +88,7 @@ export const connect = async ({ if (connector && resultType === "wallet") { connectorData = await connector.connect({ silent_mode: true, + onlyQRCode: true, }) } @@ -141,7 +121,11 @@ export const connect = async ({ let connectorData: ConnectorData | null = null if (resultType === "wallet") { - connectorData = (await connector?.connect()) ?? null + connectorData = + (await connector?.connect({ + onlyQRCode: true, + silent_mode: false, + })) ?? null } if (connector) { @@ -156,8 +140,6 @@ export const connect = async ({ } // otherwise fallback to modal } - // console.log("connectors?.length", connectors?.length) - const modalWallets: ModalWallet[] = mapModalWallets({ availableConnectors, installedWallets, @@ -166,69 +148,72 @@ export const connect = async ({ customOrder: connectors ? connectors?.length > 0 : false, }) - const getTarget = (): ShadowRoot => { - const modalId = "starknetkit-modal-container" - const existingElement = document.getElementById(modalId) - - if (existingElement) { - if (existingElement.shadowRoot) { - // element already exists, use the existing as target - return existingElement.shadowRoot - } - // element exists but shadowRoot cannot be accessed - // delete the element and create new - existingElement.remove() - } - - const element = document.createElement("div") - // set id for future retrieval - element.id = modalId - document.body.appendChild(element) - const target = element.attachShadow({ mode: "open" }) - - const styleElement = document.createElement("style") - styleElement.textContent = css - target.appendChild(styleElement) - - return target - } - return new Promise((resolve, reject) => { const modal = new Modal({ - target: getTarget(), + target: getModalTarget(), props: { dappName, - // callback: async (connector: StarknetkitConnector | null) => { - // try { - // selectedConnector = connector - // if (resultType === "wallet") { - // const connectorData = (await connector?.connect()) ?? null - // if (connector !== null) { - // setStarknetLastConnectedWallet(connector.id) - // } - // - // resolve({ - // connector, - // connectorData, - // wallet: connector?.wallet ?? null, - // }) - // } else { - // resolve({ - // connector, - // wallet: null, - // connectorData: null, - // }) - // } - // } catch (error) { - // reject(error) - // } finally { - // setTimeout(() => modal.$destroy()) - // } - // }, - // theme: modalTheme === "system" ? null : (modalTheme ?? null), + callback: async ( + modalWallet: ModalWallet | null, + useFallback: boolean = false, + ) => { + try { + if (!modalWallet) { + throw new Error("Connector error") + } + + modal.$set({ selectedWallet: modalWallet }) + + if (!modalWallet.installed) { + modal.$set({ layout: Layout.extensionDownloadList }) + return + } + + selectedConnector = extractConnector( + modalWallet.connector, + useFallback, + ) + + if (resultType === "wallet") { + if (selectedConnector instanceof ArgentMobileBaseConnector) { + modal.$set({ layout: Layout.qrCode }) + } else { + modal.$set({ layout: Layout.connecting }) + } + + const connectorData = + (await selectedConnector?.connect({ + onlyQRCode: true, + silent_mode: false, + })) ?? null + if (selectedConnector !== null) { + setStarknetLastConnectedWallet(selectedConnector.id) + } + + resolve({ + connector: selectedConnector, + connectorData, + wallet: selectedConnector?.wallet ?? null, + }) + } else { + resolve({ + connector: selectedConnector, + wallet: null, + connectorData: null, + }) + } + + modal.$set({ layout: Layout.success }) + setTimeout(() => modal.$destroy(), 3000) + } catch (error) { + modal.$set({ layout: Layout.failure }) + reject(error) + } + }, + theme: modalTheme === "system" ? null : (modalTheme ?? null), modalWallets, }, - }) + }) as ModalInstance // Prevents vite build errors }) } diff --git a/src/modal/ConnectorButton.svelte b/src/modal/ConnectorButton.svelte deleted file mode 100644 index 46a528b..0000000 --- a/src/modal/ConnectorButton.svelte +++ /dev/null @@ -1,116 +0,0 @@ - - -{#if wallet.download} - - -
  • { - cb(null) - }} - on:keyup={(e) => { - if (e.key === "Enter") { - cb(null) - } - }} - > - -

    - Install {wallet.name} -

    - {wallet.name} -
  • -
    -{:else} - -
  • { - console.log("HERE", wallet, getConnector(wallet.connector)) - cb(getConnector(wallet.connector)) - }} - on:keyup={async (e) => { - if (e.key === "Enter") { - cb(getConnector(wallet.connector)) - } - }} - > - {#if wallet.isCompoundConnector} - - compound - {/if} - -
    -

    - {wallet.title ?? wallet.name} -

    -

    - {wallet.subtitle ?? ""} -

    -
    - - {#if loadingItem === wallet?.id} -
    - - Loading... -
    - {:else if isSvg} -
    {@html icon}
    - {:else} - {wallet?.name} - {/if} -
  • -{/if} diff --git a/src/modal/Modal.d.ts b/src/modal/Modal.d.ts new file mode 100644 index 0000000..691379d --- /dev/null +++ b/src/modal/Modal.d.ts @@ -0,0 +1,25 @@ +import { Callback, Layout, ModalWallet, Theme } from "../types/modal" + +declare namespace svelte.JSX { + interface IntrinsicElements { + Modal: svelte.JSX.ModalProps + } +} + +interface Modal extends svelte.ComponentType {} + +interface ModalProps { + dappName?: string + layout?: Layout + modalWallets?: ModalWallet[] + selectedWallet?: ModalWallet | null + showBackButton?: boolean + callback?: Callback + theme?: Theme + darkModeControlClass?: string +} + +interface ModalInstance extends SvelteComponent { + $set(props: Partial): void + $destroy(): void +} diff --git a/src/modal/Modal.svelte b/src/modal/Modal.svelte index fe28865..cffa3a4 100644 --- a/src/modal/Modal.svelte +++ b/src/modal/Modal.svelte @@ -1,177 +1,126 @@ -
    -
    1 : true)} +
    -
    - - - - - - - - - - - - - - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    +
    setLayout(Layout.walletList)} + handleClose={() => nodeRef?.parentNode?.removeChild(nodeRef) } + title={dappName} + showBackButton={showBackButton && ![Layout.walletList, Layout.success].includes(layout)} + /> + + {#if layout === Layout.walletList} + + {:else if layout === Layout.connecting} + callback(selectedWallet, true)} + > + {#if selectedConnector?.icon} + + {/if} + + {:else if layout === Layout.success} + + {:else if layout === Layout.failure} + callback(selectedWallet)} + showFallback={showFallback} + handleFallback={() => callback(selectedWallet, true)} + /> + {:else if layout === Layout.qrCode} + setLayout(Layout.argentDownload)} /> + {:else if layout === Layout.extensionDownloadList} + + {:else if layout === Layout.argentDownload} + setLayout(Layout.extensionDownloadList)} /> + {/if} + +
    + +{/if} \ No newline at end of file diff --git a/src/modal/components/DynamicIcon.svelte b/src/modal/components/DynamicIcon.svelte new file mode 100644 index 0000000..8d131d8 --- /dev/null +++ b/src/modal/components/DynamicIcon.svelte @@ -0,0 +1,21 @@ + + +{#if renderIcon.trimStart()?.startsWith(" +
    {@html renderIcon}
    +{:else} + +{/if} diff --git a/src/modal/components/FallbackMobile.svelte b/src/modal/components/FallbackMobile.svelte index 8dcb928..af2ed02 100644 --- a/src/modal/components/FallbackMobile.svelte +++ b/src/modal/components/FallbackMobile.svelte @@ -1,14 +1,23 @@ -