Skip to content

Commit

Permalink
[Seedless-Onboarding] Add signer account to address book on login (#2672
Browse files Browse the repository at this point in the history
)
  • Loading branch information
schmanu authored Oct 23, 2023
1 parent 8500cdf commit f31099b
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 4 deletions.
23 changes: 21 additions & 2 deletions src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -67,6 +73,10 @@ class MockMPCCoreKit {
commitChanges() {
return Promise.resolve()
}

getUserInfo() {
return this.state.userInfo
}
}

describe('useMPCWallet', () => {
Expand All @@ -76,6 +86,7 @@ describe('useMPCWallet', () => {
beforeEach(() => {
jest.resetAllMocks()
setMPCCoreKitInstance(undefined)
jest.spyOn(useChainId, 'default').mockReturnValue('100')
})
afterAll(() => {
jest.useRealTimers()
Expand All @@ -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())

Expand All @@ -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, {
Expand Down Expand Up @@ -137,13 +151,17 @@ describe('useMPCWallet', () => {
disableModals: true,
},
})
expect(upsertABSpy).toBeCalledWith({ address: mockSignerAddress, name: '[email protected]', 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(
Expand Down Expand Up @@ -184,6 +202,7 @@ describe('useMPCWallet', () => {
disableModals: true,
},
})
expect(upsertABSpy).not.toHaveBeenCalled()
})
})

Expand Down
22 changes: 21 additions & 1 deletion src/hooks/wallets/mpc/useMPCWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<void> => {
// This is a critical function that should only be used for testing purposes
Expand Down Expand Up @@ -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)
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/utils/addresses.ts
Original file line number Diff line number Diff line change
@@ -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
}
Expand Down

0 comments on commit f31099b

Please sign in to comment.