From a27250031bcb2c90121aa2770687a3f764e9a23c Mon Sep 17 00:00:00 2001
From: Usame Algan <5880855+usame-algan@users.noreply.github.com>
Date: Tue, 14 Nov 2023 16:32:07 +0100
Subject: [PATCH] fix: Lazy load mpc core kit (#2808)
* fix: Lazy load mpc core kit
* fix: Only instantiate service once, extract address book function
* fix: Add skeleton for socialSigner loading state
* chore: Update bundle-analyzer action to update comment instead of creating new one
---
.github/workflows/nextjs_bundle_analysis.yml | 41 +-----
.gitignore | 3 +-
.../common/ConnectWallet/WalletDetails.tsx | 9 +-
src/components/common/SocialSigner/index.tsx | 7 +-
src/components/welcome/WelcomeLogin/index.tsx | 8 +-
.../wallets/mpc/__tests__/useMPC.test.ts | 134 +++++------------
src/hooks/wallets/mpc/useMPC.ts | 139 +++++++++---------
.../wallets/mpc/useRehydrateSocialWallet.ts | 68 +++++++++
src/hooks/wallets/mpc/useSocialWallet.ts | 56 +------
src/pages/_app.tsx | 4 +-
src/services/mpc/SocialLoginModule.ts | 24 +--
11 files changed, 219 insertions(+), 274 deletions(-)
create mode 100644 src/hooks/wallets/mpc/useRehydrateSocialWallet.ts
diff --git a/.github/workflows/nextjs_bundle_analysis.yml b/.github/workflows/nextjs_bundle_analysis.yml
index 2e9672b0df..1840f8d380 100644
--- a/.github/workflows/nextjs_bundle_analysis.yml
+++ b/.github/workflows/nextjs_bundle_analysis.yml
@@ -28,8 +28,6 @@ jobs:
with:
secrets: ${{ toJSON(secrets) }}
- # Here's the first place where next-bundle-analysis' own script is used
- # This step pulls the raw bundle stats for the current bundle
- name: Analyze bundle
run: npx -p nextjs-bundle-analysis report
@@ -47,19 +45,6 @@ jobs:
branch: ${{ github.event.pull_request.base.ref }}
path: .next/analyze/base
- # And here's the second place - this runs after we have both the current and
- # base branch bundle stats, and will compare them to determine what changed.
- # There are two configurable arguments that come from package.json:
- #
- # - budget: optional, set a budget (bytes) against which size changes are measured
- # it's set to 350kb here by default, as informed by the following piece:
- # https://infrequently.org/2021/03/the-performance-inequality-gap/
- #
- # - red-status-percentage: sets the percent size increase where you get a red
- # status indicator, defaults to 20%
- #
- # Either of these arguments can be changed or removed by editing the `nextBundleAnalysis`
- # entry in your package.json file.
- name: Compare with base branch bundle
if: success() && github.event.number
run: ls -laR .next/analyze/base && npx -p nextjs-bundle-analysis compare
@@ -73,26 +58,8 @@ jobs:
echo "$(cat .next/analyze/__bundle_analysis_comment.txt)" >> $GITHUB_OUTPUT
echo EOF >> $GITHUB_OUTPUT
- - name: Find Comment
- uses: peter-evans/find-comment@v2
- if: success() && github.event.number
- id: fc
- with:
- issue-number: ${{ github.event.number }}
- body-includes: ''
-
- - name: Create Comment
- uses: peter-evans/create-or-update-comment@v2
- if: success() && github.event.number && steps.fc.outputs.comment-id == 0
- with:
- issue-number: ${{ github.event.number }}
- body: ${{ steps.get-comment-body.outputs.body }}
-
- - name: Update Comment
- uses: peter-evans/create-or-update-comment@v2
- if: success() && github.event.number && steps.fc.outputs.comment-id != 0
+ - name: Comment
+ uses: marocchino/sticky-pull-request-comment@v2
with:
- issue-number: ${{ github.event.number }}
- body: ${{ steps.get-comment-body.outputs.body }}
- comment-id: ${{ steps.fc.outputs.comment-id }}
- edit-mode: replace
+ header: next-bundle-analysis
+ message: ${{ steps.get-comment-body.outputs.body }}
diff --git a/.gitignore b/.gitignore
index 97916164f6..505387fd77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,4 +52,5 @@ yalc.lock
/public/worker-*.js
/public/workbox-*.js
/public/workbox-*.js.map
-/public/fallback*
\ No newline at end of file
+/public/fallback*
+/public/*.js.LICENSE.txt
\ No newline at end of file
diff --git a/src/components/common/ConnectWallet/WalletDetails.tsx b/src/components/common/ConnectWallet/WalletDetails.tsx
index 5f0442f50a..6a2ad4caf7 100644
--- a/src/components/common/ConnectWallet/WalletDetails.tsx
+++ b/src/components/common/ConnectWallet/WalletDetails.tsx
@@ -1,8 +1,13 @@
-import { Box, Divider, SvgIcon, Typography } from '@mui/material'
+import { Box, Divider, Skeleton, SvgIcon, Typography } from '@mui/material'
+import dynamic from 'next/dynamic'
import type { ReactElement } from 'react'
import LockIcon from '@/public/images/common/lock.svg'
-import SocialSigner from '@/components/common/SocialSigner'
+
+const SocialSigner = dynamic(() => import('@/components/common/SocialSigner'), {
+ loading: () => ,
+})
+
import WalletLogin from '@/components/welcome/WelcomeLogin/WalletLogin'
const WalletDetails = ({ onConnect }: { onConnect: () => void }): ReactElement => {
diff --git a/src/components/common/SocialSigner/index.tsx b/src/components/common/SocialSigner/index.tsx
index c7f1138219..bef9d919b6 100644
--- a/src/components/common/SocialSigner/index.tsx
+++ b/src/components/common/SocialSigner/index.tsx
@@ -1,4 +1,7 @@
+import useSocialWallet from '@/hooks/wallets/mpc/useSocialWallet'
+import { type ISocialWalletService } from '@/services/mpc/interfaces'
import { Box, Button, LinearProgress, SvgIcon, Tooltip, Typography } from '@mui/material'
+import { COREKIT_STATUS } from '@web3auth/mpc-core-kit'
import { useCallback, useContext, useMemo, useState } from 'react'
import { PasswordRecovery } from '@/components/common/SocialSigner/PasswordRecovery'
import GoogleLogo from '@/public/images/welcome/logo-google.svg'
@@ -15,8 +18,6 @@ import { isSocialLoginWallet } from '@/services/mpc/SocialLoginModule'
import { CGW_NAMES } from '@/hooks/wallets/consts'
import { type ChainInfo } from '@safe-global/safe-gateway-typescript-sdk'
import { TxModalContext } from '@/components/tx-flow'
-import { COREKIT_STATUS } from '@web3auth/mpc-core-kit'
-import useSocialWallet from '@/hooks/wallets/mpc/useSocialWallet'
import madProps from '@/utils/mad-props'
import { asError } from '@/services/exceptions/utils'
import ErrorMessage from '@/components/tx/ErrorMessage'
@@ -41,7 +42,7 @@ const useIsSocialWalletEnabled = () => {
}
type SocialSignerLoginProps = {
- socialWalletService: ReturnType
+ socialWalletService: ISocialWalletService | undefined
wallet: ReturnType
supportedChains: ReturnType
isMPCLoginEnabled: ReturnType
diff --git a/src/components/welcome/WelcomeLogin/index.tsx b/src/components/welcome/WelcomeLogin/index.tsx
index ad768bf84a..9fcc869f44 100644
--- a/src/components/welcome/WelcomeLogin/index.tsx
+++ b/src/components/welcome/WelcomeLogin/index.tsx
@@ -1,9 +1,9 @@
-import SocialSigner from '@/components/common/SocialSigner'
import { AppRoutes } from '@/config/routes'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'
-import { Paper, SvgIcon, Typography, Divider, Link, Box } from '@mui/material'
+import { Paper, SvgIcon, Typography, Divider, Link, Box, Skeleton } from '@mui/material'
import SafeLogo from '@/public/images/logo-text.svg'
+import dynamic from 'next/dynamic'
import css from './styles.module.css'
import { useRouter } from 'next/router'
import WalletLogin from './WalletLogin'
@@ -11,6 +11,10 @@ import { LOAD_SAFE_EVENTS, CREATE_SAFE_EVENTS } from '@/services/analytics/event
import Track from '@/components/common/Track'
import { trackEvent } from '@/services/analytics'
+const SocialSigner = dynamic(() => import('@/components/common/SocialSigner'), {
+ loading: () => ,
+})
+
const WelcomeLogin = () => {
const router = useRouter()
const isSocialLoginEnabled = useHasFeature(FEATURES.SOCIAL_LOGIN)
diff --git a/src/hooks/wallets/mpc/__tests__/useMPC.test.ts b/src/hooks/wallets/mpc/__tests__/useMPC.test.ts
index 2cdfd3f689..7c839a0cb8 100644
--- a/src/hooks/wallets/mpc/__tests__/useMPC.test.ts
+++ b/src/hooks/wallets/mpc/__tests__/useMPC.test.ts
@@ -1,8 +1,7 @@
import * as useOnboard from '@/hooks/wallets/useOnboard'
import * as socialWalletOptions from '@/services/mpc/config'
-import { renderHook, waitFor } from '@/tests/test-utils'
-import { _getMPCCoreKitInstance, setMPCCoreKitInstance, useInitMPC } from '../useMPC'
-import * as useChains from '@/hooks/useChains'
+import { waitFor } from '@/tests/test-utils'
+import { _getMPCCoreKitInstance, initMPC, setMPCCoreKitInstance } from '../useMPC'
import { type ChainInfo, RPC_AUTHENTICATION } from '@safe-global/safe-gateway-typescript-sdk'
import { hexZeroPad } from 'ethers/lib/utils'
import { ONBOARD_MPC_MODULE_LABEL } from '@/services/mpc/SocialLoginModule'
@@ -63,53 +62,55 @@ class EventEmittingMockProvider {
}
}
-describe('useInitMPC', () => {
+describe('initMPC', () => {
+ const mockOnboard = {
+ state: {
+ get: () => ({
+ wallets: [],
+ walletModules: [],
+ }),
+ },
+ } as unknown as OnboardAPI
+
+ const mockChain = {
+ chainId: '5',
+ chainName: 'Goerli',
+ blockExplorerUriTemplate: {
+ address: 'https://goerli.someprovider.io/{address}',
+ txHash: 'https://goerli.someprovider.io/{txHash}',
+ api: 'https://goerli.someprovider.io/',
+ },
+ nativeCurrency: {
+ decimals: 18,
+ logoUri: 'https://logo.goerli.com',
+ name: 'Goerli ETH',
+ symbol: 'ETH',
+ },
+ rpcUri: {
+ authentication: RPC_AUTHENTICATION.NO_AUTHENTICATION,
+ value: 'https://goerli.somerpc.io',
+ },
+ } as unknown as ChainInfo
+
beforeEach(() => {
jest.resetAllMocks()
jest.spyOn(socialWalletOptions, 'isSocialWalletOptions').mockReturnValue(true)
})
+
it('should set the coreKit if user is not logged in yet', async () => {
- const connectWalletSpy = jest.fn().mockImplementation(() => Promise.resolve())
- jest.spyOn(useOnboard, 'connectWallet').mockImplementation(connectWalletSpy)
+ jest.spyOn(useOnboard, 'connectWallet').mockImplementation(() => Promise.resolve(undefined))
jest.spyOn(useOnboard, 'getConnectedWallet').mockReturnValue(null)
- jest.spyOn(useOnboard, 'default').mockReturnValue({
- state: {
- get: () => ({
- wallets: [],
- walletModules: [],
- }),
- },
- } as unknown as OnboardAPI)
- jest.spyOn(useChains, 'useCurrentChain').mockReturnValue({
- chainId: '5',
- chainName: 'Goerli',
- blockExplorerUriTemplate: {
- address: 'https://goerli.someprovider.io/{address}',
- txHash: 'https://goerli.someprovider.io/{txHash}',
- api: 'https://goerli.someprovider.io/',
- },
- nativeCurrency: {
- decimals: 18,
- logoUri: 'https://logo.goerli.com',
- name: 'Goerli ETH',
- symbol: 'ETH',
- },
- rpcUri: {
- authentication: RPC_AUTHENTICATION.NO_AUTHENTICATION,
- value: 'https://goerli.somerpc.io',
- },
- } as unknown as ChainInfo)
const mockWeb3AuthMpcCoreKit = jest.spyOn(require('@web3auth/mpc-core-kit'), 'Web3AuthMPCCoreKit')
mockWeb3AuthMpcCoreKit.mockImplementation(() => {
return new MockMPCCoreKit(COREKIT_STATUS.INITIALIZED, null)
})
- renderHook(() => useInitMPC())
+ await initMPC(mockChain, mockOnboard)
await waitFor(() => {
expect(_getMPCCoreKitInstance()).toBeDefined()
- expect(connectWalletSpy).not.toBeCalled()
+ expect(useOnboard.connectWallet).not.toBeCalled()
})
})
@@ -117,33 +118,6 @@ describe('useInitMPC', () => {
const connectWalletSpy = jest.fn().mockImplementation(() => Promise.resolve())
jest.spyOn(useOnboard, 'connectWallet').mockImplementation(connectWalletSpy)
jest.spyOn(useOnboard, 'getConnectedWallet').mockReturnValue(null)
- jest.spyOn(useOnboard, 'default').mockReturnValue({
- state: {
- get: () => ({
- wallets: [],
- walletModules: [],
- }),
- },
- } as unknown as OnboardAPI)
- jest.spyOn(useChains, 'useCurrentChain').mockReturnValue({
- chainId: '5',
- chainName: 'Goerli',
- blockExplorerUriTemplate: {
- address: 'https://goerli.someprovider.io/{address}',
- txHash: 'https://goerli.someprovider.io/{txHash}',
- api: 'https://goerli.someprovider.io/',
- },
- nativeCurrency: {
- decimals: 18,
- logoUri: 'https://logo.goerli.com',
- name: 'Goerli ETH',
- symbol: 'ETH',
- },
- rpcUri: {
- authentication: RPC_AUTHENTICATION.NO_AUTHENTICATION,
- value: 'https://goerli.somerpc.io',
- },
- } as unknown as ChainInfo)
const mockWeb3AuthMpcCoreKit = jest.spyOn(require('@web3auth/mpc-core-kit'), 'Web3AuthMPCCoreKit')
const mockProvider = jest.fn()
@@ -151,7 +125,7 @@ describe('useInitMPC', () => {
return new MockMPCCoreKit(COREKIT_STATUS.LOGGED_IN, mockProvider as unknown as MPCProvider)
})
- renderHook(() => useInitMPC())
+ await initMPC(mockChain, mockOnboard)
await waitFor(() => {
expect(connectWalletSpy).toBeCalled()
@@ -160,41 +134,13 @@ describe('useInitMPC', () => {
})
it('should copy event handlers and emit chainChanged if the current chain is updated', async () => {
- const connectWalletSpy = jest.fn().mockImplementation(() => Promise.resolve())
- jest.spyOn(useOnboard, 'connectWallet').mockImplementation(connectWalletSpy)
+ jest.spyOn(useOnboard, 'connectWallet').mockImplementation(() => Promise.resolve(undefined))
jest.spyOn(useOnboard, 'getConnectedWallet').mockReturnValue({
address: hexZeroPad('0x1', 20),
label: ONBOARD_MPC_MODULE_LABEL,
chainId: '1',
provider: {} as unknown as EIP1193Provider,
})
- jest.spyOn(useOnboard, 'default').mockReturnValue({
- state: {
- get: () => ({
- wallets: [],
- walletModules: [],
- }),
- },
- } as unknown as OnboardAPI)
- jest.spyOn(useChains, 'useCurrentChain').mockReturnValue({
- chainId: '5',
- chainName: 'Goerli',
- blockExplorerUriTemplate: {
- address: 'https://goerli.someprovider.io/{address}',
- txHash: 'https://goerli.someprovider.io/{txHash}',
- api: 'https://goerli.someprovider.io/',
- },
- nativeCurrency: {
- decimals: 18,
- logoUri: 'https://logo.goerli.com',
- name: 'Goerli ETH',
- symbol: 'ETH',
- },
- rpcUri: {
- authentication: RPC_AUTHENTICATION.NO_AUTHENTICATION,
- value: 'https://goerli.somerpc.io',
- },
- } as unknown as ChainInfo)
const mockWeb3AuthMpcCoreKit = jest.spyOn(require('@web3auth/mpc-core-kit'), 'Web3AuthMPCCoreKit')
const mockChainChangedListener = jest.fn()
@@ -215,12 +161,12 @@ describe('useInitMPC', () => {
return new MockMPCCoreKit(COREKIT_STATUS.LOGGED_IN, mockProvider as unknown as MPCProvider)
})
- renderHook(() => useInitMPC())
+ await initMPC(mockChain, mockOnboard)
await waitFor(() => {
expect(mockChainChangedListener).toHaveBeenCalledWith('0x5')
expect(_getMPCCoreKitInstance()).toBeDefined()
- expect(connectWalletSpy).not.toBeCalled()
+ expect(useOnboard.connectWallet).not.toBeCalled()
})
})
})
diff --git a/src/hooks/wallets/mpc/useMPC.ts b/src/hooks/wallets/mpc/useMPC.ts
index b0b9d8544a..c73a8e2fee 100644
--- a/src/hooks/wallets/mpc/useMPC.ts
+++ b/src/hooks/wallets/mpc/useMPC.ts
@@ -1,88 +1,83 @@
-import { useEffect } from 'react'
+import { IS_PRODUCTION } from '@/config/constants'
+import { connectWallet, getConnectedWallet } from '@/hooks/wallets/useOnboard'
import ExternalStore from '@/services/ExternalStore'
-import { COREKIT_STATUS, Web3AuthMPCCoreKit, WEB3AUTH_NETWORK } from '@web3auth/mpc-core-kit'
+import { SOCIAL_WALLET_OPTIONS } from '@/services/mpc/config'
+import { ONBOARD_MPC_MODULE_LABEL } from '@/services/mpc/SocialLoginModule'
+import { type ChainInfo } from '@safe-global/safe-gateway-typescript-sdk'
+import { type OnboardAPI } from '@web3-onboard/core'
import { CHAIN_NAMESPACES } from '@web3auth/base'
-
-import { useCurrentChain } from '@/hooks/useChains'
+import type { Web3AuthMPCCoreKit } from '@web3auth/mpc-core-kit'
+import { COREKIT_STATUS } from '@web3auth/mpc-core-kit'
import { getRpcServiceUrl } from '../web3'
-import useOnboard, { connectWallet, getConnectedWallet } from '@/hooks/wallets/useOnboard'
-import { useInitSocialWallet } from './useSocialWallet'
-import { ONBOARD_MPC_MODULE_LABEL } from '@/services/mpc/SocialLoginModule'
-import { isSocialWalletOptions, SOCIAL_WALLET_OPTIONS } from '@/services/mpc/config'
-import { IS_PRODUCTION } from '@/config/constants'
const { getStore, setStore, useStore } = new ExternalStore()
-export const useInitMPC = () => {
- const chain = useCurrentChain()
- const onboard = useOnboard()
- useInitSocialWallet()
+export const initMPC = async (chain: ChainInfo, onboard: OnboardAPI) => {
+ const chainConfig = {
+ chainId: `0x${Number(chain.chainId).toString(16)}`,
+ chainNamespace: CHAIN_NAMESPACES.EIP155,
+ rpcTarget: getRpcServiceUrl(chain.rpcUri),
+ displayName: chain.chainName,
+ blockExplorer: new URL(chain.blockExplorerUriTemplate.address).origin,
+ ticker: chain.nativeCurrency.symbol,
+ tickerName: chain.nativeCurrency.name,
+ }
- useEffect(() => {
- if (!chain || !onboard || !isSocialWalletOptions(SOCIAL_WALLET_OPTIONS)) {
- return
- }
+ const currentInstance = getStore()
+ let previousChainChangedListeners: Function[] = []
+ if (currentInstance?.provider) {
+ // We are already connected. We copy onboards event listener for the chainChanged event to propagate a potentially new chainId
+ const oldProvider = currentInstance.provider
+ previousChainChangedListeners = oldProvider.listeners('chainChanged')
+ }
- const chainConfig = {
- chainId: `0x${Number(chain.chainId).toString(16)}`,
- chainNamespace: CHAIN_NAMESPACES.EIP155,
- rpcTarget: getRpcServiceUrl(chain.rpcUri),
- displayName: chain.chainName,
- blockExplorer: new URL(chain.blockExplorerUriTemplate.address).origin,
- ticker: chain.nativeCurrency.symbol,
- tickerName: chain.nativeCurrency.name,
- }
+ const { Web3AuthMPCCoreKit, WEB3AUTH_NETWORK } = await import('@web3auth/mpc-core-kit')
- const currentInstance = getStore()
- let previousChainChangedListeners: Function[] = []
- if (currentInstance?.provider) {
- // We are already connected. We copy onboards event listener for the chainChanged event to propagate a potentially new chainId
- const oldProvider = currentInstance.provider
- previousChainChangedListeners = oldProvider.listeners('chainChanged')
- }
+ const web3AuthCoreKit = new Web3AuthMPCCoreKit({
+ web3AuthClientId: SOCIAL_WALLET_OPTIONS.web3AuthClientId,
+ // Available networks are "sapphire_devnet", "sapphire_mainnet"
+ web3AuthNetwork: WEB3AUTH_NETWORK.MAINNET,
+ baseUrl: `${window.location.origin}/`,
+ uxMode: 'popup',
+ enableLogging: !IS_PRODUCTION,
+ //@ts-ignore
+ chainConfig,
+ manualSync: true,
+ hashedFactorNonce: 'safe-global-sfa-nonce',
+ })
- const web3AuthCoreKit = new Web3AuthMPCCoreKit({
- web3AuthClientId: SOCIAL_WALLET_OPTIONS.web3AuthClientId,
- // Available networks are "sapphire_devnet", "sapphire_mainnet"
- web3AuthNetwork: WEB3AUTH_NETWORK.MAINNET,
- baseUrl: `${window.location.origin}/`,
- uxMode: 'popup',
- enableLogging: !IS_PRODUCTION,
- chainConfig,
- manualSync: true,
- hashedFactorNonce: 'safe-global-sfa-nonce',
- })
+ return web3AuthCoreKit
+ .init()
+ .then(() => {
+ setStore(web3AuthCoreKit)
+ // If rehydration was successful, connect to onboard
+ if (web3AuthCoreKit.status !== COREKIT_STATUS.LOGGED_IN || !web3AuthCoreKit.provider) {
+ return web3AuthCoreKit
+ }
- web3AuthCoreKit
- .init()
- .then(() => {
- setStore(web3AuthCoreKit)
- // If rehydration was successful, connect to onboard
- if (web3AuthCoreKit.status !== COREKIT_STATUS.LOGGED_IN || !web3AuthCoreKit.provider) {
- return
- }
- const connectedWallet = getConnectedWallet(onboard.state.get().wallets)
- if (!connectedWallet) {
- connectWallet(onboard, {
- autoSelect: {
- label: ONBOARD_MPC_MODULE_LABEL,
- disableModals: true,
- },
- }).catch((reason) => console.error('Error connecting to MPC module:', reason))
- } else {
- const newProvider = web3AuthCoreKit.provider
+ const connectedWallet = getConnectedWallet(onboard.state.get().wallets)
+ if (!connectedWallet) {
+ connectWallet(onboard, {
+ autoSelect: {
+ label: ONBOARD_MPC_MODULE_LABEL,
+ disableModals: true,
+ },
+ }).catch((reason) => console.error('Error connecting to MPC module:', reason))
+ } else {
+ const newProvider = web3AuthCoreKit.provider
- // To propagate the changedChain we disconnect and connect
- if (previousChainChangedListeners.length > 0 && newProvider) {
- previousChainChangedListeners.forEach((previousListener) =>
- newProvider.addListener('chainChanged', (...args: []) => previousListener(...args)),
- )
- newProvider.emit('chainChanged', `0x${Number(chainConfig.chainId).toString(16)}`)
- }
+ // To propagate the changedChain we disconnect and connect
+ if (previousChainChangedListeners.length > 0 && newProvider) {
+ previousChainChangedListeners.forEach((previousListener) =>
+ newProvider.addListener('chainChanged', (...args: []) => previousListener(...args)),
+ )
+ newProvider.emit('chainChanged', `0x${Number(chainConfig.chainId).toString(16)}`)
}
- })
- .catch((error) => console.error(error))
- }, [chain, onboard])
+ }
+
+ return web3AuthCoreKit
+ })
+ .catch((error) => console.error(error))
}
export const _getMPCCoreKitInstance = getStore
diff --git a/src/hooks/wallets/mpc/useRehydrateSocialWallet.ts b/src/hooks/wallets/mpc/useRehydrateSocialWallet.ts
new file mode 100644
index 0000000000..799d7f2036
--- /dev/null
+++ b/src/hooks/wallets/mpc/useRehydrateSocialWallet.ts
@@ -0,0 +1,68 @@
+import useAddressBook from '@/hooks/useAddressBook'
+import useChainId from '@/hooks/useChainId'
+import { useCurrentChain } from '@/hooks/useChains'
+import useOnboard, { connectWallet } from '@/hooks/wallets/useOnboard'
+import { ONBOARD_MPC_MODULE_LABEL } from '@/services/mpc/SocialLoginModule'
+import { useAppDispatch } from '@/store'
+import { upsertAddressBookEntry } from '@/store/addressBookSlice'
+import { type WalletState } from '@web3-onboard/core'
+import { type UserInfo } from '@web3auth/mpc-core-kit'
+import { useCallback, useEffect } from 'react'
+import { checksumAddress } from '@/utils/addresses'
+
+const useRehydrateSocialWallet = () => {
+ const chain = useCurrentChain()
+ const onboard = useOnboard()
+ const currentChainId = useChainId()
+ const addressBook = useAddressBook()
+ const dispatch = useAppDispatch()
+
+ const updateAddressBook = useCallback(
+ (userInfo: UserInfo | undefined, wallets: WalletState[] | undefined | void) => {
+ if (!userInfo || !wallets || !currentChainId || wallets.length === 0) return
+
+ const address = wallets[0].accounts[0]?.address
+ if (address) {
+ const signerAddress = checksumAddress(address)
+ if (addressBook[signerAddress] === undefined) {
+ const email = userInfo.email
+ dispatch(upsertAddressBookEntry({ address: signerAddress, chainId: currentChainId, name: email }))
+ }
+ }
+ },
+ [addressBook, currentChainId, dispatch],
+ )
+
+ useEffect(() => {
+ if (!chain || !onboard) return
+
+ const rehydrate = async () => {
+ const { initMPC } = await import('./useMPC')
+ const { initSocialWallet } = await import('./useSocialWallet')
+ const mpcCoreKit = await initMPC(chain, onboard)
+
+ if (!mpcCoreKit) return
+
+ const socialWalletService = await initSocialWallet(mpcCoreKit)
+
+ const onConnect = async () => {
+ const wallets = await connectWallet(onboard, {
+ autoSelect: {
+ label: ONBOARD_MPC_MODULE_LABEL,
+ disableModals: true,
+ },
+ }).catch((reason) => console.error('Error connecting to MPC module:', reason))
+
+ // If the signer is not in the address book => add the user's email as name
+ const userInfo = socialWalletService?.getUserInfo()
+ updateAddressBook(userInfo, wallets)
+ }
+
+ socialWalletService.setOnConnect(onConnect)
+ }
+
+ void rehydrate()
+ }, [chain, onboard, updateAddressBook])
+}
+
+export default useRehydrateSocialWallet
diff --git a/src/hooks/wallets/mpc/useSocialWallet.ts b/src/hooks/wallets/mpc/useSocialWallet.ts
index d98998ffdd..8db21735d6 100644
--- a/src/hooks/wallets/mpc/useSocialWallet.ts
+++ b/src/hooks/wallets/mpc/useSocialWallet.ts
@@ -1,59 +1,15 @@
-import useAddressBook from '@/hooks/useAddressBook'
-import useChainId from '@/hooks/useChainId'
import ExternalStore from '@/services/ExternalStore'
import type { ISocialWalletService } from '@/services/mpc/interfaces'
-import { ONBOARD_MPC_MODULE_LABEL } from '@/services/mpc/SocialLoginModule'
-import SocialWalletService from '@/services/mpc/SocialWalletService'
-import { useAppDispatch } from '@/store'
-import { upsertAddressBookEntry } from '@/store/addressBookSlice'
-import { checksumAddress } from '@/utils/addresses'
-import { useCallback, useEffect } from 'react'
-import useOnboard, { connectWallet } from '../useOnboard'
-import useMpc from './useMPC'
+import { type Web3AuthMPCCoreKit } from '@web3auth/mpc-core-kit'
const { getStore, setStore, useStore } = new ExternalStore()
-export const useInitSocialWallet = () => {
- const mpcCoreKit = useMpc()
- const onboard = useOnboard()
- const addressBook = useAddressBook()
- const currentChainId = useChainId()
- const dispatch = useAppDispatch()
- const socialWalletService = useStore()
+export const initSocialWallet = async (mpcCoreKit: Web3AuthMPCCoreKit) => {
+ const SocialWalletService = (await import('@/services/mpc/SocialWalletService')).default
+ const socialWalletService = new SocialWalletService(mpcCoreKit)
+ setStore(socialWalletService)
- const onConnect = useCallback(async () => {
- if (!onboard || !socialWalletService) return
-
- const wallets = await connectWallet(onboard, {
- autoSelect: {
- label: ONBOARD_MPC_MODULE_LABEL,
- disableModals: true,
- },
- }).catch((reason) => console.error('Error connecting to MPC module:', reason))
-
- // If the signer is not in the address book => add the user's email as name
- const userInfo = socialWalletService.getUserInfo()
- if (userInfo && wallets && currentChainId && wallets.length > 0) {
- const address = wallets[0].accounts[0]?.address
- if (address) {
- const signerAddress = checksumAddress(address)
- if (addressBook[signerAddress] === undefined) {
- const email = userInfo.email
- dispatch(upsertAddressBookEntry({ address: signerAddress, chainId: currentChainId, name: email }))
- }
- }
- }
- }, [addressBook, currentChainId, dispatch, onboard, socialWalletService])
-
- useEffect(() => {
- socialWalletService?.setOnConnect(onConnect)
- }, [onConnect, socialWalletService])
-
- useEffect(() => {
- if (mpcCoreKit) {
- setStore(new SocialWalletService(mpcCoreKit))
- }
- }, [mpcCoreKit])
+ return socialWalletService
}
export const getSocialWalletService = getStore
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index e7b3d6d3d7..65dcbf5fe9 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -1,3 +1,4 @@
+import useRehydrateSocialWallet from '@/hooks/wallets/mpc/useRehydrateSocialWallet'
import PasswordRecoveryModal from '@/services/mpc/PasswordRecoveryModal'
import Sentry from '@/services/sentry' // needs to be imported first
import type { ReactNode } from 'react'
@@ -38,7 +39,6 @@ import useSafeMessageNotifications from '@/hooks/messages/useSafeMessageNotifica
import useSafeMessagePendingStatuses from '@/hooks/messages/useSafeMessagePendingStatuses'
import useChangedValue from '@/hooks/useChangedValue'
import { TxModalProvider } from '@/components/tx-flow'
-import { useInitMPC } from '@/hooks/wallets/mpc/useMPC'
import { WalletConnectProvider } from '@/services/walletconnect/WalletConnectContext'
import useABTesting from '@/services/tracking/useAbTesting'
import { AbTest } from '@/services/tracking/abTesting'
@@ -65,7 +65,7 @@ const InitApp = (): null => {
useTxTracking()
useSafeMsgTracking()
useBeamer()
- useInitMPC()
+ useRehydrateSocialWallet()
useABTesting(AbTest.HUMAN_DESCRIPTION)
return null
diff --git a/src/services/mpc/SocialLoginModule.ts b/src/services/mpc/SocialLoginModule.ts
index 6e34ce89d8..84052dfbed 100644
--- a/src/services/mpc/SocialLoginModule.ts
+++ b/src/services/mpc/SocialLoginModule.ts
@@ -1,14 +1,9 @@
-import { _getMPCCoreKitInstance } from '@/hooks/wallets/mpc/useMPC'
-import { getSocialWalletService } from '@/hooks/wallets/mpc/useSocialWallet'
import { getWeb3ReadOnly } from '@/hooks/wallets/web3'
-import * as PasswordRecoveryModal from '@/services/mpc/PasswordRecoveryModal'
import { FEATURES, hasFeature } from '@/utils/chains'
import type { ChainInfo } from '@safe-global/safe-gateway-typescript-sdk'
import { type WalletInit, ProviderRpcError } from '@web3-onboard/common'
import { type EIP1193Provider } from '@web3-onboard/core'
-import { COREKIT_STATUS } from '@web3auth/mpc-core-kit'
-
-const getMPCProvider = () => _getMPCCoreKitInstance()?.provider
+import { type Web3AuthMPCCoreKit } from '@web3auth/mpc-core-kit'
const assertDefined = (mpcProvider: T | undefined) => {
if (!mpcProvider) {
@@ -23,9 +18,9 @@ export const isSocialLoginWallet = (walletLabel: string | undefined) => {
return walletLabel === ONBOARD_MPC_MODULE_LABEL
}
-const getConnectedAccounts = async () => {
+const getConnectedAccounts = async (provider: typeof Web3AuthMPCCoreKit.prototype.provider | undefined) => {
try {
- const web3 = assertDefined(getMPCProvider())
+ const web3 = assertDefined(provider)
return web3.request({ method: 'eth_accounts' })
} catch (e) {
throw new ProviderRpcError({
@@ -50,6 +45,13 @@ function MpcModule(chain: ChainInfo): WalletInit {
label: ONBOARD_MPC_MODULE_LABEL,
getIcon: async () => (await import('./icon')).default,
getInterface: async () => {
+ const { _getMPCCoreKitInstance } = await import('@/hooks/wallets/mpc/useMPC')
+ const { getSocialWalletService } = await import('@/hooks/wallets/mpc/useSocialWallet')
+ const { COREKIT_STATUS } = await import('@web3auth/mpc-core-kit')
+ const { open } = await import('./PasswordRecoveryModal')
+
+ const getMPCProvider = () => _getMPCCoreKitInstance()?.provider
+
const provider: EIP1193Provider = {
on: (event, listener) => {
const web3 = assertDefined(getMPCProvider())
@@ -85,11 +87,11 @@ function MpcModule(chain: ChainInfo): WalletInit {
const status = await socialWalletService.loginAndCreate()
if (status === COREKIT_STATUS.REQUIRED_SHARE) {
- PasswordRecoveryModal.open(() => {
- getConnectedAccounts().then(resolve).catch(reject)
+ open(() => {
+ getConnectedAccounts(getMPCProvider()).then(resolve).catch(reject)
})
} else {
- getConnectedAccounts().then(resolve).catch(reject)
+ getConnectedAccounts(getMPCProvider()).then(resolve).catch(reject)
}
}
return