diff --git a/.changeset/pretty-papayas-occur.md b/.changeset/pretty-papayas-occur.md new file mode 100644 index 00000000000..d295dd02a40 --- /dev/null +++ b/.changeset/pretty-papayas-occur.md @@ -0,0 +1,6 @@ +--- +"@thirdweb-dev/wallets": patch +"@thirdweb-dev/react": patch +--- + +Add OKX wallet diff --git a/packages/react-core/src/core/hooks/wallet-hooks.ts b/packages/react-core/src/core/hooks/wallet-hooks.ts index a783e39cc08..c8c95ee2ac8 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 { LocalWallet, MagicLink, MetaMaskWallet, + OKXWallet, PaperWallet, PhantomWallet, RainbowWallet, @@ -37,6 +38,7 @@ type WalletIdToWalletTypeMap = { walletConnect: WalletConnect; phantom: PhantomWallet; walletConnectV1: WalletConnect; + okx: OKXWallet; }; /** diff --git a/packages/react/src/evm/index.ts b/packages/react/src/evm/index.ts index dc378c26847..72f416d977c 100644 --- a/packages/react/src/evm/index.ts +++ b/packages/react/src/evm/index.ts @@ -56,6 +56,7 @@ export { PhantomWallet, RainbowWallet, MetaMaskWallet, + OKXWallet, TrustWallet, CoinbaseWallet, BloctoWallet, diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index dd4f324a643..c3e357afe08 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -18,6 +18,7 @@ export { trustWallet } from "./wallet/wallets/trustWallet/TrustWallet"; export { walletConnect } from "./wallet/wallets/walletConnect/walletConnect"; export { walletConnectV1 } from "./wallet/wallets/walletConnectV1"; export { zerionWallet } from "./wallet/wallets/zerion/zerionWallet"; +export { okxWallet } from "./wallet/wallets/okx/okxWallet"; export { darkTheme, lightTheme } from "./design-system/index"; export type { Theme, ThemeOverrides } from "./design-system/index"; diff --git a/packages/react/src/wallet/wallets/okx/OKXConnectUI.tsx b/packages/react/src/wallet/wallets/okx/OKXConnectUI.tsx new file mode 100644 index 00000000000..e57c4f67b75 --- /dev/null +++ b/packages/react/src/wallet/wallets/okx/OKXConnectUI.tsx @@ -0,0 +1,109 @@ +import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; +import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; +import { isMobile } from "../../../evm/utils/isMobile"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { OKXScan } from "./OKXScan"; +import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; +import type { OKXWallet } from "@thirdweb-dev/wallets"; +import { wait } from "../../../utils/wait"; + +export const OKXConnectUI = (props: ConnectUIProps) => { + const [screen, setScreen] = useState< + "connecting" | "scanning" | "get-started" + >("connecting"); + const { walletConfig, connected } = props; + const connect = useConnect(); + const [errorConnecting, setErrorConnecting] = useState(false); + + const hideBackButton = props.supportedWallets.length === 1; + + const connectToExtension = useCallback(async () => { + try { + connectPrompted.current = true; + setErrorConnecting(false); + setScreen("connecting"); + await wait(1000); + await connect(walletConfig); + connected(); + } catch (e) { + setErrorConnecting(true); + console.error(e); + } + }, [connected, connect, walletConfig]); + + const connectPrompted = useRef(false); + useEffect(() => { + if (connectPrompted.current) { + return; + } + + const isInstalled = walletConfig.isInstalled + ? walletConfig.isInstalled() + : false; + + // if loading + (async () => { + if (isInstalled) { + connectToExtension(); + } + + // if wallet is not injected + else { + // on mobile, deep link to the okx app + if (isMobile()) { + window.open( + `okx://wallet/dapp/details?dappUrl=${window.location.toString()}`, + ); + } else { + // on desktop, show the OKX scan qr code + setScreen("scanning"); + } + } + })(); + }, [connectToExtension, walletConfig]); + + if (screen === "connecting") { + return ( + { + setScreen("get-started"); + }} + onRetry={connectToExtension} + hideBackButton={hideBackButton} + onBack={props.goBack} + walletName={walletConfig.meta.name} + walletIconURL={walletConfig.meta.iconURL} + /> + ); + } + + if (screen === "get-started") { + return ( + + ); + } + + if (screen === "scanning") { + return ( + { + setScreen("get-started"); + }} + hideBackButton={hideBackButton} + walletConfig={walletConfig} + /> + ); + } + + return null; +}; diff --git a/packages/react/src/wallet/wallets/okx/OKXScan.tsx b/packages/react/src/wallet/wallets/okx/OKXScan.tsx new file mode 100644 index 00000000000..1850a6db945 --- /dev/null +++ b/packages/react/src/wallet/wallets/okx/OKXScan.tsx @@ -0,0 +1,61 @@ +import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; +import { + useCreateWalletInstance, + useWalletContext, +} from "@thirdweb-dev/react-core"; +import { useEffect, useRef, useState } from "react"; +import type { OKXWallet } from "@thirdweb-dev/wallets"; +import type { WalletConfig } from "@thirdweb-dev/react-core"; + +export const OKXScan: React.FC<{ + onBack: () => void; + onGetStarted: () => void; + onConnected: () => void; + walletConfig: WalletConfig; + hideBackButton: boolean; +}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { + const createInstance = useCreateWalletInstance(); + const [qrCodeUri, setQrCodeUri] = useState(); + const { setConnectedWallet, chainToConnect, setConnectionStatus } = + useWalletContext(); + + const scanStarted = useRef(false); + useEffect(() => { + if (scanStarted.current) { + return; + } + scanStarted.current = true; + + const wallet = createInstance(walletConfig); + + setConnectionStatus("connecting"); + wallet.connectWithQrCode({ + chainId: chainToConnect?.chainId, + onQrCodeUri(uri) { + setQrCodeUri(uri); + }, + onConnected() { + setConnectedWallet(wallet); + onConnected(); + }, + }); + }, [ + createInstance, + setConnectedWallet, + chainToConnect, + onConnected, + walletConfig, + setConnectionStatus, + ]); + + return ( + + ); +}; diff --git a/packages/react/src/wallet/wallets/okx/okxWallet.tsx b/packages/react/src/wallet/wallets/okx/okxWallet.tsx new file mode 100644 index 00000000000..6e9ddd3ddf2 --- /dev/null +++ b/packages/react/src/wallet/wallets/okx/okxWallet.tsx @@ -0,0 +1,52 @@ +import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core"; +import { OKXWallet, getInjectedOKXProvider } from "@thirdweb-dev/wallets"; +import { OKXConnectUI } from "./OKXConnectUI"; + +type OKXWalletOptions = { + /** + * When connecting OKX using the QR Code - Wallet Connect connector is used which requires a project id. + * This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com. + * + * https://docs.walletconnect.com/2.0/web3modal/options#projectid-required + */ + projectId?: string; + + /** + * If true, the wallet will be tagged as "reccomended" in ConnectWallet Modal + */ + recommended?: boolean; +}; + +export const okxWallet = ( + options?: OKXWalletOptions, +): WalletConfig => { + return { + id: OKXWallet.id, + recommended: options?.recommended, + meta: { + name: "OKX Wallet", + urls: { + chrome: + "https://chrome.google.com/webstore/detail/okx-wallet/mcohilncbfahbmgdjkbpemcciiolgcge", + android: + "https://play.google.com/store/apps/details?id=com.okinc.okex.gp&pli=1", + ios: "https://apps.apple.com/us/app/okx-buy-bitcoin-eth-crypto/id1327268470", + }, + iconURL: + "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMTIiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGQ9Ik00Ni4wMjg0IDMyLjk2NjhIMzMuMDE5OUMzMi40Njc3IDMyLjk2NjggMzIuMDE1IDMzLjQxOTUgMzIuMDE1IDMzLjk3MTZWNDYuOTgwMUMzMi4wMTUgNDcuNTMyMyAzMi40Njc3IDQ3Ljk4NSAzMy4wMTk5IDQ3Ljk4NUg0Ni4wMjg0QzQ2LjU4MDUgNDcuOTg1IDQ3LjAzMzIgNDcuNTMyMyA0Ny4wMzMyIDQ2Ljk4MDFWMzMuOTcxNkM0Ny4wMzMyIDMzLjQxOTUgNDYuNTgwNSAzMi45NjY4IDQ2LjAyODQgMzIuOTY2OFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0zMS4wMTMzIDE4SDE4LjAwNDlDMTcuNDUyNyAxOCAxNyAxOC40NTI3IDE3IDE5LjAwNDlWMzIuMDEzM0MxNyAzMi41NjU1IDE3LjQ1MjcgMzMuMDE4MiAxOC4wMDQ5IDMzLjAxODJIMzEuMDEzM0MzMS41NjU1IDMzLjAxODIgMzIuMDE4MiAzMi41NjU1IDMyLjAxODIgMzIuMDEzM1YxOS4wMDQ5QzMyLjAxNSAxOC40NTI3IDMxLjU2NTUgMTggMzEuMDEzMyAxOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik02MC45OTUyIDE4SDQ3Ljk4NjdDNDcuNDM0NSAxOCA0Ni45ODE4IDE4LjQ1MjcgNDYuOTgxOCAxOS4wMDQ5VjMyLjAxMzNDNDYuOTgxOCAzMi41NjU1IDQ3LjQzNDUgMzMuMDE4MiA0Ny45ODY3IDMzLjAxODJINjAuOTk1MkM2MS41NDczIDMzLjAxODIgNjIgMzIuNTY1NSA2MiAzMi4wMTMzVjE5LjAwNDlDNjIgMTguNDUyNyA2MS41NDczIDE4IDYwLjk5NTIgMThaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzEuMDEzMyA0Ny45MzM3SDE4LjAwNDlDMTcuNDUyNyA0Ny45MzM3IDE3IDQ4LjM4NjQgMTcgNDguOTM4NlY2MS45NDcxQzE3IDYyLjQ5OTIgMTcuNDUyNyA2Mi45NTE5IDE4LjAwNDkgNjIuOTUxOUgzMS4wMTMzQzMxLjU2NTUgNjIuOTUxOSAzMi4wMTgyIDYyLjQ5OTIgMzIuMDE4MiA2MS45NDcxVjQ4LjkzODZDMzIuMDE1IDQ4LjM4NjQgMzEuNTY1NSA0Ny45MzM3IDMxLjAxMzMgNDcuOTMzN1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik02MC45OTUyIDQ3LjkzMzdINDcuOTg2N0M0Ny40MzQ1IDQ3LjkzMzcgNDYuOTgxOCA0OC4zODY0IDQ2Ljk4MTggNDguOTM4NlY2MS45NDcxQzQ2Ljk4MTggNjIuNDk5MiA0Ny40MzQ1IDYyLjk1MTkgNDcuOTg2NyA2Mi45NTE5SDYwLjk5NTJDNjEuNTQ3MyA2Mi45NTE5IDYyIDYyLjQ5OTIgNjIgNjEuOTQ3MVY0OC45Mzg2QzYyIDQ4LjM4NjQgNjEuNTQ3MyA0Ny45MzM3IDYwLjk5NTIgNDcuOTMzN1oiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=", + }, + create: (walletOptions: WalletOptions) => { + const wallet = new OKXWallet({ + ...walletOptions, + projectId: options?.projectId, + qrcode: false, + }); + + return wallet; + }, + connectUI: OKXConnectUI, + isInstalled() { + return !!getInjectedOKXProvider(); + }, + }; +}; diff --git a/packages/wallets/evm/connectors/okx/package.json b/packages/wallets/evm/connectors/okx/package.json new file mode 100644 index 00000000000..80b338cdb1d --- /dev/null +++ b/packages/wallets/evm/connectors/okx/package.json @@ -0,0 +1,7 @@ +{ + "main": "dist/thirdweb-dev-wallets-evm-connectors-okx.cjs.js", + "module": "dist/thirdweb-dev-wallets-evm-connectors-okx.esm.js", + "browser": { + "./dist/thirdweb-dev-wallets-evm-connectors-okx.esm.js": "./dist/thirdweb-dev-wallets-evm-connectors-okx.browser.esm.js" + } +} diff --git a/packages/wallets/evm/wallets/okx/package.json b/packages/wallets/evm/wallets/okx/package.json new file mode 100644 index 00000000000..b04b172b654 --- /dev/null +++ b/packages/wallets/evm/wallets/okx/package.json @@ -0,0 +1,7 @@ +{ + "main": "dist/thirdweb-dev-wallets-evm-wallets-okx.cjs.js", + "module": "dist/thirdweb-dev-wallets-evm-wallets-okx.esm.js", + "browser": { + "./dist/thirdweb-dev-wallets-evm-wallets-okx.esm.js": "./dist/thirdweb-dev-wallets-evm-wallets-okx.browser.esm.js" + } +} diff --git a/packages/wallets/package.json b/packages/wallets/package.json index 4e6b54a92f7..885da041b69 100644 --- a/packages/wallets/package.json +++ b/packages/wallets/package.json @@ -22,6 +22,13 @@ }, "default": "./evm/dist/thirdweb-dev-wallets-evm.cjs.js" }, + "./evm/wallets/okx": { + "module": { + "browser": "./evm/wallets/okx/dist/thirdweb-dev-wallets-evm-wallets-okx.browser.esm.js", + "default": "./evm/wallets/okx/dist/thirdweb-dev-wallets-evm-wallets-okx.esm.js" + }, + "default": "./evm/wallets/okx/dist/thirdweb-dev-wallets-evm-wallets-okx.cjs.js" + }, "./evm/wallets/base": { "module": { "browser": "./evm/wallets/base/dist/thirdweb-dev-wallets-evm-wallets-base.browser.esm.js", @@ -141,6 +148,13 @@ }, "default": "./evm/wallets/private-key/dist/thirdweb-dev-wallets-evm-wallets-private-key.cjs.js" }, + "./evm/connectors/okx": { + "module": { + "browser": "./evm/connectors/okx/dist/thirdweb-dev-wallets-evm-connectors-okx.browser.esm.js", + "default": "./evm/connectors/okx/dist/thirdweb-dev-wallets-evm-connectors-okx.esm.js" + }, + "default": "./evm/connectors/okx/dist/thirdweb-dev-wallets-evm-connectors-okx.cjs.js" + }, "./evm/wallets/local-wallet": { "module": { "browser": "./evm/wallets/local-wallet/dist/thirdweb-dev-wallets-evm-wallets-local-wallet.browser.esm.js", diff --git a/packages/wallets/src/evm/connectors/injected/types.ts b/packages/wallets/src/evm/connectors/injected/types.ts index baa631eef31..fac3daa894f 100644 --- a/packages/wallets/src/evm/connectors/injected/types.ts +++ b/packages/wallets/src/evm/connectors/injected/types.ts @@ -50,6 +50,7 @@ type InjectedProviderFlags = { isTrustWallet?: true; isXDEFI?: true; isZerion?: true; + isOkxWallet?: true; }; type InjectedProviders = InjectedProviderFlags & { isMetaMask: true; diff --git a/packages/wallets/src/evm/connectors/okx/getInjectedOKXProvider.ts b/packages/wallets/src/evm/connectors/okx/getInjectedOKXProvider.ts new file mode 100644 index 00000000000..44dbccbeaaf --- /dev/null +++ b/packages/wallets/src/evm/connectors/okx/getInjectedOKXProvider.ts @@ -0,0 +1,20 @@ +import { Ethereum } from "../injected/types"; +import { assertWindowEthereum } from "../../utils/assertWindowEthereum"; + +declare global { + interface Window { + okxwallet?: Ethereum; + } +} + +export function getInjectedOKXProvider(): Ethereum | undefined { + if (typeof window === "undefined") { + return; + } + + if (assertWindowEthereum(globalThis.window)) { + if (globalThis.window.ethereum && window.okxwallet) { + return window.okxwallet; + } + } +} diff --git a/packages/wallets/src/evm/connectors/okx/index.ts b/packages/wallets/src/evm/connectors/okx/index.ts new file mode 100644 index 00000000000..45ac2ffdb84 --- /dev/null +++ b/packages/wallets/src/evm/connectors/okx/index.ts @@ -0,0 +1,143 @@ +import { AsyncStorage } from "../../../core/AsyncStorage"; +import { + ConnectorNotFoundError, + ResourceUnavailableError, + RpcError, + UserRejectedRequestError, +} from "../../../lib/wagmi-core/errors"; +import { walletIds } from "../../constants/walletIds"; +import { InjectedConnector, InjectedConnectorOptions } from "../injected"; +import type { Chain } from "@thirdweb-dev/chains"; +import { utils } from "ethers"; +import { getInjectedOKXProvider } from "./getInjectedOKXProvider"; + +export type OKXConnectorOptions = InjectedConnectorOptions; + +type OKXConnectorConstructorArg = { + chains?: Chain[]; + connectorStorage: AsyncStorage; + options?: OKXConnectorOptions; +}; + +export class OKXConnector extends InjectedConnector { + readonly id = walletIds.okx; + + constructor(arg: OKXConnectorConstructorArg) { + const defaultOptions = { + name: "OKX", + shimDisconnect: true, + shimChainChangedDisconnect: true, + getProvider: getInjectedOKXProvider, + }; + + const options = { + ...defaultOptions, + ...arg.options, + }; + + super({ + chains: arg.chains, + options, + connectorStorage: arg.connectorStorage, + }); + } + + /** + * Connect to injected OKX provider + */ + async connect(options: { chainId?: number } = {}) { + try { + const provider = await this.getProvider(); + if (!provider) { + throw new ConnectorNotFoundError(); + } + + this.setupListeners(); + + // emit "connecting" event + this.emit("message", { type: "connecting" }); + + // Attempt to show wallet select prompt with `wallet_requestPermissions` when + // `shimDisconnect` is active and account is in disconnected state (flag in storage) + let account: string | null = null; + if ( + this.options?.shimDisconnect && + !Boolean(this.connectorStorage.getItem(this.shimDisconnectKey)) + ) { + account = await this.getAccount().catch(() => null); + const isConnected = !!account; + if (isConnected) { + // Attempt to show another prompt for selecting wallet if already connected + try { + await provider.request({ + method: "wallet_requestPermissions", + params: [{ eth_accounts: {} }], + }); + } catch (error) { + // Not all injected providers support `wallet_requestPermissions` (e.g. iOS). + // Only bubble up error if user rejects request + if (this.isUserRejectedRequestError(error)) { + throw new UserRejectedRequestError(error); + } + } + } + } + + // if account is not already set, request accounts and use the first account + if (!account) { + const accounts = await provider.request({ + method: "eth_requestAccounts", + }); + account = utils.getAddress(accounts[0] as string); + } + + // get currently connected chainId + let connectedChainId = await this.getChainId(); + // check if connected chain is unsupported + let isUnsupported = this.isChainUnsupported(connectedChainId); + + // if chainId is given, but does not match the currently connected chainId, switch to the given chainId + if (options.chainId && connectedChainId !== options.chainId) { + try { + await this.switchChain(options.chainId); + // recalculate the chainId and isUnsupported + connectedChainId = options.chainId; + isUnsupported = this.isChainUnsupported(options.chainId); + } catch (e) { + console.error(`Could not switch to chain id : ${options.chainId}`, e); + } + } + + // if shimDisconnect is enabled + if (this.options?.shimDisconnect) { + // add shimDisconnectKey in storage - this signals that connector is "connected" + await this.connectorStorage.setItem(this.shimDisconnectKey, "true"); + } + + const connectionInfo = { + chain: { id: connectedChainId, unsupported: isUnsupported }, + provider: provider, + account, + }; + + this.emit("connect", connectionInfo); + return connectionInfo; + } catch (error) { + if (this.isUserRejectedRequestError(error)) { + throw new UserRejectedRequestError(error); + } + if ((error as RpcError).code === -32002) { + throw new ResourceUnavailableError(error); + } + throw error; + } + } + + async switchAccount() { + const provider = await this.getProvider(); + await provider.request({ + method: "wallet_requestPermissions", + params: [{ eth_accounts: {} }], + }); + } +} diff --git a/packages/wallets/src/evm/constants/walletIds.ts b/packages/wallets/src/evm/constants/walletIds.ts index b05ee69c622..f03dce239db 100644 --- a/packages/wallets/src/evm/constants/walletIds.ts +++ b/packages/wallets/src/evm/constants/walletIds.ts @@ -14,5 +14,6 @@ export const walletIds = { walletConnectV1: "walletConnectV1", walletConnect: "walletConnect", phantom: "phantom", + okx: "okx", // add new ids sorted alphabetically } as const; diff --git a/packages/wallets/src/evm/index.ts b/packages/wallets/src/evm/index.ts index 1b77c245b31..a844fefe661 100644 --- a/packages/wallets/src/evm/index.ts +++ b/packages/wallets/src/evm/index.ts @@ -45,6 +45,9 @@ export * from "./wallets/wallet-connect"; export * from "./wallets/wallet-connect-v1"; export * from "./wallets/zerion"; +export { OKXWallet, type OKXWalletOptions } from "./wallets/okx"; +export { getInjectedOKXProvider } from "./connectors/okx/getInjectedOKXProvider"; + export type { Chain } from "@thirdweb-dev/chains"; // export the window ethereum util diff --git a/packages/wallets/src/evm/wallets/metamask.ts b/packages/wallets/src/evm/wallets/metamask.ts index 96825397192..cb0a13f281e 100644 --- a/packages/wallets/src/evm/wallets/metamask.ts +++ b/packages/wallets/src/evm/wallets/metamask.ts @@ -9,7 +9,7 @@ import { getInjectedMetamaskProvider } from "../connectors/metamask/getInjectedM type MetamaskAdditionalOptions = { /** - * Whether to open the default Wallet Connect QR code Modal for connecting to Zerion Wallet on mobile if Zerion is not injected when calling connect(). + * Whether to open the default Wallet Connect QR code Modal for connecting to MetaMask Wallet on mobile if MetaMask is not injected when calling connect(). */ qrcode?: boolean; @@ -148,8 +148,7 @@ export class MetaMaskWallet extends AbstractClientWallet; + +type ConnectWithQrCodeArgs = { + chainId?: number; + onQrCodeUri: (uri: string) => void; + onConnected: (accountAddress: string) => void; +}; + +export class OKXWallet extends AbstractClientWallet { + connector?: Connector; + walletConnectConnector?: WalletConnectConnectorType; + OKXConnector?: OKXConnectorType; + isInjected: boolean; + + static id = walletIds.okx as string; + + public get walletName() { + return "OKX" as const; + } + + constructor(options: OKXWalletOptions) { + super(OKXWallet.id, options); + this.isInjected = !!getInjectedOKXProvider(); + } + + protected async getConnector(): Promise { + if (!this.connector) { + // if OKX is injected, use the injected connector + // otherwise, use the wallet connect connector for using the OKX app on mobile via QR code scan + + if (this.isInjected) { + // import the connector dynamically + const { OKXConnector } = await import("../connectors/okx"); + this.OKXConnector = new OKXConnector({ + chains: this.chains, + connectorStorage: this.walletStorage, + options: { + shimDisconnect: true, + }, + }); + + this.connector = new WagmiAdapter(this.OKXConnector); + } 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 wallet with QR code + * + * @example + * ```typescript + * wallet.connectWithQrCode({ + * chainId: 1, + * onQrCodeUri(qrCodeUri) { + * // render the QR code with `qrCodeUri` + * }, + * onConnected(accountAddress) { + * // update UI to show connected state + * }, + * }) + * ``` + */ + 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); + } + + async switchAccount() { + if (!this.OKXConnector) { + throw new Error("Can not switch Account"); + } + + await this.OKXConnector.switchAccount(); + } +} diff --git a/packages/wallets/src/evm/wallets/rainbow-wallet.ts b/packages/wallets/src/evm/wallets/rainbow-wallet.ts index 1f14517d204..57c222939c0 100644 --- a/packages/wallets/src/evm/wallets/rainbow-wallet.ts +++ b/packages/wallets/src/evm/wallets/rainbow-wallet.ts @@ -9,7 +9,7 @@ import { getInjectedRainbowProvider } from "../connectors/rainbow/getInjectedRai type RainbowAdditionalOptions = { /** - * Whether to open the default Wallet Connect QR code Modal for connecting to Zerion Wallet on mobile if Zerion is not injected when calling connect(). + * Whether to open the default Wallet Connect QR code Modal for connecting to Rainbow Wallet on mobile if Rainbow is not injected when calling connect(). */ qrcode?: boolean;