Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Seedless-Onboarding] Add signer account to address book on login #2672

Merged
merged 2 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 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,16 @@ 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 chains from '@/hooks/useChains'
import { hexZeroPad } from 'ethers/lib/utils'
import { type ChainInfo } from '@safe-global/safe-gateway-typescript-sdk'
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 +74,10 @@ class MockMPCCoreKit {
commitChanges() {
return Promise.resolve()
}

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

describe('useMPCWallet', () => {
Expand All @@ -76,6 +87,9 @@ describe('useMPCWallet', () => {
beforeEach(() => {
jest.resetAllMocks()
setMPCCoreKitInstance(undefined)
jest
.spyOn(chains, 'useCurrentChain')
.mockReturnValue({ chainId: '100', disabledWallets: [] } as unknown as ChainInfo)
})
afterAll(() => {
jest.useRealTimers()
Expand All @@ -94,6 +108,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 +117,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 @@ -135,13 +152,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 @@ -180,6 +201,7 @@ describe('useMPCWallet', () => {
disableModals: true,
},
})
expect(upsertABSpy).not.toHaveBeenCalled()
})
})

Expand Down
19 changes: 18 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 { useCurrentChain } from '@/hooks/useChains'
import { upsertAddressBookEntry } from '@/store/addressBookSlice'
import { useAppDispatch } from '@/store'
import { ethers } from 'ethers'

export enum MPCWalletState {
NOT_INITIALIZED,
Expand All @@ -30,6 +35,9 @@ export const useMPCWallet = (): MPCWalletHook => {
const [walletState, setWalletState] = useState(MPCWalletState.NOT_INITIALIZED)
const mpcCoreKit = useMPC()
const onboard = useOnboard()
const addressBook = useAddressBook()
const currentChainId = useCurrentChain()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use useChainId directly here as we only need the chain id

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 @@ -99,12 +107,21 @@ 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 it as the user's first name
if (wallets && currentChainId && wallets.length > 0) {
const signerAddress = ethers.utils.getAddress(wallets[0].accounts[0]?.address)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAddress throws an error if we pass undefined. Not sure why the types don't reflect it but I think we need to split up the call. We could also reuse our checksumAddress wdyt?

const address = wallets[0].accounts[0]?.address
if (!address) return
const checksummedAddress = checksumAddress(address)

if (addressBook[signerAddress] === undefined) {
const email = mpcCoreKit.getUserInfo().email
dispatch(upsertAddressBookEntry({ address: signerAddress, chainId: currentChainId.chainId, name: email }))
}
}
setWalletState(MPCWalletState.READY)
}
}
Expand Down
Loading