diff --git a/packages/core/package.json b/packages/core/package.json index 6ad18abd3..1c3448438 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -79,7 +79,7 @@ "@cosmjs/cosmwasm-stargate": "^0.32.3", "@cosmjs/proto-signing": "^0.32.3", "@cosmjs/stargate": "^0.32.3", - "@dao-dao/cosmiframe": "^0.1.0", + "@dao-dao/cosmiframe": "^1.0.0-rc.1", "@walletconnect/types": "2.11.0", "bowser": "2.11.0", "cosmjs-types": "^0.9.0", diff --git a/packages/core/src/cosmiframe/cosmiframe.ts b/packages/core/src/cosmiframe/cosmiframe.ts index 4c36ebc9f..fcfd07d98 100644 --- a/packages/core/src/cosmiframe/cosmiframe.ts +++ b/packages/core/src/cosmiframe/cosmiframe.ts @@ -1,6 +1,8 @@ +import { Origin } from '@dao-dao/cosmiframe'; + import { cosmiframeExtensionInfo, CosmiframeWallet } from './extension'; -export const makeCosmiframeWallet = (allowedParentOrigins: string[]) => +export const makeCosmiframeWallet = (allowedParentOrigins: Origin[]) => new CosmiframeWallet({ ...cosmiframeExtensionInfo, allowedParentOrigins, diff --git a/packages/core/src/cosmiframe/extension/types.ts b/packages/core/src/cosmiframe/extension/types.ts index f5b8dd304..870ce4e7d 100644 --- a/packages/core/src/cosmiframe/extension/types.ts +++ b/packages/core/src/cosmiframe/extension/types.ts @@ -1,5 +1,7 @@ +import { Origin } from '@dao-dao/cosmiframe'; + import { Wallet } from '../../types'; export type CosmiframeWalletInfo = Wallet & { - allowedParentOrigins: string[]; + allowedParentOrigins: Origin[]; }; diff --git a/packages/core/src/manager.ts b/packages/core/src/manager.ts index 5bd804f2d..4e19ee7d3 100644 --- a/packages/core/src/manager.ts +++ b/packages/core/src/manager.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { AssetList, Chain } from '@chain-registry/types'; -import { isInIframe } from '@dao-dao/cosmiframe'; +import { isInIframe, Origin } from '@dao-dao/cosmiframe'; import Bowser from 'bowser'; import EventEmitter from 'events'; @@ -52,7 +52,13 @@ export class WalletManager extends StateBase { logger: Logger, throwErrors: boolean | 'connect_only', subscribeConnectEvents = true, - allowedCosmiframeParentOrigins?: string[], + allowedCosmiframeParentOrigins: Origin[] = [ + /^https?:\/\/localhost(:\d+)?/, + /^https:\/\/(.+\.)?osmosis\.zone/, + /^https:\/\/(.+\.)?daodao\.zone/, + /^https:\/\/.+-da0da0\.vercel\.app/, + /^https:\/\/(.+\.)?abstract\.money/, + ], assetLists?: AssetList[], defaultNameService?: NameServiceName, walletConnectOptions?: WalletConnectOptions, @@ -87,8 +93,8 @@ export class WalletManager extends StateBase { ]; wallets.forEach( ({ walletName }) => - (this._reconnectMap[walletName] = () => - this._reconnect(walletName, true)) + (this._reconnectMap[walletName] = () => + this._reconnect(walletName, true)) ); this.init( chains, diff --git a/packages/react-lite/package.json b/packages/react-lite/package.json index 10ba3e744..49216c340 100644 --- a/packages/react-lite/package.json +++ b/packages/react-lite/package.json @@ -80,7 +80,7 @@ "dependencies": { "@chain-registry/types": "^0.46.11", "@cosmos-kit/core": "^2.15.0", - "@dao-dao/cosmiframe": "^0.1.0" + "@dao-dao/cosmiframe": "^1.0.0-rc.1" }, "resolutions": { "@types/react": "^18.2" diff --git a/packages/react-lite/src/hooks/useIframe.ts b/packages/react-lite/src/hooks/useIframe.ts index 902119f77..d5840893b 100644 --- a/packages/react-lite/src/hooks/useIframe.ts +++ b/packages/react-lite/src/hooks/useIframe.ts @@ -9,6 +9,7 @@ import { } from '@cosmos-kit/core'; import { Cosmiframe, + Origin, OverrideHandler, ParentMetadata, } from '@dao-dao/cosmiframe'; @@ -20,6 +21,12 @@ export type FunctionKeys = { [K in keyof T]: T[K] extends (...args: unknown[]) => unknown ? K : never; }[keyof T]; +export type UseIframeSignerOverrides = Partial<{ + [K in keyof (OfflineAminoSigner & OfflineDirectSigner)]: ( + ...params: Parameters<(OfflineAminoSigner & OfflineDirectSigner)[K]> + ) => OverrideHandler | Promise; +}>; + export type UseIframeOptions = { /** * Optionally attempt to use a specific wallet. Otherwise get the first active @@ -47,16 +54,34 @@ export type UseIframeOptions = { * should handle the function. By default, if nothing is returned, an error * will be thrown with the message "Handled by outer wallet." */ - signerOverrides?: Partial<{ - [K in keyof (OfflineAminoSigner & OfflineDirectSigner)]: ( - ...params: Parameters<(OfflineAminoSigner & OfflineDirectSigner)[K]> - ) => OverrideHandler | Promise; - }>; + signerOverrides?: + | UseIframeSignerOverrides + | ((chainId: string) => UseIframeSignerOverrides) + | ((chainId: string) => Promise); /** * Optionally only respond to requests from iframes of specific origin. If * undefined or empty, all origins are allowed. */ - origins?: string[]; + origins?: Origin[]; +}; + +export type UseIframeReturn = { + /** + * The main wallet, or undefined if the wallet is not connected. + */ + wallet: MainWalletBase | undefined; + /** + * A ref callback to set the iframe element. + */ + iframeRef: RefCallback; + /** + * The iframe element. + */ + iframe: HTMLIFrameElement | null; + /** + * A function to trigger a keystore change event on the iframe app wallet. + */ + triggerKeystoreChange: () => void; }; export const useIframe = ({ @@ -65,10 +90,7 @@ export const useIframe = ({ walletClientOverrides, signerOverrides, origins, -}: UseIframeOptions = {}): { - wallet: MainWalletBase; - iframeRef: RefCallback; -} => { +}: UseIframeOptions = {}): UseIframeReturn => { const wallet = useWallet(walletName).mainWallet; const [iframe, setIframe] = useState(null); @@ -79,20 +101,22 @@ export const useIframe = ({ const signerOverridesRef = useRef(signerOverrides); signerOverridesRef.current = signerOverrides; - // Broadcast keystore change event to iframe wallet. - useEffect(() => { - const notifyIframe = () => { + const triggerKeystoreChange = useCallback( + () => iframe?.contentWindow.postMessage( { event: COSMIFRAME_KEYSTORECHANGE_EVENT, }, '*' - ); - }; + ), + [iframe] + ); + // Broadcast keystore change event to iframe wallet. + useEffect(() => { // Notify inner window of keystore change on any wallet client change // (likely either connection or disconnection). - notifyIframe(); + triggerKeystoreChange(); if (!wallet || typeof window === 'undefined') { return; @@ -100,33 +124,28 @@ export const useIframe = ({ // Notify inner window of keystore change on any wallet connect event. wallet.walletInfo.connectEventNamesOnWindow?.forEach((eventName) => { - window.addEventListener(eventName, notifyIframe); + window.addEventListener(eventName, triggerKeystoreChange); }); wallet.walletInfo.connectEventNamesOnClient?.forEach(async (eventName) => { - wallet.client?.on?.(eventName, notifyIframe); + wallet.client?.on?.(eventName, triggerKeystoreChange); }); return () => { wallet.walletInfo.connectEventNamesOnWindow?.forEach((eventName) => { - window.removeEventListener(eventName, notifyIframe); + window.removeEventListener(eventName, triggerKeystoreChange); }); wallet.walletInfo.connectEventNamesOnClient?.forEach( async (eventName) => { - wallet.client?.off?.(eventName, notifyIframe); + wallet.client?.off?.(eventName, triggerKeystoreChange); } ); }; - }, [wallet, iframe]); + }, [wallet, triggerKeystoreChange]); // Whenever wallet changes, broadcast keystore change event to iframe wallet. useEffect(() => { - iframe?.contentWindow.postMessage( - { - event: COSMIFRAME_KEYSTORECHANGE_EVENT, - }, - '*' - ); - }, [wallet, iframe]); + triggerKeystoreChange(); + }, [wallet, triggerKeystoreChange]); useEffect(() => { if (!iframe) { @@ -138,13 +157,21 @@ export const useIframe = ({ target: wallet?.client || {}, getOfflineSignerDirect: wallet?.client.getOfflineSignerDirect.bind(wallet.client) || - (() => Promise.reject(COSMIFRAME_NOT_CONNECTED_MESSAGE)), + (() => + Promise.reject( + COSMIFRAME_NOT_CONNECTED_MESSAGE + + ' No direct signer client function found.' + )), getOfflineSignerAmino: wallet?.client.getOfflineSignerAmino.bind(wallet.client) || - (() => Promise.reject(COSMIFRAME_NOT_CONNECTED_MESSAGE)), + (() => + Promise.reject( + COSMIFRAME_NOT_CONNECTED_MESSAGE + + ' No amino signer client function found.' + )), nonSignerOverrides: () => ({ ...walletClientOverridesRef.current, - // Override connect to return wallet info. + // Override connect to return specific error. connect: async (...params) => { if (walletClientOverridesRef.current?.connect) { return await walletClientOverridesRef.current.connect( @@ -156,12 +183,17 @@ export const useIframe = ({ } else { return { type: 'error', - error: COSMIFRAME_NOT_CONNECTED_MESSAGE, + error: + COSMIFRAME_NOT_CONNECTED_MESSAGE + + ' No connect client function found or override provided.', }; } }, }), - signerOverrides: () => signerOverridesRef.current, + signerOverrides: async (chainId) => + typeof signerOverridesRef.current === 'function' + ? signerOverridesRef.current(chainId) + : signerOverridesRef.current, origins, metadata: { name: @@ -189,5 +221,7 @@ export const useIframe = ({ return { wallet, iframeRef, + iframe, + triggerKeystoreChange, }; }; diff --git a/packages/react-lite/src/provider.tsx b/packages/react-lite/src/provider.tsx index be9d09178..bb00a4663 100644 --- a/packages/react-lite/src/provider.tsx +++ b/packages/react-lite/src/provider.tsx @@ -15,6 +15,7 @@ import { WalletModalProps, WalletRepo, } from '@cosmos-kit/core'; +import { Origin } from '@dao-dao/cosmiframe'; import { createContext, ReactNode, useEffect, useMemo, useState } from 'react'; export const walletContext = createContext<{ @@ -35,16 +36,7 @@ export function ChainProvider({ endpointOptions, sessionOptions, logLevel = 'WARN', - allowedIframeParentOrigins = [ - 'http://localhost:3000', - 'https://localhost:3000', - 'https://app.osmosis.zone', - 'https://daodao.zone', - 'https://dao.daodao.zone', - 'https://my.abstract.money', - 'https://apps.abstract.money', - 'https://console.abstract.money', - ], + allowedIframeParentOrigins, children, }: { chains: (Chain | ChainName)[]; @@ -63,9 +55,9 @@ export function ChainProvider({ * Origins to allow wrapping this app in an iframe and connecting to this * Cosmos Kit instance. * - * Defaults to Osmosis and DAO DAO. + * Defaults to localhost, Osmosis, DAO DAO, and Abstract. */ - allowedIframeParentOrigins?: string[]; + allowedIframeParentOrigins?: Origin[]; children: ReactNode; }) { const logger = useMemo(() => new Logger(logLevel), []); @@ -143,10 +135,10 @@ export function ChainProvider({ }, []); useEffect(() => { - walletManager && walletManager.onMounted(); + walletManager?.onMounted(); return () => { setViewOpen(false); - walletManager && walletManager.onUnmounted(); + walletManager?.onUnmounted(); }; }, [render, walletManager]); diff --git a/packages/react/package.json b/packages/react/package.json index 76b6b6d85..df7dd3108 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -76,7 +76,8 @@ }, "devDependencies": { "@types/react": "^18.2", - "@types/react-dom": "^18.2" + "@types/react-dom": "^18.2", + "@dao-dao/cosmiframe": "^1.0.0-rc.1" }, "dependencies": { "@chain-registry/types": "^0.46.11", diff --git a/packages/react/src/provider.tsx b/packages/react/src/provider.tsx index 8158cd319..5c5d47f91 100644 --- a/packages/react/src/provider.tsx +++ b/packages/react/src/provider.tsx @@ -13,11 +13,12 @@ import { WalletModalProps, } from '@cosmos-kit/core'; import { ChainProvider as ChainProviderLite } from '@cosmos-kit/react-lite'; +import { Origin } from '@dao-dao/cosmiframe'; import { ReactNode, useCallback, useMemo } from 'react'; +import { SelectedWalletRepoProvider } from './context'; import { ThemeCustomizationProps, WalletModal } from './modal'; import { defaultModalViews } from './modal/components/views'; -import { SelectedWalletRepoProvider } from './context'; export const ChainProvider = ({ chains, @@ -33,16 +34,7 @@ export const ChainProvider = ({ endpointOptions, sessionOptions, logLevel = 'WARN', - allowedIframeParentOrigins = [ - 'http://localhost:3000', - 'https://localhost:3000', - 'https://app.osmosis.zone', - 'https://daodao.zone', - 'https://dao.daodao.zone', - 'https://my.abstract.money', - 'https://apps.abstract.money', - 'https://console.abstract.money' - ], + allowedIframeParentOrigins, children, modalTheme = {}, modalOptions, @@ -64,9 +56,9 @@ export const ChainProvider = ({ * Origins to allow wrapping this app in an iframe and connecting to this * Cosmos Kit instance. * - * Defaults to Osmosis and DAO DAO. + * Defaults to Osmosis, DAO DAO, and Abstract. */ - allowedIframeParentOrigins?: string[]; + allowedIframeParentOrigins?: Origin[]; children: ReactNode; modalTheme?: ThemeCustomizationProps; modalOptions?: ModalOptions; diff --git a/yarn.lock b/yarn.lock index 1530b208c..e0a2892d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1581,10 +1581,10 @@ node-appwrite "^14.0.0" ses "^0.18.4" -"@dao-dao/cosmiframe@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@dao-dao/cosmiframe/-/cosmiframe-0.1.0.tgz#5ba241d0c14a45d62df60cff7d48ba26844ebeac" - integrity sha512-NW4pGt1ctqDfhn/A6RU2vwnFEu3O4aBNnBMrGnw31n+L35drYNEsA9ZB7KZsHmRRlkNx+jSuJSv2Fv0BFBDDJQ== +"@dao-dao/cosmiframe@^1.0.0-rc.1": + version "1.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@dao-dao/cosmiframe/-/cosmiframe-1.0.0-rc.1.tgz#51d4d1801605b8abfd01ae0425c797b6b876991e" + integrity sha512-XftJPwbqgS1RWg9SUNBI58vTGnjINZ995OYRwDuUZ6CsuENS2DifAzqgzDgdac+0o5Hqvy/5Aq+HN1Jl/dptTw== dependencies: uuid "^9.0.1" @@ -18024,16 +18024,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -18149,7 +18140,7 @@ stringify-entities@^4.0.0: character-entities-html4 "^2.0.0" character-entities-legacy "^3.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -18177,13 +18168,6 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -19739,7 +19723,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -19757,15 +19741,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"