diff --git a/package.json b/package.json index 219e416..2313c33 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "tokenbound-connectorkit", + "name": "tokenbound-connectorkit-v3", "version": "1.0.1", "repository": "https://github.com/horuslabsio/tba-connector-v2", "private": false, @@ -101,6 +101,7 @@ "vitest": "^1.6.0", "ws": "^8.8.1", "zod": "^3.20.6" + }, "peerDependencies": { "starknet": "^6.9.0" diff --git a/src/connectors/index.ts b/src/connectors/index.ts index 9cc8100..313c7c2 100644 --- a/src/connectors/index.ts +++ b/src/connectors/index.ts @@ -1,4 +1,3 @@ export * from "./connector" -export * from "./webwallet" export * from "./tokenboundAccount" diff --git a/src/connectors/tokenboundAccount/constants.ts b/src/connectors/tokenboundAccount/constants.ts index 0f4c2d9..08c8ac9 100644 --- a/src/connectors/tokenboundAccount/constants.ts +++ b/src/connectors/tokenboundAccount/constants.ts @@ -42,3 +42,5 @@ export const RPC_NODE_URL_MAINNET = export const DEFAULT_CHAIN_ID = "SN_SEPOLIA" + +export const DEFAULT_WEBWALLET_URL = "https://web.argent.xyz" diff --git a/src/connectors/tokenboundAccount/helpers/openTokenboundwallet.ts b/src/connectors/tokenboundAccount/helpers/openTokenboundwallet.ts index 867cd67..e5322b5 100644 --- a/src/connectors/tokenboundAccount/helpers/openTokenboundwallet.ts +++ b/src/connectors/tokenboundAccount/helpers/openTokenboundwallet.ts @@ -1,7 +1,9 @@ import { getTokenboundAccountStarknetObject } from "../starknetWindowObject/getTokenboundWalletStarknetObject" import { createModal, hideModal } from "../starknetWindowObject/wormhole" -import { openWebwallet } from "../../webwallet/helpers/openWebwallet" +import { openWebwallet } from "../webwallet/helpers/openWebwallet" import { TBAStarknetWindowObject } from "../types/connector" +import { DEFAULT_WEBWALLET_URL } from "../constants" +import { WebWalletStarknetWindowObject } from "../webwallet/starknetWindowObject/argentStarknetWindowObject" interface Options { address: string @@ -18,78 +20,6 @@ const getIframeMessageData = (): Promise => { }) } -// export const openTokenboundModal = async ( -// origin: string, -// chainId: string, -// ): Promise => { - -// console.log("hello world") -// const modalId = "tokenbound-account-modal" -// const iframeId = "tokenbound-account-iframe" - -// const existingIframe = document.getElementById(iframeId) -// const existingModal = document.getElementById(modalId) -// if (existingIframe && existingModal) { -// existingIframe.remove() -// existingModal.remove() -// } - -// const { modal } = await createModal(origin, true) -// return new Promise((resolve) => { - -// window.addEventListener( -// "message", -// async (event: MessageEvent) => { -// if (event.type === "message") { -// const { address, parentWallet }: Options = -// await getIframeMessageData() - -// if (!parentWallet || !address) return - -// const wallet_id = parentWallet.toLowerCase() -// const globalObject: Record = globalThis - - - - -// let wallet: TBAStarknetWindowObject | undefined - -// if (wallet_id === "braavos" || wallet_id === "argentx") { - -// wallet = -// globalObject[ -// `starknet_${wallet_id === "argentx" ? "argentX" : wallet_id}` -// ] - -// if(wallet == null) { -// alert("Wallet not found!") -// } -// } - -// if (wallet_id === "argentwebwallet") { -// // _wallet = (await openWebwallet(origin)) ?? null -// } - - -// if (wallet) { -// const starknetWindowObject = -// await getTokenboundAccountStarknetObject({ -// address, -// wallet, -// chainId, -// }) - -// resolve(starknetWindowObject) -// hideModal(modal) -// } - - -// } -// }, -// { once: false }, -// ) -// }) -// } export const openTokenboundModal = async ( @@ -131,16 +61,18 @@ export const openTokenboundModal = async ( } if (wallet_id === "argentwebwallet") { - // _wallet = (await openWebwallet(origin)) ?? null + const webWallet = (await openWebwallet(DEFAULT_WEBWALLET_URL)) ?? null + await ( + webWallet as WebWalletStarknetWindowObject + ).connectWebwallet() + wallet = webWallet as TBAStarknetWindowObject; } - // Wallet not found scenario if (!wallet) { alert("Wallet not found!"); return; } - // Wallet is found, proceed const starknetWindowObject = await getTokenboundAccountStarknetObject({ address, wallet, diff --git a/src/connectors/webwallet/constants.ts b/src/connectors/tokenboundAccount/webwallet/constants.ts similarity index 100% rename from src/connectors/webwallet/constants.ts rename to src/connectors/tokenboundAccount/webwallet/constants.ts diff --git a/src/connectors/webwallet/helpers/fetchAllowedDapps.ts b/src/connectors/tokenboundAccount/webwallet/helpers/fetchAllowedDapps.ts similarity index 100% rename from src/connectors/webwallet/helpers/fetchAllowedDapps.ts rename to src/connectors/tokenboundAccount/webwallet/helpers/fetchAllowedDapps.ts diff --git a/src/connectors/tokenboundAccount/webwallet/helpers/mapTargetUrlToNetworkId.ts b/src/connectors/tokenboundAccount/webwallet/helpers/mapTargetUrlToNetworkId.ts new file mode 100644 index 0000000..580a52d --- /dev/null +++ b/src/connectors/tokenboundAccount/webwallet/helpers/mapTargetUrlToNetworkId.ts @@ -0,0 +1,32 @@ +import { constants } from "starknet" + +// Using NetworkName as a value. +const Network: typeof constants.NetworkName = constants.NetworkName + +const DEVELOPMENT_NETWORK = Network.SN_SEPOLIA + +export function mapTargetUrlToNetworkId(target: string): constants.NetworkName { + try { + const { origin } = new URL(target) + if (origin.includes("localhost") || origin.includes("127.0.0.1")) { + return DEVELOPMENT_NETWORK + } + if (origin.includes("hydrogen")) { + return Network.SN_SEPOLIA + } + if (origin.includes("staging")) { + return Network.SN_MAIN + } + if (origin.includes("dev")) { + return Network.SN_SEPOLIA + } + if (origin.includes("argent.xyz")) { + return Network.SN_MAIN + } + } catch (e) { + console.warn( + "Could not determine network from target URL, defaulting to mainnet-alpha", + ) + } + return Network.SN_MAIN +} diff --git a/src/connectors/webwallet/helpers/mapTargetUrlToNodeUrl.ts b/src/connectors/tokenboundAccount/webwallet/helpers/mapTargetUrlToNodeUrl.ts similarity index 90% rename from src/connectors/webwallet/helpers/mapTargetUrlToNodeUrl.ts rename to src/connectors/tokenboundAccount/webwallet/helpers/mapTargetUrlToNodeUrl.ts index aff97a9..105e76d 100644 --- a/src/connectors/webwallet/helpers/mapTargetUrlToNodeUrl.ts +++ b/src/connectors/tokenboundAccount/webwallet/helpers/mapTargetUrlToNodeUrl.ts @@ -1,4 +1,4 @@ -import { getRandomPublicRPCNode } from "../../../helpers/publicRcpNodes" +import { getRandomPublicRPCNode } from "./publicRcpNodes" export function mapTargetUrlToNodeUrl(target: string): string { const publicRPCNode = getRandomPublicRPCNode() diff --git a/src/connectors/webwallet/helpers/openWebwallet.ts b/src/connectors/tokenboundAccount/webwallet/helpers/openWebwallet.ts similarity index 88% rename from src/connectors/webwallet/helpers/openWebwallet.ts rename to src/connectors/tokenboundAccount/webwallet/helpers/openWebwallet.ts index d3b1d22..27a2d75 100644 --- a/src/connectors/webwallet/helpers/openWebwallet.ts +++ b/src/connectors/tokenboundAccount/webwallet/helpers/openWebwallet.ts @@ -1,9 +1,9 @@ import type { StarknetWindowObject } from "@starknet-io/types-js" -import { mapTargetUrlToNetworkId } from "../../../helpers/mapTargetUrlToNetworkId" import { getWebWalletStarknetObject } from "../starknetWindowObject/getWebWalletStarknetObject" import { createModal } from "../starknetWindowObject/wormhole" import { fetchAllowedDapps } from "./fetchAllowedDapps" import { trpcProxyClient } from "./trpc" +import { mapTargetUrlToNetworkId } from "./mapTargetUrlToNetworkId" const checkIncognitoChrome = async (isChrome: boolean) => { return new Promise((resolve) => { @@ -48,12 +48,12 @@ export const openWebwallet = async ( // if not chrome or is chrome incognito // use the popup mode and avoid checking allowed dapps for iframes if (!isChrome || isChromeIncognito) { - const windowProxyClient = trpcProxyClient({}) - return await getWebWalletStarknetObject( - origin, - windowProxyClient, - undefined, - ) + // const windowProxyClient = trpcProxyClient({}) + // return await getWebWalletStarknetObject( + // origin, + // windowProxyClient, + // undefined, + // ) } const network = mapTargetUrlToNetworkId(origin) @@ -76,19 +76,26 @@ export const openWebwallet = async ( const iframeTrpcProxyClient = trpcProxyClient({ iframe: iframe.contentWindow ?? undefined, }) - await iframeTrpcProxyClient.authorize.mutate() + const starknetWindowObject = await getWebWalletStarknetObject( origin, iframeTrpcProxyClient, { modal, iframe }, ) return starknetWindowObject - } else { + + + } + + else { const windowProxyClient = trpcProxyClient({}) - return await getWebWalletStarknetObject( + return await getWebWalletStarknetObject( origin, windowProxyClient, undefined, ) + } + + } diff --git a/src/connectors/webwallet/helpers/popupSizes.ts b/src/connectors/tokenboundAccount/webwallet/helpers/popupSizes.ts similarity index 100% rename from src/connectors/webwallet/helpers/popupSizes.ts rename to src/connectors/tokenboundAccount/webwallet/helpers/popupSizes.ts diff --git a/src/connectors/tokenboundAccount/webwallet/helpers/publicRcpNodes.ts b/src/connectors/tokenboundAccount/webwallet/helpers/publicRcpNodes.ts new file mode 100644 index 0000000..22ca837 --- /dev/null +++ b/src/connectors/tokenboundAccount/webwallet/helpers/publicRcpNodes.ts @@ -0,0 +1,22 @@ +export type PublicRpcNode = { + mainnet: string + testnet: string +} + +// Public RPC nodes +export const BLAST_RPC_NODE: PublicRpcNode = { + mainnet: "https://starknet-mainnet.public.blastapi.io", + testnet: "https://starknet-sepolia.public.blastapi.io", +} as const + +export const LAVA_RPC_NODE: PublicRpcNode = { + mainnet: "https://rpc.starknet.lava.build", + testnet: "https://rpc.starknet-sepolia.lava.build", +} as const + +export const PUBLIC_RPC_NODES = [BLAST_RPC_NODE, LAVA_RPC_NODE] as const + +export function getRandomPublicRPCNode() { + const randomIndex = Math.floor(Math.random() * PUBLIC_RPC_NODES.length) + return PUBLIC_RPC_NODES[randomIndex] +} diff --git a/src/connectors/webwallet/helpers/trpc.ts b/src/connectors/tokenboundAccount/webwallet/helpers/trpc.ts similarity index 96% rename from src/connectors/webwallet/helpers/trpc.ts rename to src/connectors/tokenboundAccount/webwallet/helpers/trpc.ts index 956eb7c..4df1375 100644 --- a/src/connectors/webwallet/helpers/trpc.ts +++ b/src/connectors/tokenboundAccount/webwallet/helpers/trpc.ts @@ -1,3 +1,4 @@ +import { Permission } from "@starknet-io/types-js" import type { CreateTRPCProxyClient } from "@trpc/client" import { createTRPCProxyClient, loggerLink, splitLink } from "@trpc/client" import { initTRPC } from "@trpc/server" @@ -8,9 +9,9 @@ import { RpcCallsArraySchema, StarknetMethodArgumentsSchemas, deployAccountContractSchema, -} from "../../../types/window" +} from "../types/window" + import { DEFAULT_WEBWALLET_URL } from "../constants" -import { Permission } from "@starknet-io/types-js" const t = initTRPC.create({ isServer: false, @@ -60,6 +61,14 @@ const appRouter = t.router({ return true }), connect: t.procedure.mutation(async () => ""), + connectWebwallet: t.procedure + .output( + z.object({ + account: z.string().array().optional(), + chainId: z.string().optional(), + }), + ) + .mutation(async () => ({})), enable: t.procedure.output(z.string()).mutation(async () => ""), execute: t.procedure .input(StarknetMethodArgumentsSchemas.execute) diff --git a/src/connectors/webwallet/starknetWindowObject/argentStarknetWindowObject.ts b/src/connectors/tokenboundAccount/webwallet/starknetWindowObject/argentStarknetWindowObject.ts similarity index 96% rename from src/connectors/webwallet/starknetWindowObject/argentStarknetWindowObject.ts rename to src/connectors/tokenboundAccount/webwallet/starknetWindowObject/argentStarknetWindowObject.ts index 998cf1f..77f9311 100644 --- a/src/connectors/webwallet/starknetWindowObject/argentStarknetWindowObject.ts +++ b/src/connectors/tokenboundAccount/webwallet/starknetWindowObject/argentStarknetWindowObject.ts @@ -1,5 +1,3 @@ -import type { CreateTRPCProxyClient } from "@trpc/client" -import type { constants } from "starknet" import type { AccountChangeEventHandler, NetworkChangeEventHandler, @@ -7,6 +5,8 @@ import type { StarknetWindowObject, WalletEvents, } from "@starknet-io/types-js" +import type { CreateTRPCProxyClient } from "@trpc/client" +import type { constants } from "starknet" import { EXECUTE_POPUP_HEIGHT, EXECUTE_POPUP_WIDTH, @@ -35,7 +35,12 @@ export type LoginStatus = { export type WebWalletStarknetWindowObject = StarknetWindowObject & { getLoginStatus(): Promise + connectWebwallet(): Promise<{ + account?: string[] + chainId?: string + }> } + export const getArgentStarknetWindowObject = ( options: GetArgentStarknetWindowObject, proxyLink: CreateTRPCProxyClient, @@ -45,6 +50,9 @@ export const getArgentStarknetWindowObject = ( getLoginStatus: () => { return proxyLink.getLoginStatus.mutate() }, + connectWebwallet: () => { + return proxyLink.connectWebwallet.mutate() + }, async request(call) { switch (call.type) { case "wallet_requestAccounts": { diff --git a/src/connectors/webwallet/starknetWindowObject/getWebWalletStarknetObject.ts b/src/connectors/tokenboundAccount/webwallet/starknetWindowObject/getWebWalletStarknetObject.ts similarity index 100% rename from src/connectors/webwallet/starknetWindowObject/getWebWalletStarknetObject.ts rename to src/connectors/tokenboundAccount/webwallet/starknetWindowObject/getWebWalletStarknetObject.ts diff --git a/src/connectors/webwallet/starknetWindowObject/wormhole.ts b/src/connectors/tokenboundAccount/webwallet/starknetWindowObject/wormhole.ts similarity index 100% rename from src/connectors/webwallet/starknetWindowObject/wormhole.ts rename to src/connectors/tokenboundAccount/webwallet/starknetWindowObject/wormhole.ts diff --git a/src/connectors/tokenboundAccount/webwallet/types/window.ts b/src/connectors/tokenboundAccount/webwallet/types/window.ts new file mode 100644 index 0000000..e403884 --- /dev/null +++ b/src/connectors/tokenboundAccount/webwallet/types/window.ts @@ -0,0 +1,261 @@ +// moved this file from @argent/x-window +// in order to remove get-starknet-core v3 dependency and stakrnetjs v5 +// TODO: update x-window to export this file only, probably as a separate package + +import type { InvokeFunctionResponse, Signature } from "starknet" +import { z } from "zod" + +const HEX_REGEX = /^0x[0-9a-f]+$/i +const DECIMAL_REGEX = /^\d+$/ + +const shortStringSchema = z + .string() + .min(1, "The short string cannot be empty") + .max(31, "The short string cannot exceed 31 characters") + .refine( + (value) => !HEX_REGEX.test(value), + "The shortString should not be a hex string", + ) + .refine( + (value) => !DECIMAL_REGEX.test(value), + "The shortString should not be an integer string", + ) + +export const BigNumberishSchema = z.union([ + z + .string() + .regex( + HEX_REGEX, + "Only hex, integers and bigint are supported in calldata", + ), + z + .string() + .regex( + DECIMAL_REGEX, + "Only hex, integers and bigint are supported in calldata", + ), + shortStringSchema, + z.number().int("Only hex, integers and bigint are supported in calldata"), + z.bigint(), +]) + +export const CallSchema = z.object({ + contractAddress: z.string(), + entrypoint: z.string(), + calldata: z + .array(BigNumberishSchema.or(z.array(BigNumberishSchema))) + .optional(), +}) + +export const CallsArraySchema = z.array(CallSchema).nonempty() + +export const typedDataSchema = z.object({ + types: z.record( + z.array( + z.union([ + z.object({ + name: z.string(), + type: z.literal("merkletree"), + contains: z.string(), + }), + z.object({ + name: z.string(), + type: z.literal("enum"), + contains: z.string(), + }), + z.object({ + name: z.string(), + type: z.string(), + }), + ]), + ), + ), + primaryType: z.string(), + domain: z.record(z.unknown()), + message: z.record(z.unknown()), +}) + +export const AssetSchema = z.object({ + type: z.literal("ERC20"), + options: z.object({ + address: z.string(), + symbol: z.string().optional(), + decimals: z.number().optional(), + image: z.string().optional(), + name: z.string().optional(), + }), +}) + +export const AddStarknetChainParametersSchema = z.union([ + z.object({ + id: z.string(), + chain_id: z.string(), + chain_name: z.string(), + rpc_urls: z.array(z.string()).optional(), + native_currency: AssetSchema.optional(), + block_explorer_url: z.array(z.string()).optional(), + }), + z + .object({ + id: z.string(), + chainId: z.string(), + chainName: z.string(), + rpcUrls: z.array(z.string()).optional(), + nativeCurrency: AssetSchema.optional(), + blockExplorerUrl: z.array(z.string()).optional(), + }) + // for backwards compatibility + .transform((value) => ({ + id: value.id, + chain_id: value.chainId, + chain_name: value.chainName, + rpc_urls: value.rpcUrls, + native_currency: value.nativeCurrency, + block_explorer_url: value.blockExplorerUrl, + })), +]) +export const StarknetMethodArgumentsSchemas = { + enable: z + .tuple([ + z + .object({ + starknetVersion: z + .union([z.literal("v3"), z.literal("v4"), z.literal("v5")]) + .optional(), + }) + .optional(), + ]) + .or(z.tuple([])), + addStarknetChain: z.tuple([AddStarknetChainParametersSchema]), + switchStarknetChain: z.tuple([ + z.object({ + chainId: z.string(), + }), + ]), + watchAsset: z.tuple([AssetSchema]), + requestAccounts: z.tuple([ + z.object({ + silent_mode: z.boolean().optional(), + }), + ]), + execute: z.tuple([ + CallsArraySchema.or(CallSchema), + z + .object({ + nonce: BigNumberishSchema.optional(), + maxFee: BigNumberishSchema.optional(), + version: BigNumberishSchema.optional(), + }) + .optional(), + ]), + signMessage: z.tuple([typedDataSchema]), +} as const + +export type StarknetMethods = { + enable: ( + ...args: z.infer + ) => Promise + addStarknetChain: ( + ...args: z.infer + ) => Promise + switchStarknetChain: ( + ...args: z.infer + ) => Promise + watchAsset: ( + ...args: z.infer + ) => Promise + requestAccounts: ( + ...args: z.infer + ) => Promise + execute: ( + ...args: z.infer + ) => Promise + signMessage: ( + ...args: z.infer + ) => Promise + + getLoginStatus: () => Promise< + | { isLoggedIn: false } + | { isLoggedIn: true; hasSession: boolean; isPreauthorized: boolean } + > +} + +export const StarknetExecuteBackwardCompatibleArgumentsSchemas = { + execute: z + .tuple([ + CallsArraySchema.or(CallSchema), + z + .object({ + nonce: BigNumberishSchema.optional(), + maxFee: BigNumberishSchema.optional(), + version: BigNumberishSchema.optional(), + }) + .optional(), + ]) + .or( + z.tuple([ + CallsArraySchema.or(CallSchema), + z.array(z.any()).optional(), + z + .object({ + nonce: BigNumberishSchema.optional(), + maxFee: BigNumberishSchema.optional(), + version: BigNumberishSchema.optional(), + }) + .optional(), + ]), + ), +} as const + +export type StarknetExecuteBackwardCompatibleMethods = { + execute: ( + ...args: z.infer< + typeof StarknetExecuteBackwardCompatibleArgumentsSchemas.execute + > + ) => Promise +} + +export type ConnectMethods = { + connect: () => void +} + +export type ModalMethods = { + shouldShow: () => void + shouldHide: () => void + heightChanged: (height: number) => void +} + +export type WebWalletMethods = ConnectMethods & ModalMethods + +export type IframeMethods = { + connect: () => void +} + +export const RpcCallSchema = z + .object({ + contract_address: z.string(), + entry_point: z.string(), + calldata: z.array(BigNumberishSchema).optional(), + }) + .transform(({ contract_address, entry_point, calldata }) => ({ + contractAddress: contract_address, + entrypoint: entry_point, + calldata: calldata || [], + })) + +export const RpcCallsArraySchema = z.array(RpcCallSchema).nonempty() + +const VERSIONS = { + ZERO: 0, + ONE: 1, +} as const + +export const deployAccountContractSchema = z.object({ + address: z.string(), + class_hash: z.string(), + salt: z.string(), + calldata: z.array(z.string()), + sigdata: z.array(z.string()).optional(), + //version: z.literal([0, 1]), + version: z.nativeEnum(VERSIONS), // allow only 0 | 1, workaround since zod doesn't support literals as numbers +}) diff --git a/src/connectors/webwallet/index.ts b/src/connectors/webwallet/index.ts deleted file mode 100644 index e8ab863..0000000 --- a/src/connectors/webwallet/index.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { - AccountInterface, - ProviderInterface, - ProviderOptions, - WalletAccount, -} from "starknet" -import { - Permission, - RequestFnCall, - RpcMessage, - RpcTypeToMessageMap, - type AccountChangeEventHandler, - type StarknetWindowObject, -} from "@starknet-io/types-js" -import { - ConnectorNotConnectedError, - ConnectorNotFoundError, - UserRejectedRequestError, -} from "../../errors" -import { getStarknetChainId } from "../../helpers/getStarknetChainId" -import { removeStarknetLastConnectedWallet } from "../../helpers/lastConnected" -import { - Connector, - type ConnectorData, - type ConnectorIcons, -} from "../connector" -import { DEFAULT_WEBWALLET_ICON, DEFAULT_WEBWALLET_URL } from "./constants" -import { openWebwallet } from "./helpers/openWebwallet" -import { setPopupOptions } from "./helpers/trpc" - -let _wallet: StarknetWindowObject | null = null - -interface WebWalletConnectorOptions { - url?: string -} - -export class WebWalletConnector extends Connector { - private _wallet: StarknetWindowObject | null = null - private _options: WebWalletConnectorOptions - - constructor(options: WebWalletConnectorOptions = {}) { - super() - this._options = options - } - - available(): boolean { - return true - } - - async ready(): Promise { - if (!_wallet) { - this._wallet = null - return false - } - - this._wallet = _wallet - try { - const permissions = await this._wallet.request({ - type: "wallet_getPermissions", - }) - - return (permissions as Permission[]).includes(Permission.ACCOUNTS) - } catch { - return false - } - } - - get id(): string { - this._wallet = _wallet - return this._wallet?.id || "argentWebWallet" - } - - get name(): string { - this._wallet = _wallet - return this._wallet?.name || "Argent Web Wallet" - } - - get icon(): ConnectorIcons { - return { - light: DEFAULT_WEBWALLET_ICON, - dark: DEFAULT_WEBWALLET_ICON, - } - } - - get wallet(): StarknetWindowObject { - if (!this._wallet) { - throw new ConnectorNotConnectedError() - } - return this._wallet - } - - get title(): string { - return "Email" - } - - get subtitle(): string { - return "Powered by Argent" - } - - async connect(): Promise { - await this.ensureWallet() - - if (!this._wallet) { - throw new ConnectorNotFoundError() - } - - let accounts: string[] - - try { - accounts = await this._wallet.request({ - type: "wallet_requestAccounts", - params: { silent_mode: false }, // explicit to show the modal - }) - } catch { - throw new UserRejectedRequestError() - } - - // Prevent trpc from throwing an error (closed prematurely) - // this happens when 2 requests to webwallet are made in a row (trpc-browser is closing the first popup and requesting a new one right after) - // won't be needed with chrome iframes will be enabled again (but still needed for other browsers) - await new Promise((r) => setTimeout(r, 200)) - const chainId = await this.chainId() - - return { - account: accounts[0], - chainId, - } - } - - async request( - call: RequestFnCall, - ): Promise { - if (!this._wallet) { - throw new ConnectorNotConnectedError() - } - try { - return await this._wallet.request(call) - } catch (e) { - console.error(e) - throw new UserRejectedRequestError() - } - } - - async disconnect(): Promise { - if (!this.available() && !this._wallet) { - throw new ConnectorNotFoundError() - } - - _wallet = null - this._wallet = _wallet - removeStarknetLastConnectedWallet() - } - - async account( - provider: ProviderOptions | ProviderInterface, - ): Promise { - this._wallet = _wallet - - if (!this._wallet) { - throw new ConnectorNotConnectedError() - } - - return new WalletAccount(provider, this._wallet) - } - - async chainId(): Promise { - if (!this._wallet) { - throw new ConnectorNotConnectedError() - } - - const chainId = await this._wallet.request({ - type: "wallet_requestChainId", - }) - - const hexChainId = getStarknetChainId(chainId) - return BigInt(hexChainId) - } - - async initEventListener(accountChangeCb: AccountChangeEventHandler) { - this._wallet = _wallet - if (!this._wallet) { - throw new ConnectorNotConnectedError() - } - - this._wallet.on("accountsChanged", accountChangeCb) - } - - async removeEventListener(accountChangeCb: AccountChangeEventHandler) { - this._wallet = _wallet - if (!this._wallet) { - throw new ConnectorNotConnectedError() - } - - this._wallet.off("accountsChanged", accountChangeCb) - - _wallet = null - this._wallet = null - } - - private async ensureWallet(): Promise { - const origin = this._options.url || DEFAULT_WEBWALLET_URL - setPopupOptions({ - origin, - location: "/interstitialLogin", - }) - - _wallet = (await openWebwallet(origin)) ?? null - - this._wallet = _wallet - } -}