Skip to content

Commit

Permalink
✨ feat(metamask): Add support for importing private keys
Browse files Browse the repository at this point in the history
  • Loading branch information
duckception committed Nov 17, 2023
1 parent 2683d0e commit c06af63
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 2 deletions.
4 changes: 4 additions & 0 deletions wallets/metamask/src/metamask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export class MetaMask {
await this.onboardingPage.importWallet(seedPhrase, this.password)
}

async importWalletFromPrivateKey(privateKey: string) {
await this.homePage.importWalletFromPrivateKey(privateKey)
}

async connectToDapp() {
if (!this.extensionId) {
throw NO_EXTENSION_ID_ERROR
Expand Down
21 changes: 21 additions & 0 deletions wallets/metamask/src/pages/HomePage/page.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Page } from '@playwright/test'
import { waitFor } from '../../utils/waitFor'
import { lock } from './actions'
import Selectors from './selectors'

Expand All @@ -15,4 +16,24 @@ export class HomePage {
async lock() {
await lock(this.page)
}

async importWalletFromPrivateKey(privateKey: string) {
await this.page.locator(Selectors.accountMenu.accountMenuButton).click()
await this.page.locator(Selectors.accountMenu.importAccountButton).click()
await this.page.locator(Selectors.importAccountScreen.privateKeyInput).fill(privateKey)

const importButton = this.page.locator(Selectors.importAccountScreen.importButton)
await importButton.click()

// TODO: Extract & make configurable
const isHidden = await waitFor(importButton, 'hidden', 1000, false)

if (!isHidden) {
const errorText = await this.page.locator(Selectors.importAccountScreen.error).textContent({
timeout: 1000 // TODO: Extract & make configurable
})

throw new Error(`[ImportWalletFromPrivateKey] Importing failed due to error: ${errorText}`)
}
}
}
18 changes: 16 additions & 2 deletions wallets/metamask/src/pages/HomePage/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,24 @@ import { createDataTestSelector } from '../../../utils/selectors/createDataTestS
const container = '.account-menu'
const accountMenu = {
accountMenuButton: createDataTestSelector('account-menu-icon'),
lockButton: `${container} .account-menu__lock-button`
lockButton: `${container} .account-menu__lock-button`,
importAccountButton: `${container} > .account-menu__item--clickable:nth-of-type(2)`
}

const importAccountScreen = {
privateKeyInput: 'input#private-key-box',
importButton: '.new-account-import-form__buttons button.btn-primary',
error: 'span.error'
}

const recoveryPhraseReminder = {
gotItButton: '.recovery-phrase-reminder button.btn-primary'
}

export default {
logo: createDataTestSelector('app-header-logo'),
accountMenu
account: createDataTestSelector('selected-account-click'),
accountMenu,
importAccountScreen,
recoveryPhraseReminder
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { testWithSynpress } from 'fixtures'
import { MetaMask, unlockForFixture } from '../../../src'

import basicSetup from '../wallet-setup/basic.setup'

const test = testWithSynpress(basicSetup, unlockForFixture)

const { expect } = test

test.use({
permissions: ['clipboard-read']
})

test('should import a new wallet from private key', async ({ context, metamaskPage }) => {
const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword)

if (await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).isVisible()) {
await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).click()
}

await metamask.importWalletFromPrivateKey('ea084c575a01e2bbefcca3db101eaeab1d8af15554640a510c73692db24d0a6a')

await metamaskPage.locator(metamask.homePage.selectors.account).click()

const accountAddressInClipboard = await metamaskPage.evaluate('navigator.clipboard.readText()')
expect(accountAddressInClipboard).toContain('0xa2ce797cA71d0EaE1be5a7EffD27Fd6C38126801')
})

test('should throw an error if trying to import private key for the 2nd time', async ({ context, metamaskPage }) => {
const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword)

if (await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).isVisible()) {
await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).click()
}

const privateKey = 'ea084c575a01e2bbefcca3db101eaeab1d8af15554640a510c73692db24d0a6a'

await metamask.importWalletFromPrivateKey(privateKey)

await metamaskPage.locator(metamask.homePage.selectors.account).click()

const importWalletPromise = metamask.importWalletFromPrivateKey(privateKey)

await expect(importWalletPromise).rejects.toThrowError(
'[ImportWalletFromPrivateKey] Importing failed due to error: The account you are trying to import is a duplicate'
)
})

test('should throw an error if the private key is invalid', async ({ context, metamaskPage }) => {
const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword)

if (await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).isVisible()) {
await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).click()
}

const importWalletPromise = metamask.importWalletFromPrivateKey('0xdeadbeef')

await expect(importWalletPromise).rejects.toThrowError(
'[ImportWalletFromPrivateKey] Importing failed due to error: Expected private key to be an Uint8Array with length 32'
)
})

0 comments on commit c06af63

Please sign in to comment.