From c0d2931ca157cfd782d54d3afe68bedbab4ed5be Mon Sep 17 00:00:00 2001 From: Sam <60031762+kws1207@users.noreply.github.com> Date: Tue, 30 Sep 2025 22:42:01 +0900 Subject: [PATCH] feat: Add Keplr mobile connector (#1) * feat: Add Keplr mobile connector * feat: Add keplr mobile to package.json exports and vite config * fix: Use user agent for isInKeplrMobileAppBrowser * fix: Add _wallet to KeplrMobileBaseConnector * feat: Add KeplrMobileBaseConnector to defaultConnectors --- package.json | 5 + src/connectors/keplrMobile/constants.ts | 5 + .../keplrMobile/helpers/inAppBrowser.ts | 14 ++ src/connectors/keplrMobile/helpers/index.ts | 1 + src/connectors/keplrMobile/index.ts | 124 ++++++++++++++++++ src/helpers/defaultConnectors.ts | 2 + vite.config.ts | 1 + 7 files changed, 152 insertions(+) create mode 100644 src/connectors/keplrMobile/constants.ts create mode 100644 src/connectors/keplrMobile/helpers/inAppBrowser.ts create mode 100644 src/connectors/keplrMobile/helpers/index.ts create mode 100644 src/connectors/keplrMobile/index.ts diff --git a/package.json b/package.json index e0555385..223ca80c 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,11 @@ "import": "./dist/keplr.js", "require": "./dist/keplr.cjs" }, + "./keplrMobile": { + "types": "./dist/keplrMobile.d.ts", + "import": "./dist/keplrMobile.js", + "require": "./dist/keplrMobile.cjs" + }, "./fordefi": { "types": "./dist/fordefi.d.ts", "import": "./dist/fordefi.js", diff --git a/src/connectors/keplrMobile/constants.ts b/src/connectors/keplrMobile/constants.ts new file mode 100644 index 00000000..ef85ab91 --- /dev/null +++ b/src/connectors/keplrMobile/constants.ts @@ -0,0 +1,5 @@ +export const KEPLR_MOBILE_APP_ICON = ` + + + +` diff --git a/src/connectors/keplrMobile/helpers/inAppBrowser.ts b/src/connectors/keplrMobile/helpers/inAppBrowser.ts new file mode 100644 index 00000000..856534fe --- /dev/null +++ b/src/connectors/keplrMobile/helpers/inAppBrowser.ts @@ -0,0 +1,14 @@ +export const isInKeplrMobileAppBrowser = (): boolean => { + if (typeof window === "undefined") { + return false + } + + const userAgent = navigator.userAgent + const isKeplrMobileApp = userAgent.includes("KeplrWalletMobile") + + if (!isKeplrMobileApp) { + return false + } + + return isKeplrMobileApp +} diff --git a/src/connectors/keplrMobile/helpers/index.ts b/src/connectors/keplrMobile/helpers/index.ts new file mode 100644 index 00000000..bfe3f220 --- /dev/null +++ b/src/connectors/keplrMobile/helpers/index.ts @@ -0,0 +1 @@ +export * from "./inAppBrowser" diff --git a/src/connectors/keplrMobile/index.ts b/src/connectors/keplrMobile/index.ts new file mode 100644 index 00000000..b96891eb --- /dev/null +++ b/src/connectors/keplrMobile/index.ts @@ -0,0 +1,124 @@ +import { type AccountChangeEventHandler } from "@starknet-io/get-starknet-core" +import type { + RequestFnCall, + RpcMessage, + RpcTypeToMessageMap, + StarknetWindowObject, +} from "@starknet-io/types-js" +import type { + AccountInterface, + ProviderInterface, + ProviderOptions, +} from "starknet" +import { + Connector, + type ConnectArgs, + type ConnectorData, + type ConnectorIcons, +} from "../connector" +import { Keplr } from "../injected/keplr" +import { type InjectedConnectorOptions } from "../injected" +import { KEPLR_MOBILE_APP_ICON } from "./constants" +import { isInKeplrMobileAppBrowser } from "./helpers/inAppBrowser" + +export class KeplrMobileBaseConnector extends Connector { + private _wallet: StarknetWindowObject | null = null + + constructor() { + super() + } + + available(): boolean { + return true + } + + async ready(): Promise { + // return true to be compatible with starknet-react + // will need to be implemented + return true + } + + get id(): string { + return "keplrMobile" + } + + get name(): string { + return "Keplr (mobile)" + } + + get icon(): ConnectorIcons { + return { + dark: KEPLR_MOBILE_APP_ICON, + light: KEPLR_MOBILE_APP_ICON, + } + } + + get wallet(): StarknetWindowObject { + throw new Error("not implemented") + } + + async connect(_args: ConnectArgs = {}): Promise { + await this.ensureWallet() + + // will return empty data, connect will only open keplr mobile app + // will require to implement the wallet connection + return { + account: "", + chainId: BigInt(0), + } + } + + async disconnect(): Promise { + throw new Error("not implemented") + } + + async account( + _: ProviderOptions | ProviderInterface, + ): Promise { + throw new Error("not implemented") + } + + async chainId(): Promise { + throw new Error("not implemented") + } + + async request( + call: RequestFnCall, + ): Promise { + throw new Error("not implemented") + } + + // needed, methods required by starknet-react. Otherwise an exception is throwd + async initEventListener(_: AccountChangeEventHandler) { + throw new Error("not implemented") + } + + // needed, methods required by starknet-react. Otherwise an exception is throwd + async removeEventListener(_: AccountChangeEventHandler) { + throw new Error("not implemented") + } + + private async ensureWallet(): Promise { + window.open( + `https://deeplink.keplr.app/web-browser?url=${encodeURIComponent(window.origin)}`, + "_blank", + ) + } +} + +export interface KeplrMobileConnectorInitParams { + inAppBrowserOptions?: Omit +} + +export class KeplrMobileConnector { + static init(params?: KeplrMobileConnectorInitParams): Connector { + const { inAppBrowserOptions } = params || {} + if (isInKeplrMobileAppBrowser()) { + return new Keplr(inAppBrowserOptions) + } else { + return new KeplrMobileBaseConnector() + } + } +} + +export { isInKeplrMobileAppBrowser } diff --git a/src/helpers/defaultConnectors.ts b/src/helpers/defaultConnectors.ts index dee8e3e6..016dae81 100644 --- a/src/helpers/defaultConnectors.ts +++ b/src/helpers/defaultConnectors.ts @@ -1,6 +1,7 @@ import type { StarknetkitConnector } from "../connectors" import { type ArgentMobileConnectorOptions } from "../connectors/argent/argentMobile" import { BraavosMobileBaseConnector } from "../connectors/braavosMobile" +import { KeplrMobileBaseConnector } from "../connectors/keplrMobile" import { ControllerConnector } from "../connectors/controller" import { WebWalletConnector } from "../connectors/webwallet" import { Braavos } from "../connectors/injected/braavos" @@ -43,6 +44,7 @@ export const defaultConnectors = ({ if (isMobileDevice()) { defaultConnectors.push(new BraavosMobileBaseConnector()) + defaultConnectors.push(new KeplrMobileBaseConnector()) } return defaultConnectors diff --git a/vite.config.ts b/vite.config.ts index e16c4300..41e74fb8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -27,6 +27,7 @@ export default defineConfig({ __dirname, "src/connectors/webwallet/index.ts", ), + keplrMobile: resolve(__dirname, "src/connectors/keplrMobile/index.ts"), argentMobile: resolve( __dirname, "src/connectors/argent/argentMobile/index.ts",