diff --git a/packages/core/package.json b/packages/core/package.json index 751d60b..c01d5af 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,6 +30,7 @@ "test": "vitest" }, "dependencies": { + "@module-federation/runtime": "^0.1.2", "@starknet-io/types-js": "^0.7.7" }, "devDependencies": { diff --git a/packages/core/src/__test__/main.test.ts b/packages/core/src/__test__/main.test.ts index dffdd73..a242172 100644 --- a/packages/core/src/__test__/main.test.ts +++ b/packages/core/src/__test__/main.test.ts @@ -131,7 +131,7 @@ describe("getDiscoveryWallets()", () => { it("should return all discovery wallets", async () => { const sn = getWallet({}) const discoveryWallets = await sn.getDiscoveryWallets() - expect(discoveryWallets.length).toBe(2) + expect(discoveryWallets.length).toBe(3) expect(discoveryWallets.map((w) => w.id)).contains(ArgentXMock.id) expect(discoveryWallets.map((w) => w.id)).contains(BraavosMock.id) }) diff --git a/packages/core/src/__test__/wallet.mock.ts b/packages/core/src/__test__/wallet.mock.ts index 91a2215..70c5a12 100644 --- a/packages/core/src/__test__/wallet.mock.ts +++ b/packages/core/src/__test__/wallet.mock.ts @@ -1,23 +1,28 @@ import wallets from "../discovery" import { Permission, type StarknetWindowObject } from "@starknet-io/types-js" -type WalletMock = Pick - -export const UnknownWalletAMock: WalletMock = { +export const UnknownWalletAMock: StarknetWindowObject = { id: "wallet-a", name: "Wallet A", + version: "0.0.0", icon: "https://avatars.dicebear.com/api/initials/Wallet%20A.svg", request: async () => false, + on: () => {}, + off: () => {}, } -export const UnknownWalletBMock: WalletMock = { +export const UnknownWalletBMock: StarknetWindowObject = { id: "wallet-b", name: "Wallet B", + version: "0.0.0", icon: "https://avatars.dicebear.com/api/initials/Wallet%20B.svg", request: async () => false, + on: () => {}, + off: () => {}, } -export const ArgentXMock: WalletMock = { +export const ArgentXMock: StarknetWindowObject = { ...wallets.find((w) => w.id === "argentX")!, + version: "0.0.0", request: async (request) => { switch (request.type) { case "wallet_getPermissions": @@ -26,10 +31,13 @@ export const ArgentXMock: WalletMock = { return undefined as any } }, + on: () => {}, + off: () => {}, } -export const BraavosMock: WalletMock = { +export const BraavosMock: StarknetWindowObject = { ...wallets.find((w) => w.id === "braavos")!, + version: "0.0.0", request: async (request) => { switch (request.type) { case "wallet_getPermissions": @@ -38,10 +46,12 @@ export const BraavosMock: WalletMock = { return undefined as any } }, + on: () => {}, + off: () => {}, } export function makeAuthorized(authorized: boolean) { - return (wallet: WalletMock) => + return (wallet: StarknetWindowObject) => ({ ...wallet, request: async (request) => { @@ -52,11 +62,11 @@ export function makeAuthorized(authorized: boolean) { return wallet.request(request) } }, - } as WalletMock) + } as StarknetWindowObject) } export function makeConnected(isConnected: boolean) { - return (wallet: WalletMock) => { + return (wallet: StarknetWindowObject) => { return { ...makeAuthorized(true)(wallet), request: async ({ type }) => { @@ -67,6 +77,6 @@ export function makeConnected(isConnected: boolean) { return [] } }, - } as WalletMock + } as StarknetWindowObject } } diff --git a/packages/core/src/discovery.ts b/packages/core/src/discovery.ts index aa82295..6a79df6 100644 --- a/packages/core/src/discovery.ts +++ b/packages/core/src/discovery.ts @@ -1,3 +1,5 @@ +import { metaMaskVirtualWallet } from "./wallet/virtualWallets/metaMaskVirtualWallet" + export type WalletProvider = { id: string name: string @@ -31,6 +33,17 @@ const wallets: WalletProvider[] = [ edge: "https://microsoftedge.microsoft.com/addons/detail/braavos-wallet/hkkpjehhcnhgefhbdcgfkeegglpjchdc", }, }, + { + id: metaMaskVirtualWallet.id, + name: metaMaskVirtualWallet.name, + icon: metaMaskVirtualWallet.icon, + downloads: { + chrome: + "https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn", + firefox: "https://addons.mozilla.org/en-US/firefox/addon/ether-metamask/", + edge: "https://microsoftedge.microsoft.com/addons/detail/metamask/ejbalbakoplchlghecdalmeeeajnimhm?hl=en-US", + }, + }, ] export default wallets diff --git a/packages/core/src/main.ts b/packages/core/src/main.ts index 046899a..fb1e8af 100644 --- a/packages/core/src/main.ts +++ b/packages/core/src/main.ts @@ -3,9 +3,17 @@ import { LocalStorageWrapper } from "./localStorageStore" import type { GetStarknetOptions, GetStarknetResult } from "./types" import { pipe } from "./utils" import { filterBy, filterByAuthorized } from "./wallet/filter" -import { isWalletObj } from "./wallet/isWalletObject" +import { + isFullWallet, + isVirtualWallet, + isWalletObject, +} from "./wallet/isWalletObject" import { scanObjectForWallets } from "./wallet/scan" import { sortBy } from "./wallet/sort" +import { + initiateVirtualWallets, + resolveVirtualWallet, +} from "./wallet/virtualWallets" import { Permission, type StarknetWindowObject } from "@starknet-io/types-js" export type { @@ -31,10 +39,8 @@ export type { WalletEvents, } from "@starknet-io/types-js" -export { Permission } from "@starknet-io/types-js" - export { scanObjectForWallets } from "./wallet/scan" -export { isWalletObj } from "./wallet/isWalletObject" +export { isWalletObject } from "./wallet/isWalletObject" export type { DisconnectOptions, @@ -48,16 +54,10 @@ const ssrSafeWindow = typeof window !== "undefined" ? window : {} const defaultOptions: GetStarknetOptions = { windowObject: ssrSafeWindow, - isWalletObject: isWalletObj, + isWalletObject, storageFactoryImplementation: (name: string) => new LocalStorageWrapper(name), } -declare global { - interface Window { - [key: `starknet_${string}`]: StarknetWindowObject | undefined - } -} - export function getStarknet( options: Partial = {}, ): GetStarknetResult { @@ -67,6 +67,8 @@ export function getStarknet( } const lastConnectedStore = storageFactoryImplementation("gsw-last") + initiateVirtualWallets(windowObject) + return { getAvailableWallets: async (options = {}) => { const availableWallets = scanObjectForWallets( @@ -112,7 +114,16 @@ export function getStarknet( return firstAuthorizedWallet }, - enable: async (wallet, options) => { + enable: async (inputWallet, options) => { + let wallet: StarknetWindowObject + if (isVirtualWallet(inputWallet)) { + wallet = await resolveVirtualWallet(windowObject, inputWallet) + } else if (isFullWallet(inputWallet)) { + wallet = inputWallet + } else { + throw new Error("Invalid wallet object") + } + await wallet.request({ type: "wallet_requestAccounts", params: { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f153015..787298e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,5 +1,6 @@ import { WalletProvider } from "./discovery" import { IStorageWrapper } from "./localStorageStore" +import { ensureKeysArray } from "./utils" import { FilterList } from "./wallet/filter" import { Sort } from "./wallet/sort" import type { @@ -11,7 +12,7 @@ export type { WalletProvider } from "./discovery" export interface GetStarknetOptions { windowObject: Record - isWalletObject: (wallet: any) => boolean + isWalletObject: (wallet: unknown) => boolean storageFactoryImplementation: (name: string) => IStorageWrapper } @@ -25,6 +26,36 @@ export interface DisconnectOptions { clearLastWallet?: boolean } +export interface VirtualWallet { + id: string + name: string + icon: string + windowKey: string + loadWallet: ( + windowObject: Record, + ) => Promise + hasSupport: (windowObject: Record) => Promise +} + +export const virtualWalletKeys = ensureKeysArray({ + id: true, + name: true, + icon: true, + windowKey: true, + loadWallet: true, + hasSupport: true, +}) + +export const fullWalletKeys = ensureKeysArray({ + id: true, + name: true, + version: true, + icon: true, + request: true, + on: true, + off: true, +}) + export interface GetStarknetResult { getAvailableWallets: ( options?: GetWalletOptions, @@ -35,8 +66,14 @@ export interface GetStarknetResult { getDiscoveryWallets: (options?: GetWalletOptions) => Promise // Returns all wallets in existence (from discovery file) getLastConnectedWallet: () => Promise // Returns the last wallet connected when it's still connected enable: ( - wallet: StarknetWindowObject, + wallet: StarknetWindowObject | VirtualWallet, options?: RequestAccountsParameters, ) => Promise // Connects to a wallet disconnect: (options?: DisconnectOptions) => Promise // Disconnects from a wallet } + +declare global { + interface Window { + [key: string]: unknown + } +} diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 6f159e2..a9b2f5e 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -17,3 +17,9 @@ export const pipe = (...fns: Array<(arg: T) => AllowPromise>): ((arg: T) => Promise) => (arg: T) => fns.reduce>((acc, fn) => acc.then(fn), Promise.resolve(arg)) + +export function ensureKeysArray(keysGuard: { + [k in keyof T]: true +}) { + return Object.keys(keysGuard) as (keyof T)[] +} diff --git a/packages/core/src/wallet/filter.ts b/packages/core/src/wallet/filter.ts index d5d43a4..40e1349 100644 --- a/packages/core/src/wallet/filter.ts +++ b/packages/core/src/wallet/filter.ts @@ -32,12 +32,16 @@ export const filterByAuthorized = async ( wallets: StarknetWindowObject[], ): Promise => { const preAuthResponses = await Promise.all( - wallets.map((w) => - w - .request({ type: "wallet_getPermissions" }) - .then((result: Permission[]) => result.includes(Permission.ACCOUNTS)) - .catch(() => false), - ), + wallets.map(async (wallet) => { + try { + const result: Permission[] = await wallet.request({ + type: "wallet_getPermissions", + }) + return result.includes(Permission.ACCOUNTS) + } catch { + return false + } + }), ) return wallets.filter((_, i) => preAuthResponses[i]) } diff --git a/packages/core/src/wallet/isWalletObject.ts b/packages/core/src/wallet/isWalletObject.ts index 471db3b..af2b21c 100644 --- a/packages/core/src/wallet/isWalletObject.ts +++ b/packages/core/src/wallet/isWalletObject.ts @@ -1,18 +1,22 @@ -export const isWalletObj = (wallet: any): boolean => { - try { +import { fullWalletKeys, virtualWalletKeys } from "../types" + +function createWalletGuard(keys: (keyof T)[]) { + return function hasKeys(obj: unknown): obj is T { return ( - wallet && - [ - // wallet's must have methods/members, see IStarknetWindowObject - "request", - "on", - "off", - "version", - "id", - "name", - "icon", - ].every((key) => key in wallet) + obj !== null && typeof obj === "object" && keys.every((key) => key in obj) ) + } +} + +const isFullWallet = createWalletGuard(fullWalletKeys) + +const isVirtualWallet = createWalletGuard(virtualWalletKeys) + +function isWalletObject(wallet: unknown): boolean { + try { + return isFullWallet(wallet) || isVirtualWallet(wallet) } catch (err) {} return false } + +export { isVirtualWallet, isFullWallet, isWalletObject } diff --git a/packages/core/src/wallet/virtualWallets/index.ts b/packages/core/src/wallet/virtualWallets/index.ts new file mode 100644 index 0000000..a7d5288 --- /dev/null +++ b/packages/core/src/wallet/virtualWallets/index.ts @@ -0,0 +1,31 @@ +import type { VirtualWallet } from "../../types" +import { metaMaskVirtualWallet } from "./metaMaskVirtualWallet" +import type { StarknetWindowObject } from "@starknet-io/types-js" + +const virtualWallets: VirtualWallet[] = [metaMaskVirtualWallet] + +function initiateVirtualWallets(windowObject: Record) { + virtualWallets.forEach(async (virtualWallet) => { + const hasSupport = await virtualWallet.hasSupport(windowObject) + if (hasSupport) { + windowObject[virtualWallet.windowKey] = virtualWallet + } + }) +} + +const virtualWalletsMap: Record = {} + +async function resolveVirtualWallet( + windowObject: Record, + virtualWallet: VirtualWallet, +) { + let wallet: StarknetWindowObject = virtualWalletsMap[virtualWallet.id] + if (!wallet) { + wallet = await virtualWallet.loadWallet(windowObject) + virtualWalletsMap[virtualWallet.id] = wallet + } + + return wallet +} + +export { initiateVirtualWallets, resolveVirtualWallet } diff --git a/packages/core/src/wallet/virtualWallets/metaMaskVirtualWallet.ts b/packages/core/src/wallet/virtualWallets/metaMaskVirtualWallet.ts new file mode 100644 index 0000000..bd5a7df --- /dev/null +++ b/packages/core/src/wallet/virtualWallets/metaMaskVirtualWallet.ts @@ -0,0 +1,119 @@ +import { VirtualWallet } from "../../types" +import { init, loadRemote } from "@module-federation/runtime" + +interface MetaMaskProvider { + isMetaMask: boolean + request(options: { method: string }): Promise +} + +function isMetaMaskProvider(obj: unknown): obj is MetaMaskProvider { + return ( + obj !== null && + typeof obj === "object" && + obj.hasOwnProperty("isMetaMask") && + obj.hasOwnProperty("request") + ) +} + +function detectMetaMaskProvider( + windowObject: Record, + { timeout = 3000 } = {}, +): Promise { + let handled = false + return new Promise((resolve) => { + const handleEIP6963Provider = (event: CustomEvent) => { + const { info, provider } = event.detail + const rdnsCheck = + info.rdns === "io.metamask" || info.rdns === "io.metamask.flask" + if (rdnsCheck && isMetaMaskProvider(provider)) { + resolve(provider) + handled = true + } + } + + if (typeof windowObject.addEventListener === "function") { + windowObject.addEventListener( + "eip6963:announceProvider", + handleEIP6963Provider, + ) + } + + setTimeout(() => { + if (!handled) { + resolve(null) + } + }, timeout) + + // Notify event listeners and other parts of the dapp that a provider is requested. + if (typeof windowObject.dispatchEvent === "function") { + windowObject.dispatchEvent(new Event("eip6963:requestProvider")) + } + }) +} + +async function waitForMetaMaskProvider( + windowObject: Record, + options: { timeout?: number; retries?: number } = {}, +): Promise { + const { timeout = 3000, retries = 0 } = options + + let provider: MetaMaskProvider | null = null + try { + provider = await detectMetaMaskProvider(windowObject, { timeout }) + } catch { + // Silent error - do nothing + } + + if (provider) { + return provider + } + + if (retries === 0) { + return null + } + + provider = await waitForMetaMaskProvider({ timeout, retries: retries - 1 }) + return provider +} + +async function detectMetamaskSupport(windowObject: Record) { + const provider = await waitForMetaMaskProvider(windowObject, { retries: 3 }) + return Boolean(provider) +} + +const metaMaskVirtualWallet: VirtualWallet = { + id: "metamask", + name: "MetaMask", + icon: `data:image/svg+xml;utf8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMTIiIGhlaWdodD0iMTg5IiB2aWV3Qm94PSIwIDAgMjEyIDE4OSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cG9seWdvbiBmaWxsPSIjQ0RCREIyIiBwb2ludHM9IjYwLjc1IDE3My4yNSA4OC4zMTMgMTgwLjU2MyA4OC4zMTMgMTcxIDkwLjU2MyAxNjguNzUgMTA2LjMxMyAxNjguNzUgMTA2LjMxMyAxODAgMTA2LjMxMyAxODcuODc1IDg5LjQzOCAxODcuODc1IDY4LjYyNSAxNzguODc1Ii8+PHBvbHlnb24gZmlsbD0iI0NEQkRCMiIgcG9pbnRzPSIxMDUuNzUgMTczLjI1IDEzMi43NSAxODAuNTYzIDEzMi43NSAxNzEgMTM1IDE2OC43NSAxNTAuNzUgMTY4Ljc1IDE1MC43NSAxODAgMTUwLjc1IDE4Ny44NzUgMTMzLjg3NSAxODcuODc1IDExMy4wNjMgMTc4Ljg3NSIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMjU2LjUgMCkiLz48cG9seWdvbiBmaWxsPSIjMzkzOTM5IiBwb2ludHM9IjkwLjU2MyAxNTIuNDM4IDg4LjMxMyAxNzEgOTEuMTI1IDE2OC43NSAxMjAuMzc1IDE2OC43NSAxMjMuNzUgMTcxIDEyMS41IDE1Mi40MzggMTE3IDE0OS42MjUgOTQuNSAxNTAuMTg4Ii8+PHBvbHlnb24gZmlsbD0iI0Y4OUMzNSIgcG9pbnRzPSI3NS4zNzUgMjcgODguODc1IDU4LjUgOTUuMDYzIDE1MC4xODggMTE3IDE1MC4xODggMTIzLjc1IDU4LjUgMTM2LjEyNSAyNyIvPjxwb2x5Z29uIGZpbGw9IiNGODlEMzUiIHBvaW50cz0iMTYuMzEzIDk2LjE4OCAuNTYzIDE0MS43NSAzOS45MzggMTM5LjUgNjUuMjUgMTM5LjUgNjUuMjUgMTE5LjgxMyA2NC4xMjUgNzkuMzEzIDU4LjUgODMuODEzIi8+PHBvbHlnb24gZmlsbD0iI0Q4N0MzMCIgcG9pbnRzPSI0Ni4xMjUgMTAxLjI1IDkyLjI1IDEwMi4zNzUgODcuMTg4IDEyNiA2NS4yNSAxMjAuMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VBOEQzQSIgcG9pbnRzPSI0Ni4xMjUgMTAxLjgxMyA2NS4yNSAxMTkuODEzIDY1LjI1IDEzNy44MTMiLz48cG9seWdvbiBmaWxsPSIjRjg5RDM1IiBwb2ludHM9IjY1LjI1IDEyMC4zNzUgODcuNzUgMTI2IDk1LjA2MyAxNTAuMTg4IDkwIDE1MyA2NS4yNSAxMzguMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VCOEYzNSIgcG9pbnRzPSI2NS4yNSAxMzguMzc1IDYwLjc1IDE3My4yNSA5MC41NjMgMTUyLjQzOCIvPjxwb2x5Z29uIGZpbGw9IiNFQThFM0EiIHBvaW50cz0iOTIuMjUgMTAyLjM3NSA5NS4wNjMgMTUwLjE4OCA4Ni42MjUgMTI1LjcxOSIvPjxwb2x5Z29uIGZpbGw9IiNEODdDMzAiIHBvaW50cz0iMzkuMzc1IDEzOC45MzggNjUuMjUgMTM4LjM3NSA2MC43NSAxNzMuMjUiLz48cG9seWdvbiBmaWxsPSIjRUI4RjM1IiBwb2ludHM9IjEyLjkzOCAxODguNDM4IDYwLjc1IDE3My4yNSAzOS4zNzUgMTM4LjkzOCAuNTYzIDE0MS43NSIvPjxwb2x5Z29uIGZpbGw9IiNFODgyMUUiIHBvaW50cz0iODguODc1IDU4LjUgNjQuNjg4IDc4Ljc1IDQ2LjEyNSAxMDEuMjUgOTIuMjUgMTAyLjkzOCIvPjxwb2x5Z29uIGZpbGw9IiNERkNFQzMiIHBvaW50cz0iNjAuNzUgMTczLjI1IDkwLjU2MyAxNTIuNDM4IDg4LjMxMyAxNzAuNDM4IDg4LjMxMyAxODAuNTYzIDY4LjA2MyAxNzYuNjI1Ii8+PHBvbHlnb24gZmlsbD0iI0RGQ0VDMyIgcG9pbnRzPSIxMjEuNSAxNzMuMjUgMTUwLjc1IDE1Mi40MzggMTQ4LjUgMTcwLjQzOCAxNDguNSAxODAuNTYzIDEyOC4yNSAxNzYuNjI1IiB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSAyNzIuMjUgMCkiLz48cG9seWdvbiBmaWxsPSIjMzkzOTM5IiBwb2ludHM9IjcwLjMxMyAxMTIuNSA2NC4xMjUgMTI1LjQzOCA4Ni4wNjMgMTE5LjgxMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMTUwLjE4OCAwKSIvPjxwb2x5Z29uIGZpbGw9IiNFODhGMzUiIHBvaW50cz0iMTIuMzc1IC41NjMgODguODc1IDU4LjUgNzUuOTM4IDI3Ii8+PHBhdGggZmlsbD0iIzhFNUEzMCIgZD0iTTEyLjM3NTAwMDIsMC41NjI1MDAwMDggTDIuMjUwMDAwMDMsMzEuNTAwMDAwNSBMNy44NzUwMDAxMiw2NS4yNTAwMDEgTDMuOTM3NTAwMDYsNjcuNTAwMDAxIEw5LjU2MjUwMDE0LDcyLjU2MjUgTDUuMDYyNTAwMDgsNzYuNTAwMDAxMSBMMTEuMjUsODIuMTI1MDAxMiBMNy4zMTI1MDAxMSw4NS41MDAwMDEzIEwxNi4zMTI1MDAyLDk2Ljc1MDAwMTQgTDU4LjUwMDAwMDksODMuODEyNTAxMiBDNzkuMTI1MDAxMiw2Ny4zMTI1MDA0IDg5LjI1MDAwMTMsNTguODc1MDAwMyA4OC44NzUwMDEzLDU4LjUwMDAwMDkgQzg4LjUwMDAwMTMsNTguMTI1MDAwOSA2My4wMDAwMDA5LDM4LjgxMjUwMDYgMTIuMzc1MDAwMiwwLjU2MjUwMDAwOCBaIi8+PGcgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMjExLjUgMCkiPjxwb2x5Z29uIGZpbGw9IiNGODlEMzUiIHBvaW50cz0iMTYuMzEzIDk2LjE4OCAuNTYzIDE0MS43NSAzOS45MzggMTM5LjUgNjUuMjUgMTM5LjUgNjUuMjUgMTE5LjgxMyA2NC4xMjUgNzkuMzEzIDU4LjUgODMuODEzIi8+PHBvbHlnb24gZmlsbD0iI0Q4N0MzMCIgcG9pbnRzPSI0Ni4xMjUgMTAxLjI1IDkyLjI1IDEwMi4zNzUgODcuMTg4IDEyNiA2NS4yNSAxMjAuMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VBOEQzQSIgcG9pbnRzPSI0Ni4xMjUgMTAxLjgxMyA2NS4yNSAxMTkuODEzIDY1LjI1IDEzNy44MTMiLz48cG9seWdvbiBmaWxsPSIjRjg5RDM1IiBwb2ludHM9IjY1LjI1IDEyMC4zNzUgODcuNzUgMTI2IDk1LjA2MyAxNTAuMTg4IDkwIDE1MyA2NS4yNSAxMzguMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VCOEYzNSIgcG9pbnRzPSI2NS4yNSAxMzguMzc1IDYwLjc1IDE3My4yNSA5MCAxNTMiLz48cG9seWdvbiBmaWxsPSIjRUE4RTNBIiBwb2ludHM9IjkyLjI1IDEwMi4zNzUgOTUuMDYzIDE1MC4xODggODYuNjI1IDEyNS43MTkiLz48cG9seWdvbiBmaWxsPSIjRDg3QzMwIiBwb2ludHM9IjM5LjM3NSAxMzguOTM4IDY1LjI1IDEzOC4zNzUgNjAuNzUgMTczLjI1Ii8+PHBvbHlnb24gZmlsbD0iI0VCOEYzNSIgcG9pbnRzPSIxMi45MzggMTg4LjQzOCA2MC43NSAxNzMuMjUgMzkuMzc1IDEzOC45MzggLjU2MyAxNDEuNzUiLz48cG9seWdvbiBmaWxsPSIjRTg4MjFFIiBwb2ludHM9Ijg4Ljg3NSA1OC41IDY0LjY4OCA3OC43NSA0Ni4xMjUgMTAxLjI1IDkyLjI1IDEwMi45MzgiLz48cG9seWdvbiBmaWxsPSIjMzkzOTM5IiBwb2ludHM9IjcwLjMxMyAxMTIuNSA2NC4xMjUgMTI1LjQzOCA4Ni4wNjMgMTE5LjgxMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMTUwLjE4OCAwKSIvPjxwb2x5Z29uIGZpbGw9IiNFODhGMzUiIHBvaW50cz0iMTIuMzc1IC41NjMgODguODc1IDU4LjUgNzUuOTM4IDI3Ii8+PHBhdGggZmlsbD0iIzhFNUEzMCIgZD0iTTEyLjM3NTAwMDIsMC41NjI1MDAwMDggTDIuMjUwMDAwMDMsMzEuNTAwMDAwNSBMNy44NzUwMDAxMiw2NS4yNTAwMDEgTDMuOTM3NTAwMDYsNjcuNTAwMDAxIEw5LjU2MjUwMDE0LDcyLjU2MjUgTDUuMDYyNTAwMDgsNzYuNTAwMDAxMSBMMTEuMjUsODIuMTI1MDAxMiBMNy4zMTI1MDAxMSw4NS41MDAwMDEzIEwxNi4zMTI1MDAyLDk2Ljc1MDAwMTQgTDU4LjUwMDAwMDksODMuODEyNTAxMiBDNzkuMTI1MDAxMiw2Ny4zMTI1MDA0IDg5LjI1MDAwMTMsNTguODc1MDAwMyA4OC44NzUwMDEzLDU4LjUwMDAwMDkgQzg4LjUwMDAwMTMsNTguMTI1MDAwOSA2My4wMDAwMDA5LDM4LjgxMjUwMDYgMTIuMzc1MDAwMiwwLjU2MjUwMDAwOCBaIi8+PC9nPjwvZz48L3N2Zz4=`, + windowKey: "starknet_metamask", + async loadWallet(windowObject) { + await init({ + name: "MetaMaskStarknetSnapWallet", + remotes: [ + { + name: "MetaMaskStarknetSnapWallet", + alias: "MetaMaskStarknetSnapWallet", + entry: + "https://s3.eu-central-1.amazonaws.com/dev.snaps.consensys.io/get-starknet/remoteEntry.js", + }, + ], + }) + + const result = await loadRemote("MetaMaskStarknetSnapWallet/index") + + const { MetaMaskSnapWallet, MetaMaskSnap } = result as { + MetaMaskSnapWallet: any + MetaMaskSnap: any + } + + const provider = await MetaMaskSnap.GetProvider(windowObject) + const wallet = new MetaMaskSnapWallet(provider, "*") + + return wallet + }, + async hasSupport(windowObject) { + return await detectMetamaskSupport(windowObject) + }, +} + +export { metaMaskVirtualWallet } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4e0709..82d8da7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,6 +66,9 @@ importers: packages/core: dependencies: + '@module-federation/runtime': + specifier: ^0.1.2 + version: 0.1.2 '@starknet-io/types-js': specifier: ^0.7.7 version: 0.7.7 @@ -990,6 +993,16 @@ packages: resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} dev: true + /@module-federation/runtime@0.1.2: + resolution: {integrity: sha512-KdTL2aR48p+hOdMuim5osN15xkxzm1ccyXnvqR6TSl61r+chq7qFhU124JQYDU/SSkmbRm7Fb53IaaXo/PjKQw==} + dependencies: + '@module-federation/sdk': 0.1.2 + dev: false + + /@module-federation/sdk@0.1.2: + resolution: {integrity: sha512-G9af5sV/yyz5EpHOpg+y5gtDxomT6T1c7KETMs9k76GOcHKGBAoqiC8RJimgncN6O2wPhiBKEvNjf9VxrrX+gw==} + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'}