Files
tom goriunovandGitHub 401dc58402 Smart wallet auth (#3220)
* basic login button implementation

* refactoring and add dynamic auth button to the other pages

* connect wallet without authenticating an user

* login and logout user in blockscout account

* refactoring and RPC overrides

* authenticate user with connected wallet

* add ENS to profile button

* override evm chains and fix contract write with waas wallet

* disconnect wallet and fix docker build

* add dynamic imports to components that use web3 wallet packages

* refactor user profile directory

* clean up

* tweaks

* update pw ci script

* fix "connect wallet" button behavior

* handle linking email to profile with dynamic provider

* fix test

* async loading web3 hooks and remove dynamic loading from components that use them

* fix pw tests

* add account navigation in horizontal layout when auth provider is dynamic

* fix address verification modal

* review fixes

* spelling fixes
2026-02-18 19:42:44 +01:00

116 lines
3.4 KiB
TypeScript

import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';
import type { DynamicContextProps, OnAuthSuccess } from '@dynamic-labs/sdk-react-core';
import { DynamicContextProvider, getAuthToken } from '@dynamic-labs/sdk-react-core';
import { DynamicWagmiConnector } from '@dynamic-labs/wagmi-connector';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { UserInfo } from 'types/api/account';
import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch';
import { getResourceKey } from 'lib/api/useApiQuery';
import getErrorMessage from 'lib/errors/getErrorMessage';
import useGetCsrfToken from 'lib/hooks/useGetCsrfToken';
import * as mixpanel from 'lib/mixpanel/index';
import { chains } from 'lib/web3/chains';
import { toaster } from 'toolkit/chakra/toaster';
import { castToString } from 'toolkit/utils/guards';
import useLogout from 'ui/snippets/auth/useLogout';
import WagmiProvider from './WagmiProvider';
const feature = config.features.blockchainInteraction;
const walletConnectors = [
EthereumWalletConnectors,
];
const overrides: DynamicContextProps['settings']['overrides'] = {
evmNetworks: chains.map((chain) => {
const logoUrl = castToString(chain.custom?.logoUrl);
return {
chainId: chain.id,
name: chain.name,
rpcUrls: chain.rpcUrls.default.http.slice(),
nativeCurrency: chain.nativeCurrency,
blockExplorerUrls: chain.blockExplorers ? [ chain.blockExplorers.default.url ] : [],
networkId: chain.id,
iconUrls: logoUrl ? [ logoUrl ] : [],
isTestnet: chain.testnet,
};
}),
};
interface Props {
children: React.ReactNode;
}
const DynamicProvider = ({ children }: Props) => {
const apiFetch = useApiFetch();
const queryClient = useQueryClient();
const csrfQuery = useGetCsrfToken();
const onLogout = useLogout();
const onAuthSuccess: OnAuthSuccess = React.useCallback(async() => {
try {
const authToken = getAuthToken();
const response = await apiFetch<'general:auth_dynamic', UserInfo, unknown>('general:auth_dynamic', {
fetchParams: {
headers: {
Authorization: `Bearer ${ authToken }`,
},
},
});
if (!('name' in response)) {
throw Error('Something went wrong');
}
queryClient.setQueryData(getResourceKey('general:user_info'), () => response);
csrfQuery.refetch();
mixpanel.logEvent(mixpanel.EventTypes.LOGIN, {
Action: 'Success',
Source: 'Dynamic',
});
} catch (error) {
toaster.error({
title: 'Error',
description: getErrorMessage(error),
});
}
}, [ apiFetch, csrfQuery, queryClient ]);
const settings: DynamicContextProps['settings'] = React.useMemo(() => {
const environmentId = feature.isEnabled && feature.connectorType === 'dynamic' ? feature.dynamic.environmentId : undefined;
if (!environmentId) {
throw new Error('Dynamic environment ID is not set');
}
return {
walletConnectors,
environmentId,
useMetamaskSdk: false,
overrides,
events: {
onAuthSuccess,
onLogout,
},
};
}, [ onAuthSuccess, onLogout ]);
return (
<DynamicContextProvider
settings={ settings }
>
<WagmiProvider>
<DynamicWagmiConnector>
{ children }
</DynamicWagmiConnector>
</WagmiProvider>
</DynamicContextProvider>
);
};
export default React.memo(DynamicProvider);