+
+
+
{txButton}
{isWalletConnected ? (
diff --git a/src/components/common/Extensions/UserNavigation/partials/UserMenu/UserMenu.tsx b/src/components/common/Extensions/UserNavigation/partials/UserMenu/UserMenu.tsx
index c5456f393a9..26365db7218 100644
--- a/src/components/common/Extensions/UserNavigation/partials/UserMenu/UserMenu.tsx
+++ b/src/components/common/Extensions/UserNavigation/partials/UserMenu/UserMenu.tsx
@@ -2,7 +2,6 @@ import {
Cardholder,
CaretLeft,
CaretRight,
- CirclesThreePlus,
List,
Plugs,
} from '@phosphor-icons/react';
@@ -19,7 +18,6 @@ import { formatText } from '~utils/intl.ts';
import { splitWalletAddress } from '~utils/splitWalletAddress.ts';
import ThemeSwitcher from '~v5/common/ThemeSwitcher/ThemeSwitcher.tsx';
import Button from '~v5/shared/Button/index.ts';
-import Link from '~v5/shared/Link/index.ts';
import PopoverBase from '~v5/shared/PopoverBase/index.ts';
import TitleLabel from '~v5/shared/TitleLabel/index.ts';
import UserDetails from '~v5/shared/UserDetails/index.ts';
@@ -136,14 +134,6 @@ const UserMenu: FC
= ({
className="pb-2"
/>
);
};
diff --git a/src/components/common/Onboarding/wizardSteps/StepCreateUser/StepCreateUser.tsx b/src/components/common/Onboarding/wizardSteps/StepCreateUser/StepCreateUser.tsx
index f1a121117a9..42cec18772d 100644
--- a/src/components/common/Onboarding/wizardSteps/StepCreateUser/StepCreateUser.tsx
+++ b/src/components/common/Onboarding/wizardSteps/StepCreateUser/StepCreateUser.tsx
@@ -1,3 +1,4 @@
+import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import React from 'react';
import { defineMessages } from 'react-intl';
import { Navigate } from 'react-router-dom';
@@ -39,6 +40,7 @@ const StepCreateUser = ({
},
}: Props) => {
const { user, updateUser } = useAppContext();
+ const { user: dynamicWalletUser } = useDynamicContext();
if (user) {
return
;
@@ -53,7 +55,7 @@ const StepCreateUser = ({
validationSchema={validationSchema}
defaultValues={{
username,
- emailAddress,
+ emailAddress: dynamicWalletUser?.email || emailAddress,
}}
mode="onChange"
actionType={ActionTypes.USERNAME_CREATE}
diff --git a/src/components/frame/LandingPage/LandingPage.tsx b/src/components/frame/LandingPage/LandingPage.tsx
index 1f85bdc293e..710d37838f7 100644
--- a/src/components/frame/LandingPage/LandingPage.tsx
+++ b/src/components/frame/LandingPage/LandingPage.tsx
@@ -47,7 +47,7 @@ const LandingPage = () => {
bottomComponent={
isContentLoading ? (
) : (
diff --git a/src/components/frame/v5/pages/UserAdvancedPage/UserAdvancedPage.tsx b/src/components/frame/v5/pages/UserAdvancedPage/UserAdvancedPage.tsx
index b7e59cdc299..81817967a85 100644
--- a/src/components/frame/v5/pages/UserAdvancedPage/UserAdvancedPage.tsx
+++ b/src/components/frame/v5/pages/UserAdvancedPage/UserAdvancedPage.tsx
@@ -20,11 +20,12 @@ const MSG = defineMessages({
});
const UserAdvancedPage = () => {
- const { user, userLoading, walletConnecting } = useAppContext();
+ const { user, userLoading, walletConnecting, willWalletAutoConnect } =
+ useAppContext();
useSetPageHeadingTitle(formatText({ id: 'advancedSettings.title' }));
- if (userLoading || walletConnecting) {
+ if (userLoading || walletConnecting || (!user && willWalletAutoConnect)) {
return
;
}
diff --git a/src/components/frame/v5/pages/UserCryptoToFiatPage/CryptoToFiatPage.tsx b/src/components/frame/v5/pages/UserCryptoToFiatPage/CryptoToFiatPage.tsx
index 11a09acb023..a1c8eeb2637 100644
--- a/src/components/frame/v5/pages/UserCryptoToFiatPage/CryptoToFiatPage.tsx
+++ b/src/components/frame/v5/pages/UserCryptoToFiatPage/CryptoToFiatPage.tsx
@@ -32,7 +32,8 @@ const MSG = defineMessages({
});
const UserCryptoToFiatPage = () => {
- const { user, userLoading, walletConnecting } = useAppContext();
+ const { user, userLoading, walletConnecting, willWalletAutoConnect } =
+ useAppContext();
const featureFlags = useContext(FeatureFlagsContext);
const cryptoToFiatFeatureFlag = featureFlags[FeatureFlag.CRYPTO_TO_FIAT];
@@ -45,7 +46,8 @@ const UserCryptoToFiatPage = () => {
return
;
}
- const isLoadingUserAndWalletInfo = userLoading || walletConnecting;
+ const isLoadingUserAndWalletInfo =
+ userLoading || walletConnecting || (!user && willWalletAutoConnect);
if (!isLoadingUserAndWalletInfo && !user) {
return
;
diff --git a/src/components/frame/v5/pages/UserPreferencesPage/UserPreferencesPage.tsx b/src/components/frame/v5/pages/UserPreferencesPage/UserPreferencesPage.tsx
index 3aafd6b7b46..3d6e5aa9075 100644
--- a/src/components/frame/v5/pages/UserPreferencesPage/UserPreferencesPage.tsx
+++ b/src/components/frame/v5/pages/UserPreferencesPage/UserPreferencesPage.tsx
@@ -23,11 +23,12 @@ const MSG = defineMessages({
});
const UserPreferencesPage: FC = () => {
- const { user, userLoading, walletConnecting } = useAppContext();
+ const { user, userLoading, walletConnecting, willWalletAutoConnect } =
+ useAppContext();
useSetPageHeadingTitle(formatText({ id: 'userPreferencesPage.title' }));
- if (userLoading || walletConnecting) {
+ if (userLoading || walletConnecting || (!user && willWalletAutoConnect)) {
return
;
}
diff --git a/src/components/frame/v5/pages/UserProfilePage/partials/UserAccountPage/UserAccountPage.tsx b/src/components/frame/v5/pages/UserProfilePage/partials/UserAccountPage/UserAccountPage.tsx
index a6166715754..c44bf582955 100644
--- a/src/components/frame/v5/pages/UserProfilePage/partials/UserAccountPage/UserAccountPage.tsx
+++ b/src/components/frame/v5/pages/UserProfilePage/partials/UserAccountPage/UserAccountPage.tsx
@@ -24,12 +24,13 @@ const MSG = defineMessages({
});
const UserAccountPage: FC = () => {
- const { user, userLoading, walletConnecting } = useAppContext();
+ const { user, userLoading, walletConnecting, willWalletAutoConnect } =
+ useAppContext();
const { handleSubmit } = useUserProfile();
useSetPageHeadingTitle(formatText({ id: 'userProfileTab.title' }));
- if (userLoading || walletConnecting) {
+ if (userLoading || walletConnecting || (!user && willWalletAutoConnect)) {
return
;
}
diff --git a/src/components/v5/common/ActionSidebar/ActionSidebar.tsx b/src/components/v5/common/ActionSidebar/ActionSidebar.tsx
index 1ad718640c8..6b8c5410774 100644
--- a/src/components/v5/common/ActionSidebar/ActionSidebar.tsx
+++ b/src/components/v5/common/ActionSidebar/ActionSidebar.tsx
@@ -18,12 +18,18 @@ import React, {
import { isFullScreen } from '~constants/index.ts';
import { useActionContext } from '~context/ActionContext/ActionContext.ts';
import { useActionSidebarContext } from '~context/ActionSidebarContext/ActionSidebarContext.ts';
+import {
+ isElementInsideDynamicXYZModal,
+ isElementInsideModalOrPortal,
+} from '~context/ActionSidebarContext/utils.ts';
import { useMobile } from '~hooks/index.ts';
import useCopyToClipboard from '~hooks/useCopyToClipboard.ts';
import useDisableBodyScroll from '~hooks/useDisableBodyScroll/index.ts';
import { useDraftAgreement } from '~hooks/useDraftAgreement.ts';
import useToggle from '~hooks/useToggle/index.ts';
import Tooltip from '~shared/Extensions/Tooltip/Tooltip.tsx';
+import { isChildOf } from '~utils/checks/isChildOf.ts';
+import { getElementWithSelector } from '~utils/elements.ts';
import { formatText } from '~utils/intl.ts';
import Modal from '~v5/shared/Modal/index.ts';
@@ -65,9 +71,16 @@ const ActionSidebar: FC
> = ({
const {
actionSidebarToggle: [
isActionSidebarOpen,
- { toggle: toggleActionSidebarOff, registerContainerRef },
+ {
+ toggleOff: toggleActionSidebarOff,
+ registerContainerRef,
+ useRegisterOnBeforeCloseCallback,
+ },
+ ],
+ cancelModalToggle: [
+ isCancelModalOpen,
+ { toggleOff: toggleCancelModalOff, toggleOn: toggleCancelModalOn },
],
- cancelModalToggle: [isCancelModalOpen, { toggleOff: toggleCancelModalOff }],
actionSidebarInitialValues,
} = useActionSidebarContext();
const actionType = mapActionTypeToAction(action);
@@ -107,6 +120,36 @@ const ActionSidebar: FC> = ({
formContextOverride: formRef.current,
});
+ useRegisterOnBeforeCloseCallback((element) => {
+ const isClickedInside = isElementInsideModalOrPortal(element);
+ const isInsideDynamicXYZModal = isElementInsideDynamicXYZModal(element);
+ const navigationWrapper = getElementWithSelector('.modal-blur-navigation');
+
+ if (isClickedInside || isInsideDynamicXYZModal) {
+ return false;
+ }
+
+ const { dirtyFields } = formRef.current?.formState ?? {};
+
+ // Prevent closing if navigation was clicked, only if the form is not dirty
+ if (!dirtyFields && isChildOf(navigationWrapper, element)) {
+ return false;
+ }
+
+ if (
+ dirtyFields &&
+ Object.keys(dirtyFields).length > 0 &&
+ !isCancelModalOpen &&
+ !getIsDraftAgreement()
+ ) {
+ toggleCancelModalOn();
+ return false;
+ }
+
+ return undefined;
+ // Remove tx param
+ });
+
const { isCopied, handleClipboardCopy } = useCopyToClipboard();
const isMobile = useMobile();
diff --git a/src/components/v5/common/ActionSidebar/partials/ActionButtons.tsx b/src/components/v5/common/ActionSidebar/partials/ActionButtons.tsx
index 75f2c54b3bd..8a229c546df 100644
--- a/src/components/v5/common/ActionSidebar/partials/ActionButtons.tsx
+++ b/src/components/v5/common/ActionSidebar/partials/ActionButtons.tsx
@@ -3,10 +3,7 @@ import React, { type FC } from 'react';
import { useFormContext } from 'react-hook-form';
import { Action } from '~constants/actions.ts';
-import { useActionSidebarContext } from '~context/ActionSidebarContext/ActionSidebarContext.ts';
-import { isElementInsideModalOrPortal } from '~context/ActionSidebarContext/utils.ts';
import { useMobile } from '~hooks/index.ts';
-import { useDraftAgreement } from '~hooks/useDraftAgreement.ts';
import IconButton from '~v5/shared/Button/IconButton.tsx';
import Button from '~v5/shared/Button/index.ts';
@@ -32,41 +29,17 @@ const ActionButtons: FC = ({
const submitText = useSubmitButtonText();
const isButtonDisabled = useSubmitButtonDisabled();
const {
- formState: { isSubmitting, dirtyFields },
+ formState: { isSubmitting },
getValues,
} = useFormContext();
- const {
- actionSidebarToggle: [, { useRegisterOnBeforeCloseCallback }],
- cancelModalToggle: [isCancelModalOpen, { toggleOn: toggleCancelModalOn }],
- } = useActionSidebarContext();
const { closeSidebarClick } = useCloseSidebarClick();
const isFieldDisabled = useIsFieldDisabled();
- const { getIsDraftAgreement } = useDraftAgreement();
const formValues = getValues();
const selectedActionType = formValues[ACTION_TYPE_FIELD_NAME];
const createDecisionActionSelected =
selectedActionType === Action.CreateDecision;
- useRegisterOnBeforeCloseCallback((element) => {
- const isClickedInside = isElementInsideModalOrPortal(element);
-
- if (!isClickedInside) {
- return false;
- }
-
- if (
- Object.keys(dirtyFields).length > 0 &&
- !isCancelModalOpen &&
- !getIsDraftAgreement()
- ) {
- toggleCancelModalOn();
- return false;
- }
-
- return undefined;
- });
-
const primaryButtonType = primaryButton?.type ?? 'submit';
return (
diff --git a/src/context/ActionSidebarContext/ActionSidebarContextProvider.tsx b/src/context/ActionSidebarContext/ActionSidebarContextProvider.tsx
index 7a29d27176e..3ca79c1f79c 100644
--- a/src/context/ActionSidebarContext/ActionSidebarContextProvider.tsx
+++ b/src/context/ActionSidebarContext/ActionSidebarContextProvider.tsx
@@ -8,12 +8,9 @@ import React, {
import { type FieldValues } from 'react-hook-form';
import { useNavigate, useSearchParams } from 'react-router-dom';
-import { useTablet } from '~hooks';
import useGetSelectedDomainFilter from '~hooks/useGetSelectedDomainFilter.tsx';
import useToggle from '~hooks/useToggle/index.ts';
import { TX_SEARCH_PARAM } from '~routes/routeConstants.ts';
-import { isChildOf } from '~utils/checks/isChildOf.ts';
-import { getElementWithSelector } from '~utils/elements.ts';
import { removeQueryParamFromUrl } from '~utils/urls.ts';
import {
FROM_FIELD_NAME,
@@ -33,7 +30,6 @@ import {
ActionSidebarContext,
type ActionSidebarContextValue,
} from './ActionSidebarContext.ts';
-import { isElementInsideModalOrPortal } from './utils.ts';
const OPEN_ACTION_PANEL_EVENT: AnalyticsEvent = {
event: AnalyticsEventType.CUSTOM_EVENT,
@@ -43,10 +39,20 @@ const OPEN_ACTION_PANEL_EVENT: AnalyticsEvent = {
};
const ActionSidebarContextProvider: FC = ({ children }) => {
+ const navigate = useNavigate();
+ const [searchParams] = useSearchParams();
+ const transactionId = searchParams?.get(TX_SEARCH_PARAM);
+
+ const removeTxParamOnClose = useCallback(() => {
+ // Remove the TX_SEARCH_PARAM search param only if it is present
+ if (transactionId) {
+ navigate(removeQueryParamFromUrl(window.location.href, TX_SEARCH_PARAM));
+ }
+ }, [navigate, transactionId]);
+
const [actionSidebarInitialValues, setActionSidebarInitialValues] =
useState();
const cancelModalToggle = useToggle();
- const isTablet = useTablet();
const [
isActionSidebarOpen,
{
@@ -57,35 +63,12 @@ const ActionSidebarContextProvider: FC = ({ children }) => {
actionSidebarUseRegisterOnBeforeCloseCallback,
registerContainerRef: actionSidebarRegisterContainerRef,
},
- ] = useToggle();
+ ] = useToggle({
+ onClose: removeTxParamOnClose,
+ });
const { trackEvent } = useAnalyticsContext();
const selectedDomain = useGetSelectedDomainFilter();
const selectedDomainNativeId = selectedDomain?.nativeId ?? '';
- const navigate = useNavigate();
- const [searchParams] = useSearchParams();
- const transactionId = searchParams?.get(TX_SEARCH_PARAM);
-
- const removeTxParamOnClose = useCallback(() => {
- // Remove the TX_SEARCH_PARAM search param only if it is present
- if (transactionId) {
- navigate(removeQueryParamFromUrl(window.location.href, TX_SEARCH_PARAM));
- }
- }, [navigate, transactionId]);
-
- actionSidebarUseRegisterOnBeforeCloseCallback((element) => {
- const isClickedInside = isElementInsideModalOrPortal(element);
- const navigationWrapper = getElementWithSelector('.modal-blur-navigation');
-
- if (
- !isClickedInside ||
- (isChildOf(navigationWrapper, element) && !isTablet)
- ) {
- return false;
- }
-
- removeTxParamOnClose();
- return undefined;
- });
const getSidebarInitialValues = useCallback(
(initialValues = {}) => ({
diff --git a/src/context/ActionSidebarContext/utils.ts b/src/context/ActionSidebarContext/utils.ts
index 19b19f4b715..38decca3e89 100644
--- a/src/context/ActionSidebarContext/utils.ts
+++ b/src/context/ActionSidebarContext/utils.ts
@@ -1,3 +1,5 @@
+import { isChildOf } from '~utils/checks/isChildOf.ts';
+import { getElementWithSelector } from '~utils/elements.ts';
import { getPortalContainer } from '~v5/shared/Portal/utils.ts';
export const isElementInsideModalOrPortal = (element: Element) => {
@@ -10,8 +12,24 @@ export const isElementInsideModalOrPortal = (element: Element) => {
getPortalContainer().contains(element) ||
reactModalPortals.some((portal) => portal.contains(element))
) {
- return false;
+ return true;
}
- return true;
+ return false;
+};
+
+export const isElementInsideDynamicXYZModal = (element: Element) => {
+ const dynamicWalletModal = getElementWithSelector('#dynamic-modal');
+ const dynamicSendTransactionModal = getElementWithSelector(
+ '#dynamic-send-transaction',
+ );
+ const dynamicSignMessageModal = getElementWithSelector(
+ '#dynamic-sign-message',
+ );
+
+ return [
+ dynamicWalletModal,
+ dynamicSendTransactionModal,
+ dynamicSignMessageModal,
+ ].some((modal) => isChildOf(modal, element));
};
diff --git a/src/context/AppContext/AppContext.ts b/src/context/AppContext/AppContext.ts
index bebc096450d..a2439a667e8 100644
--- a/src/context/AppContext/AppContext.ts
+++ b/src/context/AppContext/AppContext.ts
@@ -24,6 +24,7 @@ export interface AppContextValue {
joinedColonies: JoinedColony[];
joinedColoniesLoading: boolean;
refetchJoinedColonies: () => void;
+ willWalletAutoConnect: boolean;
}
export const AppContext = createContext(undefined);
diff --git a/src/context/AppContext/AppContextProvider.tsx b/src/context/AppContext/AppContextProvider.tsx
index d634397dd33..119c40f034b 100644
--- a/src/context/AppContext/AppContextProvider.tsx
+++ b/src/context/AppContext/AppContextProvider.tsx
@@ -1,3 +1,7 @@
+import {
+ useDynamicContext,
+ useDynamicEvents,
+} from '@dynamic-labs/sdk-react-core';
import { utils } from 'ethers';
import React, {
useState,
@@ -6,28 +10,49 @@ import React, {
useCallback,
useEffect,
} from 'react';
+import useLocalStorage from 'use-local-storage';
+import { deauthenticateWallet } from '~auth/index.ts';
+import { DEFAULT_NETWORK_INFO } from '~constants/index.ts';
import { useGetUserByAddressLazyQuery } from '~gql';
import useAsyncFunction from '~hooks/useAsyncFunction.ts';
import useJoinedColonies from '~hooks/useJoinedColonies.ts';
-import usePrevious from '~hooks/usePrevious.ts';
import { ActionTypes } from '~redux/index.ts';
-import { getLastWallet } from '~utils/autoLogin.ts';
+import retryProviderFactory from '~redux/sagas/wallet/RetryProvider.ts';
+import debugLogging from '~utils/debug/debugLogging.ts';
-import { getContext, ContextModule } from '../index.ts';
+import { getContext, ContextModule, setContext } from '../index.ts';
import { TokenActivationProvider } from '../TokenActivationContext/TokenActivationContextProvider.tsx';
import { AppContext, type AppContextValue } from './AppContext.ts';
+enum DynamicLocalStorageKeys {
+ CONNECTED_WALLETS = 'dynamic_connected_wallets',
+ CONNECTED_WALLET_NS = 'dynamic_connected_wallet_ns',
+ STORE = 'dynamic_store',
+ SOCIAL_STORAGE = 'dynamic_social_storage',
+ LAST_USED_WALLET = 'dynamic_last_used_wallet',
+ DEVICE_FINGERPRINT = 'dynamic_device_fingerprint',
+ CONTEXT_SESSION_SETTINGS = 'dynamic_context_session_settings',
+ TURNKEY_LAST_USED_CHAIN_ID = 'turnkey-last-used-chain-id',
+}
+
const AppContextProvider = ({ children }: { children: ReactNode }) => {
const [wallet, setWallet] = useState();
const [user, setUser] = useState();
const [userLoading, setUserLoading] = useState(false);
// We need to start with true here as we can't know whethere we are going to try to connect
// and the first render is important here
- const [walletConnecting, setWalletConnecting] = useState(true);
-
+ const [walletConnecting, setWalletConnecting] = useState(false);
+ const { setShowAuthFlow, handleLogOut, primaryWallet } = useDynamicContext();
const [getUserByAddress] = useGetUserByAddressLazyQuery();
+ const [autoConnectedWalletAddress] = useLocalStorage(
+ DynamicLocalStorageKeys.CONNECTED_WALLET_NS,
+ undefined,
+ );
+ const [willWalletAutoConnect, setWillWalletAutoConnect] = useState(
+ autoConnectedWalletAddress || false,
+ );
const {
joinedColonies,
@@ -43,17 +68,20 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
setUserLoading(true);
}
- const { data } = await getUserByAddress({
- variables: {
- address: utils.getAddress(address),
- },
- fetchPolicy: 'network-only',
- });
- const [currentUser] = data?.getUserByAddress?.items || [];
- if (currentUser) {
- setUser(currentUser);
- } else {
- setUser(null);
+ // Only request new user data if the wallet actually changed
+ if (user?.walletAddress !== address) {
+ const { data } = await getUserByAddress({
+ variables: {
+ address: utils.getAddress(address),
+ },
+ fetchPolicy: 'network-only',
+ });
+ const [currentUser] = data?.getUserByAddress?.items || [];
+ if (currentUser) {
+ setUser(currentUser);
+ } else {
+ setUser(null);
+ }
}
} catch (error) {
console.error(error);
@@ -62,22 +90,56 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
}
}
},
- [getUserByAddress],
+ [getUserByAddress, user],
);
- const updateWallet = useCallback(() => {
+ const updateWallet = useCallback(async () => {
try {
- const updatedWallet = getContext(ContextModule.Wallet);
- updatedWallet.address = utils.getAddress(updatedWallet.address);
- setWallet(updatedWallet);
- // Update the user as soon as the wallet address changes
- if (updatedWallet.address !== wallet?.address) {
- updateUser(updatedWallet.address);
+ if (primaryWallet) {
+ setWalletConnecting(true);
+
+ // Both methods exist as described by the documentation
+ // https://docs.dynamic.xyz/wallets/using-wallets/evm/evm-wallets#ethereum-wallet-methods
+ // and as supported by the code actually working
+ // I suspect their types are the ones not working properly here
+ // @ts-ignore
+ const publicClient = await primaryWallet.getPublicClient();
+ // @ts-ignore
+ const walletClient = await primaryWallet.getWalletClient();
+
+ const walletAddress = utils.getAddress(primaryWallet.address);
+
+ const RetryProvider = retryProviderFactory(
+ walletClient.transport,
+ walletAddress,
+ );
+ const provider = new RetryProvider();
+
+ const dynamicWallet = {
+ ...walletClient,
+ publicClient,
+ ethersProvider: provider,
+ provider,
+ primaryWallet,
+ address: walletAddress,
+ label: primaryWallet.key,
+ chains: [publicClient.chain, walletClient.chain],
+ };
+
+ debugLogging('WALLET SETTING CONTEXT', dynamicWallet);
+
+ setContext(ContextModule.Wallet, dynamicWallet);
+
+ setWallet(dynamicWallet);
+
+ setWillWalletAutoConnect(true);
+
+ setWalletConnecting(false);
}
} catch (error) {
// It means that it was not set in context yet
}
- }, [updateUser, wallet]);
+ }, [primaryWallet]);
const setupUserContext = useAsyncFunction({
submit: ActionTypes.WALLET_OPEN,
@@ -91,20 +153,53 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
success: ActionTypes.USER_LOGOUT_SUCCESS,
});
+ const setUserContextWithErrorFallback = useCallback(async () => {
+ try {
+ // Temporary! (Like all other permanant solutions)
+ // This will put the app in a loading state preventing the user from interacting
+ // with anything until they have authenticated with the auth proxy
+ setWalletConnecting(true);
+ await setupUserContext(undefined);
+ setWalletConnecting(false);
+ } catch (error) {
+ // In case of error clear out the wallet and user (in the local state)
+ // All the others are cleared by the `userLogout` saga call which is called
+ // automatically if the `setupUserContext` saga fails
+ setWalletConnecting(true);
+ await handleLogOut();
+ setWallet(null);
+ setUser(null);
+ setWalletConnecting(false);
+ }
+ }, [setupUserContext, setWallet, setUser, setWalletConnecting, handleLogOut]);
+
+ const clearDynamicWalletStorage = useCallback(() => {
+ localStorage.removeItem(DynamicLocalStorageKeys.STORE);
+ localStorage.removeItem(DynamicLocalStorageKeys.SOCIAL_STORAGE);
+ localStorage.removeItem(DynamicLocalStorageKeys.LAST_USED_WALLET);
+ localStorage.removeItem(DynamicLocalStorageKeys.DEVICE_FINGERPRINT);
+ localStorage.removeItem(DynamicLocalStorageKeys.CONTEXT_SESSION_SETTINGS);
+ localStorage.removeItem(DynamicLocalStorageKeys.TURNKEY_LAST_USED_CHAIN_ID);
+ debugLogging('WALLET STORAGE CLEARED');
+ }, []);
+
+ const clearDynamicWalletAutoLogin = useCallback(() => {
+ localStorage.removeItem(DynamicLocalStorageKeys.CONNECTED_WALLETS); // will not exist for embedded wallets
+ localStorage.removeItem(DynamicLocalStorageKeys.CONNECTED_WALLET_NS);
+ debugLogging('WALLET AUTOLOGIN CLEARED');
+ }, []);
+
/*
- * Handle wallet connection
+ * Manually Trigger Wallet Connection by pressing the "Connect Wallet" button
*/
const connectWallet = useCallback(async () => {
try {
- await setupUserContext(undefined);
- setWalletConnecting(true);
- updateWallet();
+ clearDynamicWalletStorage();
+ setShowAuthFlow(true);
} catch (error) {
- console.error('Could not connect wallet', error);
- } finally {
- setWalletConnecting(false);
+ debugLogging('WALLET COULD NOT BE CONNECTED', error);
}
- }, [setupUserContext, updateWallet, setWalletConnecting]);
+ }, [clearDynamicWalletStorage, setShowAuthFlow]);
/*
* Handle wallet disconnection
@@ -112,7 +207,13 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
const disconnectWallet = useCallback(
async ({ shouldRemoveWalletContext = true } = {}) => {
try {
+ setWalletConnecting(true);
+ clearDynamicWalletAutoLogin();
+ setWillWalletAutoConnect(false);
await userLogout({ shouldRemoveWalletContext });
+ await handleLogOut();
+
+ setWalletConnecting(false);
} catch (error) {
console.error('Could not disconnect wallet', error);
return;
@@ -120,59 +221,124 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
setWallet(null);
setUser(null);
},
- [setWallet, setUser, userLogout],
+ [clearDynamicWalletAutoLogin, userLogout, handleLogOut],
);
- /*
- * When the user switches account in Metamask, re-initiate the wallet connect flow
- * so as to update their wallet details in the app's memory.
- */
- const handleAccountChange = useCallback(async () => {
- // @ts-ignore
- const accounts = await window.ethereum.request({
- method: 'eth_accounts',
- });
- const loggedInAccount = accounts[0];
- await disconnectWallet({ shouldRemoveWalletContext: false });
- if (loggedInAccount) {
- connectWallet();
+ // Embedded Wallet Logout
+ useDynamicEvents('logout', async (...args) => {
+ // Only log out if a wallet is set, and if that wallet is embedded
+ if (wallet?.label === 'turnkeyhd') {
+ debugLogging('WALLET EMBEDDED LOGOUT', args);
+ await disconnectWallet();
}
- }, [connectWallet, disconnectWallet]);
-
- const previousAccountChange = usePrevious(handleAccountChange);
+ });
+ // Handle wallet connected
useEffect(() => {
- if (window.ethereum) {
- if (previousAccountChange) {
- // @ts-ignore
- window.ethereum.removeListener(
- 'accountsChanged',
- previousAccountChange,
- );
- }
- // @ts-ignore
- window.ethereum.on('accountsChanged', handleAccountChange);
- }
+ const walletHandler = async () => {
+ if (primaryWallet) {
+ const primaryWalletAddress = utils.getAddress(primaryWallet.address);
+ let contextWallet;
- return () => {
- if (window.ethereum) {
- // @ts-ignore
- window.ethereum.removeListener('accountsChanged', handleAccountChange);
+ try {
+ contextWallet = getContext(ContextModule.Wallet);
+ } catch (error) {
+ // no wallet in context
+ }
+
+ if (!contextWallet) {
+ if (primaryWallet?.connector.supportsNetworkSwitching()) {
+ try {
+ const currentPrimaryWalletChainId =
+ await primaryWallet.getNetwork();
+ const requiredChainId = parseInt(
+ DEFAULT_NETWORK_INFO.chainId,
+ 10,
+ );
+ // Only swith the chain if not already on it
+ if (currentPrimaryWalletChainId !== requiredChainId) {
+ await primaryWallet.switchNetwork(
+ parseInt(DEFAULT_NETWORK_INFO.chainId, 10),
+ );
+ debugLogging(
+ 'WALLET AUTOMATICALLY SWITCHED NETWORK',
+ currentPrimaryWalletChainId,
+ parseInt(DEFAULT_NETWORK_INFO.chainId, 10),
+ );
+ }
+ } catch (error) {
+ debugLogging('WALLET AUTOMATIC NETWORK SWITCH FAILED', error);
+ }
+ }
+
+ await updateWallet();
+
+ await updateUser(primaryWalletAddress);
+
+ await setUserContextWithErrorFallback();
+
+ return;
+ }
+
+ // App crashed an have to recover
+ if (contextWallet?.address === primaryWalletAddress && !wallet) {
+ await updateWallet();
+
+ await updateUser(primaryWalletAddress);
+
+ await setUserContextWithErrorFallback();
+
+ debugLogging('WALLET RECOVER AFTER CRASH', {
+ primaryWallet,
+ contextWallet,
+ wallet,
+ });
+ }
}
};
- }, [handleAccountChange, previousAccountChange]);
+ walletHandler();
+ }, [
+ primaryWallet,
+ setUserContextWithErrorFallback,
+ updateUser,
+ updateWallet,
+ wallet,
+ ]);
+
+ // Network changed
+ useDynamicEvents('primaryWalletNetworkChanged', async (...args) => {
+ debugLogging('WALLET NETWORK CHANGED', args);
+ await updateWallet();
+ });
+ // Wallet address changed
useEffect(() => {
- if (getLastWallet()) {
- connectWallet();
- } else {
- setWalletConnecting(false);
- }
+ const watchForWalletChange = async () => {
+ if (primaryWallet && wallet) {
+ const primaryWalletAddress = utils.getAddress(primaryWallet.address);
+ if (primaryWalletAddress !== wallet.address) {
+ debugLogging('WALLET ADDRESS CHANGED', primaryWallet);
- // NOTE: We really want this to run exactly once (when the app starts)
- // connectWallet is dependent on the wallet itself, so this is to avoid an infinite loop
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ setWalletConnecting(true);
+
+ await deauthenticateWallet();
+
+ await updateWallet();
+
+ await updateUser(primaryWalletAddress);
+
+ await setUserContextWithErrorFallback();
+ }
+ }
+ };
+ watchForWalletChange();
+ }, [
+ primaryWallet,
+ setUserContextWithErrorFallback,
+ updateUser,
+ updateWallet,
+ wallet,
+ ]);
const appContext = useMemo(
() => ({
@@ -188,6 +354,7 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
joinedColonies,
joinedColoniesLoading,
refetchJoinedColonies,
+ willWalletAutoConnect,
}),
[
wallet,
@@ -201,6 +368,7 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
joinedColonies,
joinedColoniesLoading,
refetchJoinedColonies,
+ willWalletAutoConnect,
],
);
diff --git a/src/hooks/useToggle/index.ts b/src/hooks/useToggle/index.ts
index 8ea5560e9a3..00620eba275 100644
--- a/src/hooks/useToggle/index.ts
+++ b/src/hooks/useToggle/index.ts
@@ -60,9 +60,11 @@ const documentMousedownHandler = (event: MouseEvent): void => {
const useToggle = ({
defaultToggleState = false,
closeOnRouteChange = false,
+ onClose,
}: {
defaultToggleState?: boolean;
closeOnRouteChange?: boolean;
+ onClose?: () => void;
} = {}): UseToggleReturnType => {
const [toggleState, setToggleState] = useState(defaultToggleState);
const onBeforeCloseCallbacksRef = useRef([]);
@@ -71,15 +73,22 @@ const useToggle = ({
const toggle = useCallback(() => {
setTimeout(() => {
- setToggleState((currentToggleState) => !currentToggleState);
+ setToggleState((currentToggleState) => {
+ if (currentToggleState) {
+ onClose?.();
+ }
+
+ return !currentToggleState;
+ });
});
- }, []);
+ }, [onClose]);
const toggleOff = useCallback(() => {
setTimeout(() => {
setToggleState(false);
+ onClose?.();
});
- }, []);
+ }, [onClose]);
const toggleOn = useCallback(() => {
setTimeout(() => {
diff --git a/src/i18n/en.json b/src/i18n/en.json
index c223a1af775..4c9465ead89 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -681,7 +681,6 @@
"userInfo.colonyReputation.product": "Product",
"userInfo.top.contributor.in": "Top contributor in:",
"userMenu.optionsTitle": "options",
- "userMenu.getStartedTitle": "Get started",
"userMenu.guidedToursTitle": "Guided tours",
"userMenu.contactAndSupportTitle": "Contact and support",
"userMenu.developersTitle": "Developers",
diff --git a/src/images/icons/hardhat.svg b/src/images/icons/hardhat.svg
new file mode 100644
index 00000000000..c7fcb1b3cc8
--- /dev/null
+++ b/src/images/icons/hardhat.svg
@@ -0,0 +1 @@
+
diff --git a/src/redux/sagas/messages/index.ts b/src/redux/sagas/messages/index.ts
index 13abbe2543a..98daf26dc99 100644
--- a/src/redux/sagas/messages/index.ts
+++ b/src/redux/sagas/messages/index.ts
@@ -4,17 +4,12 @@ import { call, put } from 'redux-saga/effects';
import { ContextModule, getContext } from '~context/index.ts';
import { ActionTypes } from '~redux/actionTypes.ts';
import { type AllActions } from '~redux/types/actions/index.ts';
-import { isFullWallet } from '~types/wallet.ts';
import { putError, initiateMessageSigning } from '../utils/index.ts';
export function* signMessage(purpose, message) {
const wallet = getContext(ContextModule.Wallet);
- if (!isFullWallet(wallet)) {
- throw new Error('Background login not yet completed.');
- }
-
const messageId = `${nanoid(10)}-signMessage`;
/*
* @NOTE Initiate the message signing process
diff --git a/src/redux/sagas/setupUserContext.ts b/src/redux/sagas/setupUserContext.ts
index b547177cc56..cc219ad0361 100644
--- a/src/redux/sagas/setupUserContext.ts
+++ b/src/redux/sagas/setupUserContext.ts
@@ -3,11 +3,8 @@ import { all, call, fork, put } from 'redux-saga/effects';
// import AppLoadingState from '~context/appLoadingState';
import { authenticateWallet } from '~auth/index.ts';
-import { getContext, setContext, ContextModule } from '~context/index.ts';
+import { getContext, ContextModule } from '~context/index.ts';
import { failPendingTransactions } from '~state/transactionState.ts';
-import { type ColonyWallet } from '~types/wallet.ts';
-import { getLastWallet, type LastWallet } from '~utils/autoLogin.ts';
-import { createAddress } from '~utils/web3/index.ts';
import { ActionTypes } from '../actionTypes.ts';
import { type AllActions } from '../types/actions/index.ts';
@@ -24,27 +21,7 @@ import multiSigSagas from './multiSig/index.ts';
import setupTransactionsSaga from './transactions/transactionsToDb.ts';
import { setupUsersSagas, userLogout } from './users/index.ts';
import { getGasPrices, putError } from './utils/index.ts';
-import { getWallet } from './wallet/index.ts';
// import vestingSagas from './vesting';
-import getOnboard from './wallet/onboard.ts';
-
-const ONBOARD_METAMASK_WALLET_LABEL = 'MetaMask';
-
-const getMetamaskAddress = async () => {
- // try/catch just in case createAddress errors
- try {
- if (window.ethereum) {
- // @ts-ignore
- const accounts = await window.ethereum.request({
- method: 'eth_accounts',
- });
- return createAddress(accounts[0]);
- }
- } catch {
- // silent
- }
- return undefined;
-};
function* setupContextDependentSagas() {
// const appLoadingState: typeof AppLoadingState = AppLoadingState;
@@ -69,9 +46,7 @@ function* setupContextDependentSagas() {
]);
}
-function* initializeFullWallet(lastWallet: LastWallet | null) {
- const wallet = yield call(getWallet, lastWallet);
- setContext(ContextModule.Wallet, wallet);
+function* initializeFullWallet() {
// We're forking the next one as we don't really need to wait for it
yield call(getGasPrices);
yield call(authenticateWallet);
@@ -85,14 +60,7 @@ function* initializeFullWallet(lastWallet: LastWallet | null) {
*/
export function* setupUserContext() {
try {
- /* Instantiate the onboard object and load into context */
- const onboard = yield getOnboard();
- setContext(ContextModule.Onboard, onboard);
- /*
- * Get the new wallet and set it in context.
- */
-
- let wallet: ColonyWallet | undefined;
+ let wallet;
try {
wallet = getContext(ContextModule.Wallet);
@@ -100,22 +68,7 @@ export function* setupUserContext() {
// wallet not seen in context yet
}
- const lastWallet = getLastWallet();
- const selectedMetamaskAddress = yield getMetamaskAddress();
-
- /*
- * If the wallet we've pulled from context does not have the same address as the selected account
- * in Metamask, it's because the user just switched their account in metamask.
- * In this case we just logout previous user and disconnect wallet.
- */
- if (
- !wallet &&
- lastWallet &&
- selectedMetamaskAddress &&
- lastWallet.address.toLocaleLowerCase() !==
- selectedMetamaskAddress.toLocaleLowerCase() &&
- lastWallet.type === ONBOARD_METAMASK_WALLET_LABEL
- ) {
+ if (!wallet) {
return yield putError(
ActionTypes.WALLET_OPEN_ERROR,
Error(
@@ -125,7 +78,7 @@ export function* setupUserContext() {
);
}
- yield call(initializeFullWallet, lastWallet);
+ yield call(initializeFullWallet);
yield put({
type: ActionTypes.WALLET_OPEN_SUCCESS,
@@ -139,18 +92,6 @@ export function* setupUserContext() {
*/
yield fork(setupContextDependentSagas);
- // const userContext = {
- // apolloClient,
- // colonyManager,
- // wallet,
- // };
- // yield setupResolvers(apolloClient, userContext);
-
- /*
- * Get the network contract values from the resolver
- */
- // yield updateNetworkContracts();
-
yield put({
type: ActionTypes.USER_CONTEXT_SETUP_SUCCESS,
});
diff --git a/src/redux/sagas/transactions/getMetatransactionPromise.ts b/src/redux/sagas/transactions/getMetatransactionPromise.ts
index e3a9cff7b11..bbb9fd03421 100644
--- a/src/redux/sagas/transactions/getMetatransactionPromise.ts
+++ b/src/redux/sagas/transactions/getMetatransactionPromise.ts
@@ -11,7 +11,6 @@ import {
TRANSACTION_METHODS,
ExtendedClientType,
} from '~types/transactions.ts';
-import { isFullWallet } from '~types/wallet.ts';
import { getChainId } from '~utils/chainId.ts';
import debugLogging from '~utils/debug/debugLogging.ts';
import {
@@ -32,10 +31,6 @@ async function getMetatransactionPromise(
): Promise {
const wallet = getContext(ContextModule.Wallet);
- if (!isFullWallet(wallet)) {
- throw new Error('Background login not yet completed.');
- }
-
const signer = wallet.ethersProvider.getSigner();
let colonyManager;
diff --git a/src/redux/sagas/users/index.ts b/src/redux/sagas/users/index.ts
index aefa33e7afb..b444a3da5c3 100644
--- a/src/redux/sagas/users/index.ts
+++ b/src/redux/sagas/users/index.ts
@@ -24,7 +24,7 @@ import { ActionTypes } from '~redux/actionTypes.ts';
import { type Action, type AllActions } from '~redux/types/actions/index.ts';
import { LANDING_PAGE_ROUTE } from '~routes/index.ts';
import { TRANSACTION_METHODS } from '~types/transactions.ts';
-import { clearLastWallet, getLastWallet } from '~utils/autoLogin.ts';
+import { clearLastWallet } from '~utils/autoLogin.ts';
import {
createGroupTransaction,
@@ -198,15 +198,10 @@ export function* userLogout(data?: UserLogoutParams) {
try {
removeContext(ContextModule.ColonyManager);
const apolloClient = getContext(ContextModule.ApolloClient);
- let walletLabel;
- try {
- const wallet = getContext(ContextModule.Wallet);
- walletLabel = wallet.label;
- } catch {
- const lastWallet = getLastWallet();
- walletLabel = lastWallet?.type;
+ if (shouldRemoveWalletContext) {
+ removeContext(ContextModule.Wallet);
}
- disconnectWallet(walletLabel, shouldRemoveWalletContext);
+ clearLastWallet(); // only for legacy purposes
yield deauthenticateWallet();
apolloClient.resetStore();
yield put({
diff --git a/src/redux/sagas/utils/getGasPrices.ts b/src/redux/sagas/utils/getGasPrices.ts
index c1775405a27..4f8d7bad9b4 100644
--- a/src/redux/sagas/utils/getGasPrices.ts
+++ b/src/redux/sagas/utils/getGasPrices.ts
@@ -63,9 +63,10 @@ const fetchGasPrices = async (): Promise => {
);
}
- const rawNetworkGasPrice = await userWallet.provider.request({
- method: RpcMethods.GasPrice,
- });
+ const rawNetworkGasPrice = await userWallet.ethersProvider.send(
+ RpcMethods.GasPrice,
+ );
+
defaultGasPrices.network = BigNumber.from(rawNetworkGasPrice);
let response;
diff --git a/src/redux/sagas/utils/getNetworkClient.ts b/src/redux/sagas/utils/getNetworkClient.ts
index 80b797efb12..2d3fbd05116 100644
--- a/src/redux/sagas/utils/getNetworkClient.ts
+++ b/src/redux/sagas/utils/getNetworkClient.ts
@@ -7,7 +7,6 @@ import {
import { DEFAULT_NETWORK } from '~constants/index.ts';
import { ContextModule, getContext } from '~context/index.ts';
import { ColonyJSNetworkMapping, Network } from '~types/network.ts';
-import { isFullWallet } from '~types/wallet.ts';
import type { SignerOrProvider } from '@colony/colony-js';
@@ -17,10 +16,6 @@ import type { SignerOrProvider } from '@colony/colony-js';
const getNetworkClient = async (signerOrProvider?: SignerOrProvider) => {
const wallet = getContext(ContextModule.Wallet);
- if (!isFullWallet(wallet)) {
- throw new Error('Background login not yet completed.');
- }
-
const network = DEFAULT_NETWORK;
const signer = signerOrProvider || wallet.ethersProvider.getSigner();
diff --git a/src/redux/sagas/utils/metatransactions.ts b/src/redux/sagas/utils/metatransactions.ts
index d44efc6fb17..55211cb4462 100644
--- a/src/redux/sagas/utils/metatransactions.ts
+++ b/src/redux/sagas/utils/metatransactions.ts
@@ -2,7 +2,6 @@ import { type BigNumberish, utils, type TypedDataField } from 'ethers';
import { ContextModule, getContext } from '~context/index.ts';
import { type Address } from '~types/index.ts';
-import { isFullWallet } from '~types/wallet.ts';
import { generateBroadcasterHumanReadableError } from './errorMessages.ts';
@@ -22,10 +21,6 @@ export const signTypedData = async ({
}> => {
const wallet = getContext(ContextModule.Wallet);
- if (!isFullWallet(wallet)) {
- throw new Error('Background login not yet completed.');
- }
-
const signer = wallet.ethersProvider.getSigner();
// eslint-disable-next-line no-underscore-dangle
const signature = await signer._signTypedData(domain, types, message);
diff --git a/src/redux/sagas/wallet/RetryProvider.ts b/src/redux/sagas/wallet/RetryProvider.ts
index bca597a9211..84f823c78c3 100644
--- a/src/redux/sagas/wallet/RetryProvider.ts
+++ b/src/redux/sagas/wallet/RetryProvider.ts
@@ -1,8 +1,6 @@
-import { type Block } from '@ethersproject/providers';
import { providers, utils } from 'ethers';
import { backOff } from 'exponential-backoff';
-import { GANACHE_LOCAL_RPC_URL } from '~constants/index.ts';
import { type Address } from '~types/index.ts';
import {
RetryProviderMethod,
@@ -14,17 +12,8 @@ type RetryProviderOptions = {
delay?: number; // in milliseconds
};
-const classFactory = (
- walletType: 'MetaMask' | string = '',
- walletAddress?: Address,
-) => {
- const devWallet = walletType !== 'MetaMask';
-
- const Extender = devWallet
- ? providers.JsonRpcProvider
- : providers.Web3Provider;
-
- return class RetryRpcProvider extends Extender {
+const classFactory = (transport, walletAddress?: Address) => {
+ return class RetryRpcProvider extends providers.Web3Provider {
attempts: number;
delay: number;
@@ -32,7 +21,7 @@ const classFactory = (
bypassedMethods: string[];
constructor(options?: RetryProviderOptions) {
- super(devWallet ? GANACHE_LOCAL_RPC_URL : window.ethereum);
+ super(transport);
this.attempts = options?.attempts || 5;
this.delay = options?.delay || 1000;
this.bypassedMethods = [
@@ -55,10 +44,6 @@ const classFactory = (
return true;
}
- getBlock(blockHashOrBlockTag?: string | number): Promise {
- return super.getBlock(blockHashOrBlockTag);
- }
-
getSigner(
// If custom address not provided, return the signer of the wallet
addressOrIndex: string | number | undefined = walletAddress,
diff --git a/src/redux/sagas/wallet/onboard.ts b/src/redux/sagas/wallet/onboard.ts
index bfe058d7a0d..8731d077625 100644
--- a/src/redux/sagas/wallet/onboard.ts
+++ b/src/redux/sagas/wallet/onboard.ts
@@ -1,5 +1,5 @@
import Onboard, { type InitOptions } from '@web3-onboard/core';
-import injectedWallets from '@web3-onboard/injected-wallets';
+import injectedWalletsModule from '@web3-onboard/injected-wallets';
import {
TERMS_AND_CONDITIONS,
@@ -7,6 +7,9 @@ import {
TOKEN_DATA,
GANACHE_NETWORK,
GANACHE_LOCAL_RPC_URL,
+ ARBITRUM_NETWORK,
+ ARBITRUM_SEPOLIA_NETWORK,
+ GNOSIS_NETWORK,
} from '~constants/index.ts';
import { Network } from '~types/network.ts';
import { getChainIdAsHex } from '~utils/chainId.ts';
@@ -16,7 +19,7 @@ import ganacheModule from './ganacheModule.ts';
const { formatMessage } = intl({
'metadata.name': 'Colony App',
- 'metadata.description': `Logging into your Colony is done using your wallet. You’ll be able to perform actions, contribute, and make use of any earned reputation.`,
+ 'metadata.description': `Logging into your Colony is done using your wallet. You'll be able to perform actions, contribute, and make use of any earned reputation.`,
'info.text': 'Connect your wallet to log in',
});
@@ -47,23 +50,11 @@ const getDevelopmentWallets = async () => {
return [];
};
-// chains: [
-// {
-// /*
-// * chain id for @web3-onboard needs to be expressed as a hex string
-// */
-// // id: `0x${GANACHE_NETWORK.chainId.toString(16)}`,
-// id: '0x64',
-// token: TOKEN_DATA[Network.Gnosis].symbol,
-// label: 'Metamask Wallet',
-// rpcUrl: 'https://rpc.gnosischain.com',
-// },
-// ],
-
const onboardConfig: InitOptions = {
- wallets: [injectedWallets()],
+ wallets: [injectedWalletsModule()],
// Chains array only used in `ganacheModule` for use in development.
chains: [
+ // local
{
// web3-onboard formats chain id as hex strings
id: getChainIdAsHex(GANACHE_NETWORK.chainId),
@@ -71,6 +62,27 @@ const onboardConfig: InitOptions = {
label: GANACHE_NETWORK.shortName,
rpcUrl: GANACHE_LOCAL_RPC_URL,
},
+ // arbitrum
+ {
+ // web3-onboard formats chain id as hex strings
+ id: getChainIdAsHex(ARBITRUM_NETWORK.chainId),
+ token: TOKEN_DATA[Network.ArbitrumOne].symbol,
+ label: ARBITRUM_NETWORK.shortName,
+ },
+ // arbitrum sepolia (testnet)
+ {
+ // web3-onboard formats chain id as hex strings
+ id: getChainIdAsHex(ARBITRUM_SEPOLIA_NETWORK.chainId),
+ token: TOKEN_DATA[Network.ArbitrumSepolia].symbol,
+ label: ARBITRUM_SEPOLIA_NETWORK.shortName,
+ },
+ // gnosis
+ {
+ // web3-onboard formats chain id as hex strings
+ id: getChainIdAsHex(GNOSIS_NETWORK.chainId),
+ token: TOKEN_DATA[Network.Gnosis].symbol,
+ label: GNOSIS_NETWORK.shortName,
+ },
],
accountCenter: {
desktop: { enabled: false },
diff --git a/src/styles/dynamicOverrides.ts b/src/styles/dynamicOverrides.ts
new file mode 100644
index 00000000000..5ec9889822e
--- /dev/null
+++ b/src/styles/dynamicOverrides.ts
@@ -0,0 +1,11 @@
+export const dynamicCssOverridesDark = `
+ .badge__container__primary {
+ background-color: var(--dynamic-badge-dot-background) !important;
+ }
+`;
+
+export const dynamicCssOverridesLight = `
+ .badge__container__primary {
+ background-color: #eff8ff !important;
+ }
+`;
diff --git a/src/styles/main.css b/src/styles/main.css
index a6f8fcdaee2..8f2304d7a6a 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -116,4 +116,11 @@ body.ReactModal__Body--open:not(.show-header-in-modal) .modal-blur-navigation {
--onboard-black: var(--color-base-black);
--onboard-shadow-2: transparent;
--onboard-modal-z-index: 9999;
+
+ /* wallet connect modal */
+ --wcm-overlay-background-color: transparent !important;
+ --wcm-z-index: 10000 !important;
+
+ /* trezor wallet select modal */
+ --onboard-account-select-modal-z-index: 10000 !important;
}
diff --git a/src/types/wallet.ts b/src/types/wallet.ts
index c14014c0ba1..8bcc6f1290d 100644
--- a/src/types/wallet.ts
+++ b/src/types/wallet.ts
@@ -6,7 +6,7 @@ import retryRpcProviderFactory from '~redux/sagas/wallet/RetryProvider.ts';
export type ColonyWallet = BasicWallet | FullWallet;
-const RetryProvider = retryRpcProviderFactory();
+const RetryProvider = retryRpcProviderFactory(undefined);
export interface FullWallet extends WalletState, Account {
ethersProvider: InstanceType;
diff --git a/src/utils/hoc/index.ts b/src/utils/hoc/index.ts
index 55e46fe63c6..520720698b5 100644
--- a/src/utils/hoc/index.ts
+++ b/src/utils/hoc/index.ts
@@ -8,5 +8,5 @@ export const withForwardingRef = >(
BaseComponent: ComponentType,
) =>
forwardRef((props, ref) =>
- createElement(BaseComponent, { ...props, forwardedRef: ref }),
+ createElement(BaseComponent, { ...(props as Props), forwardedRef: ref }),
);