diff --git a/packages/extension/src/libs/background/index.ts b/packages/extension/src/libs/background/index.ts index ca4fdee91..cc8582731 100644 --- a/packages/extension/src/libs/background/index.ts +++ b/packages/extension/src/libs/background/index.ts @@ -25,6 +25,7 @@ import { sendToTab, newAccount, lock, + getPrivateKey, } from './internal'; import { handlePersistentEvents } from './external'; import SettingsState from '../settings-state'; @@ -168,6 +169,8 @@ class BackgroundHandler { case InternalMethods.getNewAccount: case InternalMethods.saveNewAccount: return newAccount(this.#keyring, message); + case InternalMethods.getPrivateKey: + return getPrivateKey(this.#keyring, message); default: return Promise.resolve({ error: getCustomError( diff --git a/packages/extension/src/libs/background/internal/get-private-key.ts b/packages/extension/src/libs/background/internal/get-private-key.ts new file mode 100644 index 000000000..ce7471101 --- /dev/null +++ b/packages/extension/src/libs/background/internal/get-private-key.ts @@ -0,0 +1,28 @@ +import { getCustomError } from '@/libs/error' +import KeyRingBase from '@/libs/keyring/keyring' +import { InternalOnMessageResponse } from '@/types/messenger' +import { EnkryptAccount, RPCRequestType } from '@enkryptcom/types' + +const getPrivateKey = async ( + keyring: KeyRingBase, + message: RPCRequestType, +): Promise => { + if (!message.params || message.params.length < 2) + return { + error: getCustomError('background: invalid params for getting private key'), + } + const account = message.params[0] as EnkryptAccount + const password = message.params[1] as string + try { + const privKey = await keyring.getPrivateKey(account, password) + return { + result: JSON.stringify(privKey), + } + } catch (e: any) { + return { + error: getCustomError(e.message), + } + } +} + +export default getPrivateKey diff --git a/packages/extension/src/libs/background/internal/index.ts b/packages/extension/src/libs/background/internal/index.ts index b33949948..a5613be13 100644 --- a/packages/extension/src/libs/background/internal/index.ts +++ b/packages/extension/src/libs/background/internal/index.ts @@ -6,6 +6,7 @@ import changeNetwork from './change-network'; import sendToTab from './send-to-tab'; import newAccount from './new-account'; import lock from './lock'; +import getPrivateKey from './get-private-key'; export { sign, getEthereumPubKey, @@ -15,4 +16,5 @@ export { sendToTab, newAccount, lock, + getPrivateKey, }; diff --git a/packages/extension/src/libs/keyring/keyring.ts b/packages/extension/src/libs/keyring/keyring.ts index 4fc5f98e7..cd0b8d93c 100644 --- a/packages/extension/src/libs/keyring/keyring.ts +++ b/packages/extension/src/libs/keyring/keyring.ts @@ -97,5 +97,8 @@ export class KeyRingBase { deleteAccount(address: string): Promise { return this.#keyring.deleteAccount(address); } + getPrivateKey(options: SignOptions, password: string): Promise { + return this.#keyring.getPrivateKey(options, password); + } } export default KeyRingBase; diff --git a/packages/extension/src/types/messenger.ts b/packages/extension/src/types/messenger.ts index 276fb234b..3815e0328 100644 --- a/packages/extension/src/types/messenger.ts +++ b/packages/extension/src/types/messenger.ts @@ -35,6 +35,7 @@ export enum InternalMethods { getNewAccount = 'enkrypt_getNewAccount', saveNewAccount = 'enkrypt_saveNewAccount', changeNetwork = 'enkrypt_changeNetwork', + getPrivateKey = 'enkrypt_getPrivateKey', } export interface SendMessage { [key: string]: any; diff --git a/packages/extension/src/ui/action/views/accounts/components/accounts-list-item-menu.vue b/packages/extension/src/ui/action/views/accounts/components/accounts-list-item-menu.vue index dcec20c30..0021d941a 100644 --- a/packages/extension/src/ui/action/views/accounts/components/accounts-list-item-menu.vue +++ b/packages/extension/src/ui/action/views/accounts/components/accounts-list-item-menu.vue @@ -10,18 +10,28 @@ > Delete + + Export + @@ -77,6 +87,10 @@ defineProps({ &.delete { color: @error; } + + &.export { + color: @grayPrimary; + } } } } diff --git a/packages/extension/src/ui/action/views/accounts/components/accounts-list-item.vue b/packages/extension/src/ui/action/views/accounts/components/accounts-list-item.vue index 1a13ee487..12654570e 100644 --- a/packages/extension/src/ui/action/views/accounts/components/accounts-list-item.vue +++ b/packages/extension/src/ui/action/views/accounts/components/accounts-list-item.vue @@ -31,6 +31,7 @@ v-if="openEdit" ref="dropdown" :deletable="deletable" + :exportable="exportable" v-bind="$attrs" /> @@ -78,6 +79,7 @@ defineProps({ active: Boolean, showEdit: Boolean, deletable: Boolean, + exportable: Boolean, }); const toggleEdit = () => { diff --git a/packages/extension/src/ui/action/views/accounts/components/export-account-form.vue b/packages/extension/src/ui/action/views/accounts/components/export-account-form.vue new file mode 100644 index 000000000..4300c184e --- /dev/null +++ b/packages/extension/src/ui/action/views/accounts/components/export-account-form.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/packages/extension/src/ui/action/views/accounts/index.vue b/packages/extension/src/ui/action/views/accounts/index.vue index d2605c067..6b4548cd6 100644 --- a/packages/extension/src/ui/action/views/accounts/index.vue +++ b/packages/extension/src/ui/action/views/accounts/index.vue @@ -23,8 +23,10 @@ :identicon-element="network.identicon" :show-edit="true" :deletable="account.walletType !== WalletType.mnemonic" + :exportable="true" @action:rename="renameAccount(index)" @action:delete="deleteAccount(index)" + @action:export="exportAccount(index)" />
@@ -100,6 +102,13 @@ :account="accountToDelete" /> + + (); const isAddAccount = ref(false); const isRenameAccount = ref(false); +const isExportAccount = ref(false); const isDeleteAccount = ref(false); const isImportAccount = ref(false); const hwWallet = new HWwallets(); @@ -154,6 +165,7 @@ const props = defineProps({ }); const accountToRename = ref(); const accountToDelete = ref(); +const accountToExport = ref(); const close = () => { props.toggle(); @@ -185,6 +197,14 @@ const renameAccount = (accountIdx: number) => { }, 100); }; +const exportAccount = (accountIdx: number) => { + accountToExport.value = props.accountInfo.activeAccounts[accountIdx]; + props.toggle(); + setTimeout(() => { + isExportAccount.value = true; + }, 100); +}; + const deleteAccount = (accountIdx: number) => { accountToDelete.value = props.accountInfo.activeAccounts[accountIdx]; props.toggle(); diff --git a/packages/keyring/src/index.ts b/packages/keyring/src/index.ts index 9200005a9..062725ad3 100644 --- a/packages/keyring/src/index.ts +++ b/packages/keyring/src/index.ts @@ -317,6 +317,30 @@ class KeyRing { await this.#storage.set(configs.STORAGE_KEYS.KEY_INFO, existingKeys); } + async getPrivateKey( + options: SignOptions, + keyringPassword: string, + ): Promise { + assert( + !Object.values(HWwalletType).includes( + options.walletType as unknown as HWwalletType, + ), + Errors.KeyringErrors.CannotUseKeyring, + ); + if (options.walletType === WalletType.privkey) { + const privkeys = await this.#getPrivateKeys(keyringPassword); + assert(privkeys[options.pathIndex.toString()], Errors.KeyringErrors.AddressDoesntExists); + return privkeys[options.pathIndex.toString()]; + } else { + const mnemonic = await this.#getMnemonic(keyringPassword) + const keypair = await this.#signers[options.signerType].generate( + mnemonic, + pathParser(options.basePath, options.pathIndex, options.signerType), + ); + return keypair.privateKey; + } + } + async #getPrivateKeys( keyringPassword: string, ): Promise> {