From 79c9e130fdc6e1f7de1486894a5ac3e6d6776fa4 Mon Sep 17 00:00:00 2001 From: Mako Shan Date: Thu, 21 Mar 2024 02:54:18 +0800 Subject: [PATCH] [react, wallets] Add imToken Wallet (#2497) Signed-off-by: Mako Shan Signed-off-by: Jonas Daniels Co-authored-by: xwartz Co-authored-by: Jonas Daniels --- .changeset/tall-moose-add-imToken.md | 7 + .../react-core/src/core/hooks/wallet-hooks.ts | 4 +- packages/react/src/evm/index.ts | 1 + packages/react/src/evm/locales/en.ts | 1 + packages/react/src/evm/locales/es.ts | 1 + packages/react/src/evm/locales/ja.ts | 1 + packages/react/src/evm/locales/tl.ts | 1 + packages/react/src/evm/locales/types.ts | 3 +- packages/react/src/index.ts | 5 + .../src/wallet/hooks/useInstalledWallets.ts | 3 + .../wallet/wallets/imtoken/imTokenWallet.tsx | 73 +++++++++ .../evm/connectors/imtoken/package.json | 7 + .../wallets/evm/wallets/imtoken/package.json | 7 + packages/wallets/package.json | 14 ++ .../src/evm/connectors/imtoken/index.ts | 48 ++++++ .../src/evm/connectors/injected/types.ts | 2 + .../wallets/src/evm/constants/walletIds.ts | 1 + packages/wallets/src/evm/index.ts | 1 + packages/wallets/src/evm/wallets/imtoken.ts | 139 ++++++++++++++++++ 19 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 .changeset/tall-moose-add-imToken.md create mode 100644 packages/react/src/wallet/wallets/imtoken/imTokenWallet.tsx create mode 100644 packages/wallets/evm/connectors/imtoken/package.json create mode 100644 packages/wallets/evm/wallets/imtoken/package.json create mode 100644 packages/wallets/src/evm/connectors/imtoken/index.ts create mode 100644 packages/wallets/src/evm/wallets/imtoken.ts diff --git a/.changeset/tall-moose-add-imToken.md b/.changeset/tall-moose-add-imToken.md new file mode 100644 index 0000000000..cb08364324 --- /dev/null +++ b/.changeset/tall-moose-add-imToken.md @@ -0,0 +1,7 @@ +--- +"@thirdweb-dev/wallets": patch +"@thirdweb-dev/react": patch +"@thirdweb-dev/react-core": patch +--- + +Add imToken Wallet diff --git a/packages/react-core/src/core/hooks/wallet-hooks.ts b/packages/react-core/src/core/hooks/wallet-hooks.ts index c5e6474eba..a4e41579a1 100644 --- a/packages/react-core/src/core/hooks/wallet-hooks.ts +++ b/packages/react-core/src/core/hooks/wallet-hooks.ts @@ -8,6 +8,7 @@ import type { CryptoDefiWallet, EmbeddedWallet, FrameWallet, + ImTokenWallet, LocalWallet, MagicEdenWallet, MagicLink, @@ -41,6 +42,7 @@ export type WalletIdToWalletTypeMap = { rainbowWallet: RainbowWallet; blocto: BloctoWallet; frame: FrameWallet; + imToken: ImTokenWallet; localWallet: LocalWallet; magicLink: MagicLink; paper: PaperWallet; @@ -57,7 +59,7 @@ export type WalletIdToWalletTypeMap = { cryptoDefiWallet: CryptoDefiWallet; rabby: RabbyWallet; xdefi: XDEFIWallet; - magicEden: MagicEdenWallet + magicEden: MagicEdenWallet; }; /** diff --git a/packages/react/src/evm/index.ts b/packages/react/src/evm/index.ts index d14a3678bf..ac6ff6fcc9 100644 --- a/packages/react/src/evm/index.ts +++ b/packages/react/src/evm/index.ts @@ -111,4 +111,5 @@ export { CryptoDefiWallet, RabbyWallet, Coin98Wallet, + ImTokenWallet, } from "@thirdweb-dev/wallets"; diff --git a/packages/react/src/evm/locales/en.ts b/packages/react/src/evm/locales/en.ts index b1f7a1b9b4..734bf2cd0d 100644 --- a/packages/react/src/evm/locales/en.ts +++ b/packages/react/src/evm/locales/en.ts @@ -171,6 +171,7 @@ export function enDefault(): ThirdwebLocale { xdefiWallet: extensionAndQRScanScreens("XDEFI"), rainbowWallet: extensionAndQRScanScreens("Rainbow"), trustWallet: extensionAndQRScanScreens("Trust"), + imTokenWallet: extensionAndQRScanScreens("imToken"), zerionWallet: extensionAndQRScanScreens("Zerion"), coreWallet: extensionAndQRScanScreens("Core"), oneKeyWallet: extensionAndQRScanScreens("OneKey"), diff --git a/packages/react/src/evm/locales/es.ts b/packages/react/src/evm/locales/es.ts index 6c5cb31273..0374c97976 100644 --- a/packages/react/src/evm/locales/es.ts +++ b/packages/react/src/evm/locales/es.ts @@ -176,6 +176,7 @@ export function esDefault(): ThirdwebLocale { xdefiWallet: extensionAndQRScanScreens("XDEFI"), rainbowWallet: extensionAndQRScanScreens("Rainbow"), trustWallet: extensionAndQRScanScreens("Trust"), + imTokenWallet: extensionAndQRScanScreens("imToken"), zerionWallet: extensionAndQRScanScreens("Zerion"), oneKeyWallet: extensionAndQRScanScreens("OneKey"), cryptoDefiWallet: extensionAndQRScanScreens("Crypto Defi"), diff --git a/packages/react/src/evm/locales/ja.ts b/packages/react/src/evm/locales/ja.ts index 7e2ce099e0..7ab17e4aeb 100644 --- a/packages/react/src/evm/locales/ja.ts +++ b/packages/react/src/evm/locales/ja.ts @@ -176,6 +176,7 @@ export function jaDefault(): ThirdwebLocale { xdefiWallet: extensionAndQRScanScreens("XDEFI"), rainbowWallet: extensionAndQRScanScreens("Rainbow"), trustWallet: extensionAndQRScanScreens("Trust"), + imTokenWallet: extensionAndQRScanScreens("imToken"), zerionWallet: extensionAndQRScanScreens("Zerion"), oneKeyWallet: extensionAndQRScanScreens("OneKey"), cryptoDefiWallet: extensionAndQRScanScreens("Crypto Defi"), diff --git a/packages/react/src/evm/locales/tl.ts b/packages/react/src/evm/locales/tl.ts index 93b8d271a1..e43f6ac90d 100644 --- a/packages/react/src/evm/locales/tl.ts +++ b/packages/react/src/evm/locales/tl.ts @@ -176,6 +176,7 @@ export function tlDefault(): ThirdwebLocale { xdefiWallet: extensionAndQRScanScreens("XDEFI"), rainbowWallet: extensionAndQRScanScreens("Rainbow"), trustWallet: extensionAndQRScanScreens("Trust"), + imTokenWallet: extensionAndQRScanScreens("imToken"), zerionWallet: extensionAndQRScanScreens("Zerion"), coreWallet: extensionAndQRScanScreens("Core"), oneKeyWallet: extensionAndQRScanScreens("OneKey"), diff --git a/packages/react/src/evm/locales/types.ts b/packages/react/src/evm/locales/types.ts index ac7dbb142a..e82e686f96 100644 --- a/packages/react/src/evm/locales/types.ts +++ b/packages/react/src/evm/locales/types.ts @@ -183,6 +183,7 @@ export type ThirdwebLocale = { getStartedScreen: { instruction: string }; scanScreen: { instruction: string }; }; + trustWallet: ExtensionAndQRScreensLocale; localWallet: { confirmPasswordLabel: string; createScreen: { @@ -302,7 +303,7 @@ export type ThirdwebLocale = { title: string; }; }; - trustWallet: ExtensionAndQRScreensLocale; + imTokenWallet: ExtensionAndQRScreensLocale; walletConnect: { scanInstruction: string }; zerionWallet: ExtensionAndQRScreensLocale; }; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 1cbdbf9744..c6a83f111a 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -107,6 +107,11 @@ export { type Coin98WalletConfigOptions, } from "./wallet/wallets/coin98/coin98Wallet"; +export { + imTokenWallet, + type ImTokenWalletConfigOptions, +} from "./wallet/wallets/imtoken/imTokenWallet"; + // theming export { darkTheme, lightTheme } from "./design-system/index"; export type { Theme, ThemeOverrides } from "./design-system/index"; diff --git a/packages/react/src/wallet/hooks/useInstalledWallets.ts b/packages/react/src/wallet/hooks/useInstalledWallets.ts index cf61aa4655..e2d1d100bb 100644 --- a/packages/react/src/wallet/hooks/useInstalledWallets.ts +++ b/packages/react/src/wallet/hooks/useInstalledWallets.ts @@ -12,6 +12,7 @@ export function useInstalledWallets() { let isCoinbaseWalletInstalled = false; let isZerionWalletInstalled = false; let isTrustWalletInstalled = false; + let isImTokenInstalled = false; const window_: Window | undefined = globalThis?.window; if (assertWindowEthereum(window_)) { @@ -19,6 +20,7 @@ export function useInstalledWallets() { isCoinbaseWalletInstalled = !!getInjectedCoinbaseProvider(); isZerionWalletInstalled = !!window_.ethereum?.isZerion; isTrustWalletInstalled = !!window_.ethereum?.isTrust; + isImTokenInstalled = !!window_.ethereum?.isToken; } return { @@ -26,5 +28,6 @@ export function useInstalledWallets() { coinbaseWallet: isCoinbaseWalletInstalled, trustWallet: isTrustWalletInstalled, zerionWallet: isZerionWalletInstalled, + imtokenWallet: isImTokenInstalled, }; } diff --git a/packages/react/src/wallet/wallets/imtoken/imTokenWallet.tsx b/packages/react/src/wallet/wallets/imtoken/imTokenWallet.tsx new file mode 100644 index 0000000000..1b25d97a1c --- /dev/null +++ b/packages/react/src/wallet/wallets/imtoken/imTokenWallet.tsx @@ -0,0 +1,73 @@ +import type { + WalletOptions, + WalletConfig, + ConnectUIProps, +} from "@thirdweb-dev/react-core"; +import { ImTokenWallet, assertWindowEthereum } from "@thirdweb-dev/wallets"; +import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { ExtensionOrWCConnectionUI } from "../_common/ExtensionORWCConnectionUI"; + +const imTokenWalletUris = { + ios: "https://itunes.apple.com/us/app/imtoken2/id1384798940", + android: "https://play.google.com/store/apps/details?id=im.token.app", +}; + +export type ImTokenWalletConfigOptions = { + projectId?: string; + recommended?: boolean; +}; + +export const imTokenWallet = ( + options?: ImTokenWalletConfigOptions, +): WalletConfig => { + return { + id: ImTokenWallet.id, + recommended: options?.recommended, + meta: { + ...ImTokenWallet.meta, + iconURL: + "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzIyMl80NzgzKSI+CjxtYXNrIGlkPSJtYXNrMF8yMjJfNDc4MyIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCI+CjxwYXRoIGQ9Ik03OS44OTQ4IDBIMC4wNTA3ODEyVjgwSDc5Ljg5NDhWMFoiIGZpbGw9IndoaXRlIi8+CjwvbWFzaz4KPGcgbWFzaz0idXJsKCNtYXNrMF8yMjJfNDc4MykiPgo8cGF0aCBkPSJNNjIuMDI3NSAwSDE4LjA1MDlDOC4xNDYzOSAwIDAuMTE3MTg4IDguMDQ0ODggMC4xMTcxODggMTcuOTY4OFY2Mi4wMzEyQzAuMTE3MTg4IDcxLjk1NTEgOC4xNDYzOSA4MCAxOC4wNTA5IDgwSDYyLjAyNzVDNzEuOTMyIDgwIDc5Ljk2MTIgNzEuOTU1MSA3OS45NjEyIDYyLjAzMTJWMTcuOTY4OEM3OS45NjEyIDguMDQ0ODggNzEuOTMyIDAgNjIuMDI3NSAwWiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzIyMl80NzgzKSIvPgo8cGF0aCBkPSJNNjUuMDk4MSAyNC43MzNDNjYuNzU4NiA0Ny4yNjY3IDUyLjMwMjEgNTcuOTE3MiAzOS4zNDIzIDU5LjA1M0MyNy4yOTM1IDYwLjEwODcgMTUuOTUyIDUyLjY5MDggMTQuOTU3MSA0MS4yOTM2QzE0LjEzNjMgMzEuODc3NiAxOS45NDQ1IDI3Ljg2ODkgMjQuNTA4IDI3LjQ2OTRDMjkuMjAxNSAyNy4wNTcgMzMuMTQ1OCAzMC4zMDAxIDMzLjQ4NzkgMzQuMjI2OUMzMy44MTc1IDM4LjAwMiAzMS40NjY0IDM5LjcyMDUgMjkuODMxMyAzOS44NjM0QzI4LjUzODIgMzkuOTc3IDI2LjkxMTQgMzkuMTkwNSAyNi43NjQ1IDM3LjUwMTZDMjYuNjM4NSAzNi4wNTAzIDI3LjE4ODUgMzUuODUyNiAyNy4wNTQxIDM0LjMxMDlDMjYuODE0OSAzMS41NjYyIDI0LjQyNjEgMzEuMjQ2NiAyMy4xMTgzIDMxLjM2MDFDMjEuNTM1NyAzMS40OTkxIDE4LjY2NDEgMzMuMzQ5OCAxOS4wNjcgMzcuOTZDMTkuNDcyMiA0Mi42MTAxIDIzLjkyMjIgNDYuMjg0NSAyOS43NTU3IDQ1Ljc3MzRDMzYuMDUwOSA0NS4yMjIzIDQwLjQzMzcgNDAuMzExNCA0MC43NjM0IDMzLjQyMzRDNDAuNzYwMyAzMy4wNTg2IDQwLjgzNyAzMi42OTc1IDQwLjk4OCAzMi4zNjU1TDQwLjk5IDMyLjM1NzJDNDEuMDU3OCAzMi4yMTI4IDQxLjEzNzIgMzIuMDc0MiA0MS4yMjcyIDMxLjk0MjhDNDEuMzYxNiAzMS43NDA5IDQxLjUzMzggMzEuNTE4IDQxLjc1NjIgMzEuMjczOUM0MS43NTgzIDMxLjI2NzYgNDEuNzU4MyAzMS4yNjc2IDQxLjc2MjYgMzEuMjY3NkM0MS45MjQxIDMxLjA4NDcgNDIuMTE5NCAzMC44ODcgNDIuMzM5NyAzMC42NzQ1QzQ1LjA4OTcgMjguMDc1IDU0Ljk5MzEgMjEuOTQ0MiA2NC4zNTkyIDIzLjg4NTVDNjQuNTU3MyAyMy45MjggNjQuNzM2MSAyNC4wMzM0IDY0Ljg2OTMgMjQuMTg2MkM2NS4wMDI1IDI0LjMzOTEgNjUuMDgyNiAyNC41MzA4IDY1LjA5ODEgMjQuNzMzWiIgZmlsbD0id2hpdGUiLz4KPC9nPgo8L2c+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMjIyXzQ3ODMiIHgxPSI3My41MDA5IiB5MT0iNS4zMTI1IiB4Mj0iMi44NzI0MSIgeTI9Ijc3LjY3NDciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzExQzREMSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMwMDYyQUQiLz4KPC9saW5lYXJHcmFkaWVudD4KPGNsaXBQYXRoIGlkPSJjbGlwMF8yMjJfNDc4MyI+CjxyZWN0IHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4=", + }, + create: (walletOptions: WalletOptions) => { + const wallet = new ImTokenWallet({ + ...walletOptions, + projectId: options?.projectId, + qrcode: false, + }); + + return wallet; + }, + connectUI: ConnectUI, + isInstalled: isInstalled, + }; +}; + +function isInstalled() { + if (assertWindowEthereum(globalThis.window)) { + return !!globalThis.window.ethereum.isImToken; + } + return false; +} + +function ConnectUI(props: ConnectUIProps) { + const locale = useTWLocale(); + return ( + props.setConnectedWallet(w as ImTokenWallet)} + setConnectionStatus={props.setConnectionStatus} + supportedWallets={props.supportedWallets} + walletConnectUris={{ + ios: imTokenWalletUris.ios, + android: imTokenWalletUris.android, + other: imTokenWalletUris.android, + }} + walletLocale={locale.wallets.imTokenWallet} + isInstalled={isInstalled} + /> + ); +} diff --git a/packages/wallets/evm/connectors/imtoken/package.json b/packages/wallets/evm/connectors/imtoken/package.json new file mode 100644 index 0000000000..e5a79293bd --- /dev/null +++ b/packages/wallets/evm/connectors/imtoken/package.json @@ -0,0 +1,7 @@ +{ + "main": "dist/thirdweb-dev-wallets-evm-connectors-imtoken.cjs.js", + "module": "dist/thirdweb-dev-wallets-evm-connectors-imtoken.esm.js", + "browser": { + "./dist/thirdweb-dev-wallets-evm-connectors-imtoken.esm.js": "./dist/thirdweb-dev-wallets-evm-connectors-imtoken.browser.esm.js" + } +} diff --git a/packages/wallets/evm/wallets/imtoken/package.json b/packages/wallets/evm/wallets/imtoken/package.json new file mode 100644 index 0000000000..1c82b73d98 --- /dev/null +++ b/packages/wallets/evm/wallets/imtoken/package.json @@ -0,0 +1,7 @@ +{ + "main": "dist/thirdweb-dev-wallets-evm-wallets-imtoken.cjs.js", + "module": "dist/thirdweb-dev-wallets-evm-wallets-imtoken.esm.js", + "browser": { + "./dist/thirdweb-dev-wallets-evm-wallets-imtoken.esm.js": "./dist/thirdweb-dev-wallets-evm-wallets-imtoken.browser.esm.js" + } +} diff --git a/packages/wallets/package.json b/packages/wallets/package.json index 9888a4d6dc..225d9dbd2a 100644 --- a/packages/wallets/package.json +++ b/packages/wallets/package.json @@ -147,6 +147,13 @@ }, "default": "./evm/wallets/gcp-kms/dist/thirdweb-dev-wallets-evm-wallets-gcp-kms.cjs.js" }, + "./evm/wallets/imtoken": { + "module": { + "browser": "./evm/wallets/imtoken/dist/thirdweb-dev-wallets-evm-wallets-imtoken.browser.esm.js", + "default": "./evm/wallets/imtoken/dist/thirdweb-dev-wallets-evm-wallets-imtoken.esm.js" + }, + "default": "./evm/wallets/imtoken/dist/thirdweb-dev-wallets-evm-wallets-imtoken.cjs.js" + }, "./evm/wallets/phantom": { "module": { "browser": "./evm/wallets/phantom/dist/thirdweb-dev-wallets-evm-wallets-phantom.browser.esm.js", @@ -336,6 +343,13 @@ }, "default": "./evm/wallets/embedded-wallet/dist/thirdweb-dev-wallets-evm-wallets-embedded-wallet.cjs.js" }, + "./evm/connectors/imtoken": { + "module": { + "browser": "./evm/connectors/imtoken/dist/thirdweb-dev-wallets-evm-connectors-imtoken.browser.esm.js", + "default": "./evm/connectors/imtoken/dist/thirdweb-dev-wallets-evm-connectors-imtoken.esm.js" + }, + "default": "./evm/connectors/imtoken/dist/thirdweb-dev-wallets-evm-connectors-imtoken.cjs.js" + }, "./evm/connectors/phantom": { "module": { "browser": "./evm/connectors/phantom/dist/thirdweb-dev-wallets-evm-connectors-phantom.browser.esm.js", diff --git a/packages/wallets/src/evm/connectors/imtoken/index.ts b/packages/wallets/src/evm/connectors/imtoken/index.ts new file mode 100644 index 0000000000..3928f558be --- /dev/null +++ b/packages/wallets/src/evm/connectors/imtoken/index.ts @@ -0,0 +1,48 @@ +import { InjectedConnector, InjectedConnectorOptions } from "../injected"; +import { assertWindowEthereum } from "../../utils/assertWindowEthereum"; +import { Ethereum } from "../injected/types"; +import type { Chain } from "@thirdweb-dev/chains"; +import { AsyncStorage } from "../../../core/AsyncStorage"; + +type ImTokenConnectorConstructorArg = { + chains?: Chain[]; + connectorStorage: AsyncStorage; + options?: InjectedConnectorOptions; +}; + +export class ImTokenConnector extends InjectedConnector { + constructor(arg: ImTokenConnectorConstructorArg) { + const defaultOptions = { + name: "imToken", + getProvider() { + function getReady(ethereum?: Ethereum) { + const isToken = !!ethereum?.isToken; + if (!isToken) { + return; + } + return ethereum; + } + + if (typeof window === "undefined") { + return; + } + if (assertWindowEthereum(globalThis.window)) { + if (globalThis.window.ethereum?.providers) { + return globalThis.window.ethereum.providers.find(getReady); + } + + return getReady(globalThis.window.ethereum); + } + }, + }; + const options = { + ...defaultOptions, + ...arg.options, + }; + super({ + chains: arg.chains, + options, + connectorStorage: arg.connectorStorage, + }); + } +} diff --git a/packages/wallets/src/evm/connectors/injected/types.ts b/packages/wallets/src/evm/connectors/injected/types.ts index c48b83bf0f..7e85ac2f0d 100644 --- a/packages/wallets/src/evm/connectors/injected/types.ts +++ b/packages/wallets/src/evm/connectors/injected/types.ts @@ -56,6 +56,8 @@ type InjectedProviderFlags = { isDefiWallet?: true; isRabbyWallet?: true; isCoin98Wallet?: true; + // imToken + isToken?: true; }; type InjectedProviders = InjectedProviderFlags & { isMetaMask: true; diff --git a/packages/wallets/src/evm/constants/walletIds.ts b/packages/wallets/src/evm/constants/walletIds.ts index 800ef304d5..b1811c6672 100644 --- a/packages/wallets/src/evm/constants/walletIds.ts +++ b/packages/wallets/src/evm/constants/walletIds.ts @@ -5,6 +5,7 @@ export const walletIds = { coreWallet: "coreWallet", cryptoDefiWallet: "cryptoDefiWallet", frame: "frame", + imtoken: "imToken", localWallet: "localWallet", magicLink: "magicLink", metamask: "metamask", diff --git a/packages/wallets/src/evm/index.ts b/packages/wallets/src/evm/index.ts index 9136b810b1..1b1cab7cca 100644 --- a/packages/wallets/src/evm/index.ts +++ b/packages/wallets/src/evm/index.ts @@ -32,6 +32,7 @@ export * from "./wallets/coinbase-wallet"; export * from "./wallets/embedded-wallet"; export * from "./wallets/ethers"; export * from "./wallets/frame"; +export * from "./wallets/imtoken"; export * from "./wallets/injected"; export * from "./wallets/local-wallet"; export * from "./wallets/signer"; diff --git a/packages/wallets/src/evm/wallets/imtoken.ts b/packages/wallets/src/evm/wallets/imtoken.ts new file mode 100644 index 0000000000..3da6b32b0e --- /dev/null +++ b/packages/wallets/src/evm/wallets/imtoken.ts @@ -0,0 +1,139 @@ +import type { WalletConnectConnector as WalletConnectConnectorType } from "../connectors/wallet-connect"; +import type { QRModalOptions } from "../connectors/wallet-connect/qrModalOptions"; +import { Connector, WagmiAdapter } from "../interfaces/connector"; +import { assertWindowEthereum } from "../utils/assertWindowEthereum"; +import { AbstractClientWallet, WalletOptions } from "./base"; +import type { ImTokenConnector as ImTokenConnectorType } from "../connectors/imtoken"; +import { walletIds } from "../constants/walletIds"; +import { TW_WC_PROJECT_ID } from "../constants/wc"; + +type ImTokenAdditionalOptions = { + qrcode?: boolean; + projectId?: string; + qrModalOptions?: QRModalOptions; +}; + +export type ImTokenWalletOptions = WalletOptions; + +type ConnectWithQrCodeArgs = { + chainId?: number; + onQrCodeUri: (uri: string) => void; + onConnected: (accountAddress: string) => void; +}; + +export class ImTokenWallet extends AbstractClientWallet { + connector?: Connector; + walletConnectConnector?: WalletConnectConnectorType; + imtokenConnector?: ImTokenConnectorType; + isInjected: boolean; + static id = walletIds.imtoken as string; + + public get walletName() { + return "imToken Wallet" as const; + } + + constructor(options: ImTokenWalletOptions) { + super(ImTokenWallet.id, options); + + if (assertWindowEthereum(globalThis.window)) { + this.isInjected = !!globalThis.window.ethereum?.isImToken; + } else { + this.isInjected = false; + } + } + + protected async getConnector(): Promise { + if (!this.connector) { + if (this.isInjected) { + // import the connector dynamically + const { ImTokenConnector } = await import("../connectors/imtoken"); + const imtokenConnector = new ImTokenConnector({ + chains: this.chains, + connectorStorage: this.walletStorage, + options: { + shimDisconnect: true, + }, + }); + + this.imtokenConnector = imtokenConnector; + + this.connector = new WagmiAdapter(imtokenConnector); + } else { + const { WalletConnectConnector } = await import( + "../connectors/wallet-connect" + ); + + const walletConnectConnector = new WalletConnectConnector({ + chains: this.chains, + options: { + projectId: this.options?.projectId || TW_WC_PROJECT_ID, // TODO, + storage: this.walletStorage, + qrcode: this.options?.qrcode, + dappMetadata: this.dappMetadata, + qrModalOptions: this.options?.qrModalOptions, + }, + }); + + walletConnectConnector.getProvider().then((provider) => { + provider.signer.client.on("session_request_sent", () => { + this.emit("wc_session_request_sent"); + }); + }); + + // need to save this for getting the QR code URI + this.walletConnectConnector = walletConnectConnector; + this.connector = new WagmiAdapter(walletConnectConnector); + } + } + + return this.connector; + } + + /** + * Connect to the wallet using a QR code. + * You can use this method to display a QR code. The user can scan this QR code using the Trust Wallet mobile app to connect to your dapp. + * + * @example + * ```typescript + * wallet.connectWithQrCode({ + * chainId: 1, + * onQrCodeUri(qrCodeUri) { + * // render the QR code with `qrCodeUri` + * }, + * onConnected(accountAddress) { + * // update UI to show connected state + * }, + * }) + * ``` + * + * @param options - + * The options object contains the following properties/method: + * + * ### chainId (optional) + * If provided, wallet will prompt the user to switch to the network with the given `chainId` after connecting. + * + * ### onQrCodeUri + * A callback to get the QR code URI to display to the user. + * + * ### onConnected + * A callback that is called when the user has connected their wallet using the QR code. + */ + async connectWithQrCode(options: ConnectWithQrCodeArgs) { + await this.getConnector(); + const wcConnector = this.walletConnectConnector; + + if (!wcConnector) { + throw new Error("WalletConnect connector not found"); + } + + const wcProvider = await wcConnector.getProvider(); + + // set a listener for display_uri event + wcProvider.on("display_uri", (uri) => { + options.onQrCodeUri(uri); + }); + + // trigger connect flow + this.connect({ chainId: options.chainId }).then(options.onConnected); + } +}