diff --git a/docs/docs/guides/connect-using-iframe.md b/docs/docs/guides/connect-using-iframe.md index ec2e333..f77617e 100644 --- a/docs/docs/guides/connect-using-iframe.md +++ b/docs/docs/guides/connect-using-iframe.md @@ -14,7 +14,9 @@ Cosmiframe enforces security by requiring you to specify allowed origins in the diff --git a/docs/docs/provider/grazProvider.md b/docs/docs/provider/grazProvider.md index 44d77de..44e5351 100644 --- a/docs/docs/provider/grazProvider.md +++ b/docs/docs/provider/grazProvider.md @@ -67,7 +67,11 @@ export default function CustomApp({ Component, pageProps }: AppProps) { onReconnectFailed?: () => void; walletConnect?: WalletConnectStore | null; multiChainFetchConcurrency?: number // when using multi chain hooks it fetch 3 function simultaneously. defaults to 3. - allowedIframeParentOrigins?: string[] // for integrating using WalletType.COSMIFRAME + iframeOptions?: { + // for integrating using WalletType.COSMIFRAME + allowedIframeParentOrigins: string[] + autoConnect?: boolean + } } ``` diff --git a/example/next/pages/_app.tsx b/example/next/pages/_app.tsx index f3fb65d..8c32f32 100644 --- a/example/next/pages/_app.tsx +++ b/example/next/pages/_app.tsx @@ -25,7 +25,9 @@ const CustomApp: NextPage = ({ Component, pageProps }) => { apiKey: process.env.NEXT_PUBLIC_CAPSULE_API_KEY, env: process.env.NEXT_PUBLIC_CAPSULE_ENV, }, - allowedIframeParentOrigins: ["https://daodao.zone", "https://dao.daodao.zone"], + iframeOptions: { + allowedIframeParentOrigins: ["https://daodao.zone", "https://dao.daodao.zone", "http://localhost:3000"], + }, }} > diff --git a/example/starter/src/pages/_app.tsx b/example/starter/src/pages/_app.tsx index 7f88d76..30c0458 100644 --- a/example/starter/src/pages/_app.tsx +++ b/example/starter/src/pages/_app.tsx @@ -38,7 +38,9 @@ const MyApp = ({ Component, pageProps }: AppProps) => { preferNoSetFee: true, }, }, - allowedIframeParentOrigins: ["https://daodao.zone", "https://dao.daodao.zone"], + iframeOptions: { + allowedIframeParentOrigins: ["https://daodao.zone", "https://dao.daodao.zone"], + }, }} > diff --git a/packages/graz/src/actions/configure.ts b/packages/graz/src/actions/configure.ts index f0f90f2..dcde88f 100644 --- a/packages/graz/src/actions/configure.ts +++ b/packages/graz/src/actions/configure.ts @@ -1,6 +1,6 @@ import type { ChainInfo } from "@keplr-wallet/types"; -import type { CapsuleConfig, ChainConfig, GrazInternalStore } from "../store"; +import type { CapsuleConfig, ChainConfig, GrazInternalStore, IframeOptions } from "../store"; import { useGrazInternalStore } from "../store"; import type { WalletType } from "../types/wallet"; @@ -23,17 +23,14 @@ export interface ConfigureGrazArgs { */ multiChainFetchConcurrency?: number; /** - * Origins to allow wrapping this app in an iframe and connecting to this - * Graz instance. - * - * Defaults to none, which disables the iframe wallet. + * Options to enable iframe wallet connection. */ - allowedIframeParentOrigins?: string[]; + iframeOptions?: IframeOptions; } export const configureGraz = (args: ConfigureGrazArgs): ConfigureGrazArgs => { useGrazInternalStore.setState((prev) => ({ - allowedIframeParentOrigins: args.allowedIframeParentOrigins || prev.allowedIframeParentOrigins, + iframeOptions: args.iframeOptions || prev.iframeOptions, walletConnect: args.walletConnect || prev.walletConnect, walletType: args.defaultWallet || prev.walletType, capsuleConfig: args.capsuleConfig || prev.capsuleConfig, diff --git a/packages/graz/src/actions/wallet/cosmiframe.ts b/packages/graz/src/actions/wallet/cosmiframe.ts index 262ef4f..e8f80af 100644 --- a/packages/graz/src/actions/wallet/cosmiframe.ts +++ b/packages/graz/src/actions/wallet/cosmiframe.ts @@ -20,17 +20,22 @@ import type { Wallet } from "../../types/wallet"; export const getCosmiframe = (): Wallet => { const state = useGrazInternalStore.getState(); + if (!state.iframeOptions) { + state._notFoundFn(); + throw new Error("no iframe options set"); + } + if (!isInIframe()) { state._notFoundFn(); throw new Error("not in iframe"); } - if (!state.allowedIframeParentOrigins?.length) { + if (!state.iframeOptions.allowedIframeParentOrigins.length) { state._notFoundFn(); throw new Error("no iframe allowed origins"); } - const keplr = new Cosmiframe(state.allowedIframeParentOrigins).getKeplrClient(); + const keplr = new Cosmiframe(state.iframeOptions.allowedIframeParentOrigins).getKeplrClient(); return { enable: keplr.enable.bind(keplr), diff --git a/packages/graz/src/provider/events.tsx b/packages/graz/src/provider/events.tsx index b4f2b24..8c12d32 100644 --- a/packages/graz/src/provider/events.tsx +++ b/packages/graz/src/provider/events.tsx @@ -1,7 +1,8 @@ +import { Cosmiframe } from "@dao-dao/cosmiframe"; import type { FC } from "react"; import { useEffect } from "react"; -import { reconnect } from "../actions/account"; +import { connect, reconnect } from "../actions/account"; import { checkWallet } from "../actions/wallet"; import { getCosmiframe } from "../actions/wallet/cosmiframe"; import { getCosmostation } from "../actions/wallet/cosmostation"; @@ -23,10 +24,34 @@ import { WalletType } from "../types/wallet"; export const useGrazEvents = () => { const isSessionActive = typeof window !== "undefined" && window.sessionStorage.getItem(RECONNECT_SESSION_KEY) === "Active"; - const { _reconnect, _onReconnectFailed, _reconnectConnector } = useGrazInternalStore(); + const { _reconnect, _onReconnectFailed, _reconnectConnector, iframeOptions, chains } = useGrazInternalStore(); const { activeChainIds: activeChains, wcSignClients } = useGrazSessionStore(); const isReconnectConnectorReady = checkWallet(_reconnectConnector || undefined); + // Auto connect to iframe if possible. + useEffect(() => { + if ( + !iframeOptions || + iframeOptions.autoConnect === false || + !iframeOptions.allowedIframeParentOrigins.length || + !chains + ) { + return; + } + + const cosmiframe = new Cosmiframe(iframeOptions.allowedIframeParentOrigins); + void cosmiframe.isReady().then((ready) => { + if (ready) { + return connect({ + chainId: chains.map((c) => c.chainId), + walletType: WalletType.COSMIFRAME, + }); + } + }); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [iframeOptions]); + useEffect(() => { // will reconnect on refresh if (_reconnectConnector) { diff --git a/packages/graz/src/store/index.ts b/packages/graz/src/store/index.ts index 82b456a..f2cfe33 100644 --- a/packages/graz/src/store/index.ts +++ b/packages/graz/src/store/index.ts @@ -32,19 +32,29 @@ export interface CapsuleState { showModal: boolean; chainId?: string[]; } -export interface GrazInternalStore { + +export interface IframeOptions { + /** + * Origins to allow wrapping this app in an iframe and connecting to this Graz + * instance. + */ + allowedIframeParentOrigins: string[]; /** - * Origins to allow wrapping this app in an iframe and connecting to this - * Graz instance. + * Whether or not to auto connect when in an iframe running Cosmiframe. This + * will attempt to connect to all chains provided to GrazProvider. * - * Defaults to none, which disables the iframe wallet. + * Defaults to true. */ - allowedIframeParentOrigins: string[] | null; + autoConnect?: boolean; +} + +export interface GrazInternalStore { recentChainIds: string[] | null; capsuleConfig: CapsuleConfig | null; capsuleState: CapsuleState | null; chains: ChainInfo[] | null; chainsConfig: Record | null; + iframeOptions: IframeOptions | null; /** * Graz will use this number to determine how many concurrent requests to make when using `multiChain` args in hooks. * Defaults to 3. @@ -75,7 +85,7 @@ export type GrazInternalPersistedStore = Pick< >; export const grazInternalDefaultValues: GrazInternalStore = { - allowedIframeParentOrigins: null, + iframeOptions: null, recentChainIds: null, chains: null, chainsConfig: null,