diff --git a/src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts b/src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts index 53a0b60985..176e7b777f 100644 --- a/src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts +++ b/src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts @@ -14,9 +14,15 @@ import { setMPCCoreKitInstance } from '../useMPC' import { ONBOARD_MPC_MODULE_LABEL } from '@/services/mpc/module' import { ethers } from 'ethers' import BN from 'bn.js' +import * as addressBookSlice from '@/store/addressBookSlice' +import * as useChainId from '@/hooks/useChainId' +import { hexZeroPad } from 'ethers/lib/utils' +import * as useAddressBook from '@/hooks/useAddressBook' /** time until mock login resolves */ const MOCK_LOGIN_TIME = 1000 +/** Mock address for successful login */ +const mockSignerAddress = hexZeroPad('0x1', 20) /** * Helper class for mocking MPC Core Kit login flow @@ -67,6 +73,10 @@ class MockMPCCoreKit { commitChanges() { return Promise.resolve() } + + getUserInfo() { + return this.state.userInfo + } } describe('useMPCWallet', () => { @@ -76,6 +86,7 @@ describe('useMPCWallet', () => { beforeEach(() => { jest.resetAllMocks() setMPCCoreKitInstance(undefined) + jest.spyOn(useChainId, 'default').mockReturnValue('100') }) afterAll(() => { jest.useRealTimers() @@ -94,6 +105,7 @@ describe('useMPCWallet', () => { }) it('should throw if MPC Core Kit is not initialized', () => { + jest.spyOn(useAddressBook, 'default').mockReturnValue({}) jest.spyOn(useOnboard, 'default').mockReturnValue({} as unknown as OnboardAPI) const { result } = renderHook(() => useMPCWallet()) @@ -102,8 +114,10 @@ describe('useMPCWallet', () => { }) it('should handle successful log in for SFA account', async () => { + jest.spyOn(useAddressBook, 'default').mockReturnValue({}) + const upsertABSpy = jest.spyOn(addressBookSlice, 'upsertAddressBookEntry') jest.spyOn(useOnboard, 'default').mockReturnValue({} as unknown as OnboardAPI) - const connectWalletSpy = jest.fn().mockImplementation(() => Promise.resolve()) + const connectWalletSpy = jest.fn().mockResolvedValue([{ accounts: [{ address: mockSignerAddress }] }]) jest.spyOn(useOnboard, 'connectWallet').mockImplementation(connectWalletSpy) setMPCCoreKitInstance( new MockMPCCoreKit(COREKIT_STATUS.LOGGED_IN, { @@ -137,13 +151,17 @@ describe('useMPCWallet', () => { disableModals: true, }, }) + expect(upsertABSpy).toBeCalledWith({ address: mockSignerAddress, name: 'test@test.com', chainId: '100' }) }) }) it('should handle successful log in for MFA account with device share', async () => { + jest.spyOn(useAddressBook, 'default').mockReturnValue({ [mockSignerAddress]: 'Some name' }) + const upsertABSpy = jest.spyOn(addressBookSlice, 'upsertAddressBookEntry') const mockDeviceFactor = ethers.Wallet.createRandom().privateKey.slice(2) jest.spyOn(useOnboard, 'default').mockReturnValue({} as unknown as OnboardAPI) - const connectWalletSpy = jest.fn().mockImplementation(() => Promise.resolve()) + const connectWalletSpy = jest.fn().mockResolvedValue([{ accounts: [{ address: mockSignerAddress }] }]) + jest.spyOn(useOnboard, 'connectWallet').mockImplementation(connectWalletSpy) setMPCCoreKitInstance( new MockMPCCoreKit( @@ -184,6 +202,7 @@ describe('useMPCWallet', () => { disableModals: true, }, }) + expect(upsertABSpy).not.toHaveBeenCalled() }) }) diff --git a/src/hooks/wallets/mpc/useMPCWallet.ts b/src/hooks/wallets/mpc/useMPCWallet.ts index 06328e4e82..1c7c1416af 100644 --- a/src/hooks/wallets/mpc/useMPCWallet.ts +++ b/src/hooks/wallets/mpc/useMPCWallet.ts @@ -9,6 +9,11 @@ import { SecurityQuestionRecovery } from './recovery/SecurityQuestionRecovery' import { DeviceShareRecovery } from './recovery/DeviceShareRecovery' import { trackEvent } from '@/services/analytics' import { MPC_WALLET_EVENTS } from '@/services/analytics/events/mpcWallet' +import useAddressBook from '@/hooks/useAddressBook' +import { upsertAddressBookEntry } from '@/store/addressBookSlice' +import { useAppDispatch } from '@/store' +import useChainId from '@/hooks/useChainId' +import { checksumAddress } from '@/utils/addresses' export enum MPCWalletState { NOT_INITIALIZED, @@ -32,6 +37,9 @@ export const useMPCWallet = (): MPCWalletHook => { const [walletState, setWalletState] = useState(MPCWalletState.NOT_INITIALIZED) const mpcCoreKit = useMPC() const onboard = useOnboard() + const addressBook = useAddressBook() + const currentChainId = useChainId() + const dispatch = useAppDispatch() const criticalResetAccount = async (): Promise => { // This is a critical function that should only be used for testing purposes @@ -101,12 +109,24 @@ export const useMPCWallet = (): MPCWalletHook => { if (mpcCoreKit.status === COREKIT_STATUS.LOGGED_IN) { await mpcCoreKit.commitChanges() - await connectWallet(onboard, { + 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 + if (wallets && currentChainId && wallets.length > 0) { + const address = wallets[0].accounts[0]?.address + if (address) { + const signerAddress = checksumAddress(address) + if (addressBook[signerAddress] === undefined) { + const email = mpcCoreKit.getUserInfo().email + dispatch(upsertAddressBookEntry({ address: signerAddress, chainId: currentChainId, name: email })) + } + } + } setWalletState(MPCWalletState.READY) } } diff --git a/src/utils/addresses.ts b/src/utils/addresses.ts index e1c4bfce68..8718efbd22 100644 --- a/src/utils/addresses.ts +++ b/src/utils/addresses.ts @@ -1,6 +1,10 @@ import { getAddress } from 'ethers/lib/utils' import { isAddress } from '@ethersproject/address' - +/** + * Checksums the given address + * @param address ethereum address + * @returns the checksummed address if the given address is valid otherwise returns the address unchanged + */ export const checksumAddress = (address: string): string => { return isAddress(address) ? getAddress(address) : address }