Restore Account with JSON Backup (#336)
* initial mockup * it works! * forgot a file * refactor a bit * fix lint * fix styling, refactor * fix lint * Apply suggestions from code review Co-authored-by: Jaco Greeff <jacogr@gmail.com> * Open message rename * Go back to root on import * Only open in new when not in a window, add back Co-authored-by: Jaco Greeff <jacogr@gmail.com>
This commit is contained in:
parent
7678e27f55
commit
256c5d975f
@ -4,14 +4,14 @@
|
||||
|
||||
import { MetadataDef } from '@polkadot/extension-inject/types';
|
||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||
import { AccountJson, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountCreateExternal, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountValidate, RequestAuthorizeApprove, RequestAuthorizeReject, RequestDeriveCreate, ResponseDeriveValidate, RequestMetadataApprove, RequestMetadataReject, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSeedCreate, RequestTypes, ResponseAccountExport, RequestAccountForget, ResponseSeedCreate, RequestSeedValidate, RequestDeriveValidate, ResponseSeedValidate, ResponseType, SigningRequest } from '../types';
|
||||
import { AccountJson, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountCreateExternal, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountValidate, RequestAuthorizeApprove, RequestAuthorizeReject, RequestDeriveCreate, ResponseDeriveValidate, RequestMetadataApprove, RequestMetadataReject, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSeedCreate, RequestTypes, ResponseAccountExport, RequestAccountForget, ResponseSeedCreate, RequestSeedValidate, RequestDeriveValidate, ResponseSeedValidate, ResponseType, SigningRequest, RequestJsonRestore, ResponseJsonRestore } from '../types';
|
||||
|
||||
import chrome from '@polkadot/extension-inject/chrome';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
import { KeyringPair, KeyringPair$Meta } from '@polkadot/keyring/types';
|
||||
import { assert, isHex } from '@polkadot/util';
|
||||
import { assert, isHex, isObject } from '@polkadot/util';
|
||||
import { keyExtractSuri, mnemonicGenerate, mnemonicValidate } from '@polkadot/util-crypto';
|
||||
|
||||
import State from './State';
|
||||
@ -177,6 +177,41 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
private jsonRestore ({ json, password }: RequestJsonRestore): ResponseJsonRestore {
|
||||
try {
|
||||
const pair = keyring.restoreAccount(json, password);
|
||||
|
||||
if (pair) {
|
||||
return { error: null };
|
||||
}
|
||||
} catch (error) {
|
||||
return { error: (error as Error).message };
|
||||
}
|
||||
|
||||
return { error: 'Could not restore account.' };
|
||||
}
|
||||
|
||||
private jsonVerifyFile ({ json }: RequestJsonRestore): boolean {
|
||||
try {
|
||||
const publicKey = keyring.decodeAddress(json.address, true);
|
||||
const isFileValid = publicKey.length === 32 && isHex(json.encoded) && isObject(json.meta) && (
|
||||
Array.isArray(json.encoding.content)
|
||||
? json.encoding.content[0] === 'pkcs8'
|
||||
: json.encoding.content === 'pkcs8'
|
||||
);
|
||||
|
||||
return isFileValid;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private jsonVerifyPassword (password: string): boolean {
|
||||
return keyring.isPassValid(password);
|
||||
}
|
||||
|
||||
private seedCreate ({ length = SEED_DEFAULT_LENGTH, type }: RequestSeedCreate): ResponseSeedCreate {
|
||||
const seed = mnemonicGenerate(length);
|
||||
|
||||
@ -270,9 +305,11 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
private windowOpen (): boolean {
|
||||
private windowOpen (path = '/'): boolean {
|
||||
console.error('open', `${chrome.extension.getURL('index.html')}#${path}`);
|
||||
|
||||
chrome.tabs.create({
|
||||
url: chrome.extension.getURL('index.html')
|
||||
url: `${chrome.extension.getURL('index.html')}#${path}`
|
||||
});
|
||||
|
||||
return true;
|
||||
@ -367,6 +404,15 @@ export default class Extension {
|
||||
case 'pri(derivation.validate)':
|
||||
return this.derivationValidate(request as RequestDeriveValidate);
|
||||
|
||||
case 'pri(json.restore)':
|
||||
return this.jsonRestore(request as RequestJsonRestore);
|
||||
|
||||
case 'pri(json.verify.file)':
|
||||
return this.jsonVerifyFile(request as RequestJsonRestore);
|
||||
|
||||
case 'pri(json.verify.password)':
|
||||
return this.jsonVerifyPassword(request as string);
|
||||
|
||||
case 'pri(seed.create)':
|
||||
return this.seedCreate(request as RequestSeedCreate);
|
||||
|
||||
@ -388,6 +434,9 @@ export default class Extension {
|
||||
case 'pri(window.open)':
|
||||
return this.windowOpen();
|
||||
|
||||
case 'pri(window.open.json)':
|
||||
return this.windowOpen('/account/restore-json');
|
||||
|
||||
default:
|
||||
throw new Error(`Unable to handle message of type ${type}`);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { InjectedAccount, MetadataDef, ProviderList, ProviderMeta, InjectedMetadataKnown } from '@polkadot/extension-inject/types';
|
||||
import { JsonRpcResponse } from '@polkadot/rpc-provider/types';
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import { KeyringPair, KeyringPair$Meta } from '@polkadot/keyring/types';
|
||||
import { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@polkadot/keyring/types';
|
||||
import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
@ -76,6 +76,9 @@ export interface RequestSignatures {
|
||||
'pri(authorize.requests)': [RequestAuthorizeSubscribe, boolean, AuthorizeRequest[]];
|
||||
'pri(derivation.create)': [RequestDeriveCreate, boolean];
|
||||
'pri(derivation.validate)': [RequestDeriveValidate, ResponseDeriveValidate];
|
||||
'pri(json.restore)': [RequestJsonRestore, ResponseJsonRestore];
|
||||
'pri(json.verify.file)': [RequestJsonRestore, boolean];
|
||||
'pri(json.verify.password)': [string, boolean];
|
||||
'pri(metadata.approve)': [RequestMetadataApprove, boolean];
|
||||
'pri(metadata.reject)': [RequestMetadataReject, boolean];
|
||||
'pri(metadata.requests)': [RequestMetadataSubscribe, boolean, MetadataRequest[]];
|
||||
@ -87,6 +90,7 @@ export interface RequestSignatures {
|
||||
'pri(signing.cancel)': [RequestSigningCancel, boolean];
|
||||
'pri(signing.requests)': [RequestSigningSubscribe, boolean, SigningRequest[]];
|
||||
'pri(window.open)': [null, boolean];
|
||||
'pri(window.open.json)': [null, boolean];
|
||||
// public/external requests, i.e. from a page
|
||||
'pub(accounts.list)': [RequestAccountList, InjectedAccount[]];
|
||||
'pub(accounts.subscribe)': [RequestAccountSubscribe, boolean, InjectedAccount[]];
|
||||
@ -306,3 +310,12 @@ export interface RequestSign {
|
||||
|
||||
sign (registry: TypeRegistry, pair: KeyringPair): { signature: string };
|
||||
}
|
||||
|
||||
export interface RequestJsonRestore {
|
||||
json: KeyringPair$Json;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ResponseJsonRestore {
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"@polkadot/util-crypto": "^2.11.1",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-dropzone": "^11.0.1",
|
||||
"react-is": "^16.13.1",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
|
||||
@ -5,10 +5,11 @@
|
||||
import React, { useCallback, useContext, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { ActionContext, Address, ButtonArea, NextStepButton, TextAreaWithLabel, ValidatedInput, VerticalSpace } from '../components';
|
||||
import { ActionContext, Address, ButtonArea, NextStepButton, TextAreaWithLabel, ValidatedInput, VerticalSpace, ActionText } from '../components';
|
||||
import { allOf, isNotShorterThan, Result } from '../validators';
|
||||
import { createAccountSuri, validateSeed } from '../messaging';
|
||||
import { createAccountSuri, validateSeed, jsonRestoreWindowOpen } from '../messaging';
|
||||
import { Header, Name, Password } from '../partials';
|
||||
import upload from '../assets/file-upload.svg';
|
||||
|
||||
async function validate (suri: string): Promise<Result<string>> {
|
||||
try {
|
||||
@ -52,6 +53,15 @@ export default function Import (): React.ReactElement {
|
||||
}
|
||||
}, [account, name, onAction, password]);
|
||||
|
||||
const _toJson = useCallback((): void => {
|
||||
// are we in a popup?
|
||||
if (window.innerWidth <= 480) {
|
||||
jsonRestoreWindowOpen().catch(console.error);
|
||||
} else {
|
||||
onAction('/account/restore-json');
|
||||
}
|
||||
}, [onAction]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderWithSmallerMargin
|
||||
@ -85,6 +95,15 @@ export default function Import (): React.ReactElement {
|
||||
</ButtonArea>
|
||||
</>
|
||||
)}
|
||||
{!account &&
|
||||
<ButtonsRow>
|
||||
<ActionText
|
||||
icon={upload}
|
||||
onClick={_toJson}
|
||||
text='Restore from JSON backup'
|
||||
/>
|
||||
</ButtonsRow>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -99,3 +118,12 @@ const SeedInput = styled(TextAreaWithLabel)`
|
||||
height: unset;
|
||||
}
|
||||
`;
|
||||
|
||||
const ButtonsRow = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
${ActionText} {
|
||||
margin-right: 32px;
|
||||
}
|
||||
`;
|
||||
|
||||
118
packages/extension-ui/src/Popup/RestoreJson.tsx
Normal file
118
packages/extension-ui/src/Popup/RestoreJson.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
// Copyright 2019-2020 @polkadot/extension-ui authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeyringPair$Json } from '@polkadot/keyring/types';
|
||||
import React, { useCallback, useContext, useState } from 'react';
|
||||
import { ActionContext, InputWithLabel, InputFileWithLabel, Button, Address } from '../components';
|
||||
import { u8aToString } from '@polkadot/util';
|
||||
import styled from 'styled-components';
|
||||
import { jsonRestore, jsonVerifyPassword, jsonVerifyFile } from '../messaging';
|
||||
|
||||
import { Header } from '../partials';
|
||||
|
||||
interface FileState {
|
||||
address: string | null;
|
||||
isFileValid: boolean;
|
||||
json: KeyringPair$Json | null;
|
||||
}
|
||||
|
||||
// FIXME We want to display the decodeError
|
||||
interface PassState {
|
||||
decodeError?: string | null;
|
||||
isPassValid: boolean;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const acceptedFormats = ['application/json', 'text/plain'].join(', ');
|
||||
|
||||
async function parseFile (file: Uint8Array): Promise<FileState> {
|
||||
try {
|
||||
const json = JSON.parse(u8aToString(file)) as KeyringPair$Json;
|
||||
const isFileValid = await jsonVerifyFile(json);
|
||||
const address = json.address;
|
||||
|
||||
return { address, isFileValid, json };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return { address: null, isFileValid: false, json: null };
|
||||
}
|
||||
|
||||
export default function Upload (): React.ReactElement {
|
||||
const onAction = useContext(ActionContext);
|
||||
const [{ address, isFileValid, json }, setJson] = useState<FileState>({ address: null, isFileValid: false, json: null });
|
||||
const [{ isPassValid, password }, setPass] = useState<PassState>({ isPassValid: false, password: '' });
|
||||
|
||||
const _onChangePass = useCallback(
|
||||
(password: string): void => {
|
||||
jsonVerifyPassword(password)
|
||||
.then((isPassValid) => setPass({ isPassValid, password }))
|
||||
.catch(console.error);
|
||||
}, []
|
||||
);
|
||||
|
||||
const _onChangeFile = useCallback(
|
||||
async (file: Uint8Array): Promise<void> => {
|
||||
setJson(await parseFile(file));
|
||||
}, []
|
||||
);
|
||||
|
||||
const _onRestore = useCallback(
|
||||
(): void => {
|
||||
if (!json || !password) {
|
||||
return;
|
||||
}
|
||||
|
||||
jsonRestore(json, password)
|
||||
.then(({ error }): void => {
|
||||
if (error) {
|
||||
setPass(({ password }) => ({ decodeError: error, isPassValid: false, password }));
|
||||
} else {
|
||||
onAction('/');
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
},
|
||||
[json, onAction, password]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderWithSmallerMargin
|
||||
showBackArrow
|
||||
text='Restore from JSON'
|
||||
/>
|
||||
<div>
|
||||
<Address
|
||||
address={(isFileValid && address) || null}
|
||||
name={(isFileValid && json?.meta.name as string) || null}
|
||||
/>
|
||||
<InputFileWithLabel
|
||||
accept={acceptedFormats}
|
||||
isError={!isFileValid}
|
||||
label={'backup file'}
|
||||
onChange={_onChangeFile}
|
||||
withLabel
|
||||
/>
|
||||
<InputWithLabel
|
||||
isError={!isPassValid}
|
||||
label='Password for this file'
|
||||
onChange={_onChangePass}
|
||||
type='password'
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!isFileValid || !isPassValid}
|
||||
onClick={_onRestore}
|
||||
>
|
||||
Restore
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const HeaderWithSmallerMargin = styled(Header)`
|
||||
margin-bottom: 15px;
|
||||
`;
|
||||
@ -22,6 +22,7 @@ import Export from './Export';
|
||||
import Forget from './Forget';
|
||||
import ImportQr from './ImportQr';
|
||||
import ImportSeed from './ImportSeed';
|
||||
import RestoreJson from './RestoreJson';
|
||||
import Metadata from './Metadata';
|
||||
import Signing from './Signing';
|
||||
import Welcome from './Welcome';
|
||||
@ -121,6 +122,7 @@ export default function Popup (): React.ReactElement {
|
||||
<Route path='/account/export/:address'><Export /></Route>
|
||||
<Route path='/account/import-qr'><ImportQr /></Route>
|
||||
<Route path='/account/import-seed'><ImportSeed /></Route>
|
||||
<Route path='/account/restore-json'><RestoreJson /></Route>
|
||||
<Route path='/account/derive/:address/locked'><Derive isLocked /></Route>
|
||||
<Route path='/account/derive/:address'><Derive /></Route>
|
||||
<Route
|
||||
|
||||
1
packages/extension-ui/src/assets/file-upload.svg
Normal file
1
packages/extension-ui/src/assets/file-upload.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M224 136V0H24C10.7 0 0 10.7 0 24v464c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V160H248c-13.2 0-24-10.8-24-24zm65.18 216.01H224v80c0 8.84-7.16 16-16 16h-32c-8.84 0-16-7.16-16-16v-80H94.82c-14.28 0-21.41-17.29-11.27-27.36l96.42-95.7c6.65-6.61 17.39-6.61 24.04 0l96.42 95.7c10.15 10.07 3.03 27.36-11.25 27.36zM377 105L279.1 7c-4.5-4.5-10.6-7-17-7H256v128h128v-6.1c0-6.3-2.5-12.4-7-16.9z"/></svg>
|
||||
|
After Width: | Height: | Size: 467 B |
141
packages/extension-ui/src/components/InputFileWithLabel.tsx
Normal file
141
packages/extension-ui/src/components/InputFileWithLabel.tsx
Normal file
@ -0,0 +1,141 @@
|
||||
// Copyright 2017-2020 @polkadot/react-components authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React, { useCallback, useState, createRef } from 'react';
|
||||
import Dropzone, { DropzoneRef } from 'react-dropzone';
|
||||
import styled from 'styled-components';
|
||||
import { formatNumber, isHex, u8aToString, hexToU8a } from '@polkadot/util';
|
||||
|
||||
import Label from './Label';
|
||||
|
||||
function classes (...classNames: (boolean | null | string | undefined)[]): string {
|
||||
return classNames
|
||||
.filter((className): boolean => !!className)
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
export interface InputFileProps {
|
||||
// Reference Example Usage: https://github.com/react-dropzone/react-dropzone/tree/master/examples/Accept
|
||||
// i.e. MIME types: 'application/json, text/plain', or '.json, .txt'
|
||||
className?: string;
|
||||
accept?: string;
|
||||
clearContent?: boolean;
|
||||
convertHex?: boolean;
|
||||
help?: React.ReactNode;
|
||||
isDisabled?: boolean;
|
||||
isError?: boolean;
|
||||
label: string;
|
||||
onChange?: (contents: Uint8Array, name: string) => void;
|
||||
placeholder?: React.ReactNode | null;
|
||||
withEllipsis?: boolean;
|
||||
withLabel?: boolean;
|
||||
}
|
||||
|
||||
interface FileState {
|
||||
name: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
const BYTE_STR_0 = '0'.charCodeAt(0);
|
||||
const BYTE_STR_X = 'x'.charCodeAt(0);
|
||||
const NOOP = (): void => undefined;
|
||||
|
||||
function convertResult (result: ArrayBuffer, convertHex?: boolean): Uint8Array {
|
||||
const data = new Uint8Array(result);
|
||||
|
||||
// this converts the input (if detected as hex), vai the hex conversion route
|
||||
if (convertHex && data[0] === BYTE_STR_0 && data[1] === BYTE_STR_X) {
|
||||
const hex = u8aToString(data);
|
||||
|
||||
if (isHex(hex)) {
|
||||
return hexToU8a(hex);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function InputFile ({ accept, className = '', clearContent, convertHex, isDisabled, isError = false, label, onChange, placeholder }: InputFileProps): React.ReactElement<InputFileProps> {
|
||||
const dropRef = createRef<DropzoneRef>();
|
||||
const [file, setFile] = useState<FileState | undefined>();
|
||||
|
||||
const _onDrop = useCallback(
|
||||
(files: File[]): void => {
|
||||
files.forEach((file): void => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onabort = NOOP;
|
||||
reader.onerror = NOOP;
|
||||
|
||||
reader.onload = ({ target }: ProgressEvent<FileReader>): void => {
|
||||
if (target && target.result) {
|
||||
const name = file.name;
|
||||
const data = convertResult(target.result as ArrayBuffer, convertHex);
|
||||
|
||||
onChange && onChange(data, name);
|
||||
dropRef && setFile({
|
||||
name,
|
||||
size: data.length
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
},
|
||||
[convertHex, dropRef, onChange]
|
||||
);
|
||||
|
||||
const dropZone = (
|
||||
<Dropzone
|
||||
accept={accept}
|
||||
disabled={isDisabled}
|
||||
multiple={false}
|
||||
onDrop={_onDrop}
|
||||
ref={dropRef}
|
||||
>
|
||||
{({ getInputProps, getRootProps }): JSX.Element => (
|
||||
<div {...getRootProps({ className: classes('ui--InputFile', isError ? 'error' : '', className) })} >
|
||||
<input {...getInputProps()} />
|
||||
<em className='label' >
|
||||
{
|
||||
!file || clearContent
|
||||
? placeholder || 'click to select or drag and drop the file here'
|
||||
: placeholder || `${file.name} (${formatNumber(file.size)} bytes)`
|
||||
}
|
||||
</em>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
);
|
||||
|
||||
return label
|
||||
? (
|
||||
<Label
|
||||
label={label}
|
||||
>
|
||||
{dropZone}
|
||||
</Label>
|
||||
)
|
||||
: dropZone;
|
||||
}
|
||||
|
||||
export default React.memo(styled(InputFile)`
|
||||
border: 1px solid rgb(67, 68, 75);
|
||||
background: rgb(17, 18, 24);
|
||||
border-radius: 0.28571429rem;
|
||||
font-size: 1rem;
|
||||
margin: 0.25rem 0;
|
||||
color: rgb(255, 255, 255);
|
||||
padding: 0.5rem 0.75rem;
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
&.error {
|
||||
border-color: rgb(126, 53, 48);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`);
|
||||
@ -16,6 +16,7 @@ export { default as Fonts } from './Fonts';
|
||||
export { default as Icon } from './Icon';
|
||||
export { default as Identicon } from './Identicon';
|
||||
export { default as InputWithLabel } from './InputWithLabel';
|
||||
export { default as InputFileWithLabel } from './InputFileWithLabel';
|
||||
export { default as Label } from './Label';
|
||||
export { default as Link } from './Link';
|
||||
export { default as List } from './List';
|
||||
|
||||
@ -3,13 +3,14 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Message } from '@polkadot/extension-base/types';
|
||||
import { AccountJson, AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MetadataRequest, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions, ResponseDeriveValidate } from '@polkadot/extension-base/background/types';
|
||||
import { AccountJson, AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MetadataRequest, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions, ResponseDeriveValidate, ResponseJsonRestore } from '@polkadot/extension-base/background/types';
|
||||
import { Chain } from '@polkadot/extension-chains/types';
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
import { PORT_EXTENSION } from '@polkadot/extension-base/defaults';
|
||||
import chrome from '@polkadot/extension-inject/chrome';
|
||||
import { findChain } from '@polkadot/extension-chains';
|
||||
import { KeyringPair$Json } from '@polkadot/keyring/types';
|
||||
|
||||
interface Handler {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -155,3 +156,19 @@ export async function validateDerivationPath (parentAddress: string, suri: strin
|
||||
export async function deriveAccount (parentAddress: string, suri: string, parentPassword: string, name: string, password: string): Promise<boolean> {
|
||||
return sendMessage('pri(derivation.create)', { name, parentAddress, parentPassword, password, suri });
|
||||
}
|
||||
|
||||
export async function jsonRestoreWindowOpen (): Promise<boolean> {
|
||||
return sendMessage('pri(window.open.json)', null);
|
||||
}
|
||||
|
||||
export async function jsonVerifyFile (json: KeyringPair$Json): Promise<boolean> {
|
||||
return sendMessage('pri(json.verify.file)', { json, password: '' });
|
||||
}
|
||||
|
||||
export async function jsonVerifyPassword (password: string): Promise<boolean> {
|
||||
return sendMessage('pri(json.verify.password)', password);
|
||||
}
|
||||
|
||||
export async function jsonRestore (json: KeyringPair$Json, password: string): Promise<ResponseJsonRestore> {
|
||||
return sendMessage('pri(json.restore)', { json, password });
|
||||
}
|
||||
|
||||
30
yarn.lock
30
yarn.lock
@ -3030,6 +3030,7 @@ __metadata:
|
||||
enzyme-adapter-react-16: ^1.15.2
|
||||
react: ^16.13.1
|
||||
react-dom: ^16.13.1
|
||||
react-dropzone: ^11.0.1
|
||||
react-is: ^16.13.1
|
||||
react-router: ^5.2.0
|
||||
react-router-dom: ^5.2.0
|
||||
@ -5044,6 +5045,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"attr-accept@npm:^2.0.0":
|
||||
version: 2.1.0
|
||||
resolution: "attr-accept@npm:2.1.0"
|
||||
checksum: 3/a31fdf3b37daed32e8f33a46e17b3a85b156900c9b7e564cf9202a4893bf475aeebda41421c0febce65bac02c528cbe603abc18592af907fc4a7b839a181a5e4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"autocomplete.js@npm:0.36.0":
|
||||
version: 0.36.0
|
||||
resolution: "autocomplete.js@npm:0.36.0"
|
||||
@ -9819,6 +9827,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"file-selector@npm:^0.1.12":
|
||||
version: 0.1.12
|
||||
resolution: "file-selector@npm:0.1.12"
|
||||
dependencies:
|
||||
tslib: ^1.9.0
|
||||
checksum: 3/b1a8287dabc8c239998ecb319c5b4eae156bdeb23130d3b5b6bed349f296d7798d0e5ef43e83dad3550aa2b1d1fd45ab7c5302fe8a9e26196676089de565f4ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"file-uri-to-path@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "file-uri-to-path@npm:1.0.0"
|
||||
@ -17262,6 +17279,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-dropzone@npm:^11.0.1":
|
||||
version: 11.0.1
|
||||
resolution: "react-dropzone@npm:11.0.1"
|
||||
dependencies:
|
||||
attr-accept: ^2.0.0
|
||||
file-selector: ^0.1.12
|
||||
prop-types: ^15.7.2
|
||||
peerDependencies:
|
||||
react: ">= 16.8"
|
||||
checksum: 3/c5192e7367df18d77b8b4c219fd9e091d9d63e7a530ec9f983b42e32a738079f0f5f5082057a867ffd7031aa0a703638d7e873476cb6a27b96c0f330e4166523
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-is@npm:^16.12.0, react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0, react-is@npm:^16.8.1, react-is@npm:^16.8.6, react-is@npm:^16.9.0":
|
||||
version: 16.13.1
|
||||
resolution: "react-is@npm:16.13.1"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user