Allow account creation by derivation from existing one (#296)
* Allow specifying derivation path on account creation (#57) * Allow specifying derivation path on account creation * Improve linting and formatting * Highlight input field when der-path is invalid * Fix header * Hide advanced options * Add tests * Fix name * Improve styling * Change names * Fix case * Add arrow icon * Add smooth transition * Refactor tests Co-authored-by: Bartłomiej Rutkowski <bar.rutkow@gmail.com> * [CI Skip] 0.22.0-beta.7 skip-checks: true * Import account derivation path (#59) * Import account derivation path * Fix test * fix scroll * Lint fix * add tests to importSeed Co-authored-by: Bartłomiej Rutkowski <bartek@ethworks.io> Co-authored-by: Kamil <kamil@ethworks.io> * [CI Skip] 0.22.0-beta.8 skip-checks: true * Hover and copy button (#58) * copy button and change hover * working on * add Toast * chage address width * Minor fixes * Add cursor: hand * Finish merge Co-authored-by: Kamil <kamil@ethworks.io> Co-authored-by: ivan <ivanruch@gmail.com> * [CI Skip] 0.22.0-beta.9 skip-checks: true * Fix build * [CI Skip] 0.23.0-beta.0 skip-checks: true * [CI Skip] Undo Github Actions version bump * [CI Skip] 0.23.0-beta.5 skip-checks: true * Add derive screen (#60) * Add derivation screen * Remove "///password" from derivation path placeholder * Refactor tests Co-authored-by: Bartłomiej Rutkowski <bar.rutkow@gmail.com> Co-authored-by: Anze1m <bartek@ethworks.io> * Improve account menu; fix linting (#61) * Fix test * Fix account menu * Fix linting * [CI Skip] 0.23.0-beta.6 skip-checks: true * Add proper account decoding (#62) * Add proper account decoding * Improve tests and error handling * Fix linting * Add refactoring * Update packages/extension/src/background/handlers/Extension.ts Co-Authored-By: Ivan Rukhavets <ivanruch@gmail.com> * Fix test * Fix linting * Fix actions * Slight UX improvement * Add big red button * Change red button to orange * Wrap in useCallback * Fix linting * Add proper password checking method * Fix linting Co-authored-by: Ivan Rukhavets <ivanruch@gmail.com> * [CI Skip] 0.23.0-beta.7 skip-checks: true * Apply merge fixes (#65) * Fix linting * CI * [CI Skip] 0.24.0-beta.1 skip-checks: true * CR requests * [CI Skip] 0.24.0-beta.2 skip-checks: true * [CI Skip] 0.24.0-beta.2 skip-checks: true Co-authored-by: Ivan Rukhavets <ivanruch@gmail.com> Co-authored-by: Github Actions <action@github.com> Co-authored-by: Kamil <kamil@ethworks.io>
This commit is contained in:
parent
6d83833545
commit
eeac2c1064
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@ yarn-error.log*
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
_book
|
_book
|
||||||
docs/html
|
docs/html
|
||||||
|
.idea
|
||||||
@ -9,5 +9,5 @@
|
|||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"version": "0.24.0-beta.1"
|
"version": "0.24.0-beta.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@polkadot/extension-base",
|
"name": "@polkadot/extension-base",
|
||||||
"description": "Functions, classes and other utilities used in @polkadot/extension",
|
"description": "Functions, classes and other utilities used in @polkadot/extension",
|
||||||
"version": "0.24.0-beta.1",
|
"version": "0.24.0-beta.2",
|
||||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||||
"license": "Apache-2",
|
"license": "Apache-2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -2,8 +2,6 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { ResponseAccountExport } from '../types';
|
|
||||||
|
|
||||||
import keyring from '@polkadot/ui-keyring';
|
import keyring from '@polkadot/ui-keyring';
|
||||||
import ExtensionStore from '@polkadot/ui-keyring/stores/Extension';
|
import ExtensionStore from '@polkadot/ui-keyring/stores/Extension';
|
||||||
import { cryptoWaitReady } from '@polkadot/util-crypto';
|
import { cryptoWaitReady } from '@polkadot/util-crypto';
|
||||||
@ -17,18 +15,92 @@ describe('Extension', () => {
|
|||||||
keyring.loadAll({ store: new ExtensionStore() });
|
keyring.loadAll({ store: new ExtensionStore() });
|
||||||
return new Extension(new State());
|
return new Extension(new State());
|
||||||
}
|
}
|
||||||
|
let extension: Extension;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
extension = await createExtension();
|
||||||
|
});
|
||||||
|
|
||||||
|
const suri = 'seed sock milk update focus rotate barely fade car face mechanic mercy';
|
||||||
|
const password = 'passw0rd';
|
||||||
|
|
||||||
test('exports account from keyring', async () => {
|
test('exports account from keyring', async () => {
|
||||||
const extension = await createExtension();
|
const { pair: { address } } = keyring.addUri(suri, password);
|
||||||
const { pair: { address } } = keyring.addUri(
|
|
||||||
'seed sock milk update focus rotate barely fade car face mechanic mercy', 'passw0rd'
|
|
||||||
);
|
|
||||||
const result = await extension.handle('id', 'pri(accounts.export)', {
|
const result = await extension.handle('id', 'pri(accounts.export)', {
|
||||||
address,
|
address,
|
||||||
password: 'passw0rd'
|
password
|
||||||
}, {} as chrome.runtime.Port) as ResponseAccountExport;
|
}, {} as chrome.runtime.Port);
|
||||||
|
|
||||||
expect(result.exportedJson).toContain(address);
|
expect(result.exportedJson).toContain(address);
|
||||||
expect(result.exportedJson).toContain('"encoded"');
|
expect(result.exportedJson).toContain('"encoded"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('account derivation', () => {
|
||||||
|
const createAccount = async (): Promise<string> => {
|
||||||
|
await extension.handle('id', 'pri(accounts.create.suri)', {
|
||||||
|
name: 'parent',
|
||||||
|
password,
|
||||||
|
suri
|
||||||
|
}, {} as chrome.runtime.Port);
|
||||||
|
const { address } = await extension.handle('id', 'pri(seed.validate)', {
|
||||||
|
suri
|
||||||
|
}, {} as chrome.runtime.Port);
|
||||||
|
return address;
|
||||||
|
};
|
||||||
|
let address: string;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
address = await createAccount();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pri(derivation.validate) passes for valid suri', async () => {
|
||||||
|
const result = await extension.handle('id', 'pri(derivation.validate)', {
|
||||||
|
parentAddress: address,
|
||||||
|
suri: '//path',
|
||||||
|
parentPassword: password
|
||||||
|
}, {} as chrome.runtime.Port);
|
||||||
|
expect(result).toStrictEqual({
|
||||||
|
address: '5FP3TT3EruYBNh8YM8yoxsreMx7uZv1J1zNX7fFhoC5enwmN',
|
||||||
|
suri: '//path'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pri(derivation.validate) throws for invalid suri', async () => {
|
||||||
|
await expect(extension.handle('id', 'pri(derivation.validate)', {
|
||||||
|
parentAddress: address,
|
||||||
|
suri: 'invalid-path',
|
||||||
|
parentPassword: password
|
||||||
|
}, {} as chrome.runtime.Port)).rejects.toStrictEqual(new Error('"invalid-path" is not a valid derivation path'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pri(derivation.validate) throws for invalid password', async () => {
|
||||||
|
await expect(extension.handle('id', 'pri(derivation.validate)', {
|
||||||
|
parentAddress: address,
|
||||||
|
suri: '//path',
|
||||||
|
parentPassword: 'invalid-password'
|
||||||
|
}, {} as chrome.runtime.Port)).rejects.toStrictEqual(new Error('invalid password'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pri(derivation.create) adds a derived account', async () => {
|
||||||
|
await extension.handle('id', 'pri(derivation.create)', {
|
||||||
|
parentAddress: address,
|
||||||
|
name: 'child',
|
||||||
|
suri: '//path',
|
||||||
|
parentPassword: password,
|
||||||
|
password
|
||||||
|
}, {} as chrome.runtime.Port);
|
||||||
|
expect(keyring.getAccounts()).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pri(derivation.create) saves parent address in meta', async () => {
|
||||||
|
await extension.handle('id', 'pri(derivation.create)', {
|
||||||
|
parentAddress: address,
|
||||||
|
name: 'child',
|
||||||
|
suri: '//path',
|
||||||
|
parentPassword: password,
|
||||||
|
password
|
||||||
|
}, {} as chrome.runtime.Port);
|
||||||
|
expect(keyring.getAccount('5FP3TT3EruYBNh8YM8yoxsreMx7uZv1J1zNX7fFhoC5enwmN')?.meta.parentAddress).toEqual(address);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4,12 +4,42 @@
|
|||||||
|
|
||||||
import { MetadataDef } from '@polkadot/extension-inject/types';
|
import { MetadataDef } from '@polkadot/extension-inject/types';
|
||||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||||
import { AccountJson, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountCreateExternal, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAuthorizeApprove, RequestAuthorizeReject, RequestMetadataApprove, RequestMetadataReject, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSeedCreate, RequestTypes, ResponseAccountExport, RequestAccountForget, ResponseSeedCreate, RequestSeedValidate, ResponseSeedValidate, ResponseTypes, 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
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
import extension from 'extensionizer';
|
import extension from 'extensionizer';
|
||||||
import keyring from '@polkadot/ui-keyring';
|
import keyring from '@polkadot/ui-keyring';
|
||||||
import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
|
import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||||
import { TypeRegistry } from '@polkadot/types';
|
import { TypeRegistry } from '@polkadot/types';
|
||||||
|
import { KeyringPair, KeyringPair$Meta } from '@polkadot/keyring/types';
|
||||||
import { assert, isHex } from '@polkadot/util';
|
import { assert, isHex } from '@polkadot/util';
|
||||||
import { keyExtractSuri, mnemonicGenerate, mnemonicValidate } from '@polkadot/util-crypto';
|
import { keyExtractSuri, mnemonicGenerate, mnemonicValidate } from '@polkadot/util-crypto';
|
||||||
|
|
||||||
@ -62,6 +92,15 @@ export default class Extension {
|
|||||||
return { exportedJson: JSON.stringify(keyring.backupAccount(keyring.getPair(address), password)) };
|
return { exportedJson: JSON.stringify(keyring.backupAccount(keyring.getPair(address), password)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private accountsValidate ({ address, password }: RequestAccountValidate): boolean {
|
||||||
|
try {
|
||||||
|
keyring.backupAccount(keyring.getPair(address), password);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private accountsForget ({ address }: RequestAccountForget): boolean {
|
private accountsForget ({ address }: RequestAccountForget): boolean {
|
||||||
keyring.forgetAccount(address);
|
keyring.forgetAccount(address);
|
||||||
|
|
||||||
@ -265,9 +304,40 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private derive (parentAddress: string, suri: string, password: string, metadata: KeyringPair$Meta): KeyringPair {
|
||||||
|
const parentPair = keyring.getPair(parentAddress);
|
||||||
|
|
||||||
|
try {
|
||||||
|
parentPair.decodePkcs8(password);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('invalid password');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return parentPair.derive(suri, metadata);
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`"${suri}" is not a valid derivation path`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private derivationValidate ({ parentAddress, suri, parentPassword }: RequestDeriveValidate): ResponseDeriveValidate {
|
||||||
|
const childPair = this.derive(parentAddress, suri, parentPassword, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
address: childPair.address,
|
||||||
|
suri
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private derivationCreate ({ parentAddress, suri, parentPassword, genesisHash, name, password }: RequestDeriveCreate): boolean {
|
||||||
|
const childPair = this.derive(parentAddress, suri, parentPassword, { genesisHash, name, parentAddress });
|
||||||
|
keyring.addPair(childPair, password);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Weird thought, the eslint override is not needed in Tabs
|
// Weird thought, the eslint override is not needed in Tabs
|
||||||
// eslint-disable-next-line @typescript-eslint/require-await
|
// eslint-disable-next-line @typescript-eslint/require-await
|
||||||
public async handle<TMessageType extends MessageTypes> (id: string, type: TMessageType, request: RequestTypes[TMessageType], port: chrome.runtime.Port): Promise<ResponseTypes[keyof ResponseTypes]> {
|
public async handle<TMessageType extends MessageTypes> (id: string, type: TMessageType, request: RequestTypes[TMessageType], port: chrome.runtime.Port): Promise<ResponseType<TMessageType>> {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'pri(authorize.approve)':
|
case 'pri(authorize.approve)':
|
||||||
return this.authorizeApprove(request as RequestAuthorizeApprove);
|
return this.authorizeApprove(request as RequestAuthorizeApprove);
|
||||||
@ -293,6 +363,9 @@ export default class Extension {
|
|||||||
case 'pri(accounts.forget)':
|
case 'pri(accounts.forget)':
|
||||||
return this.accountsForget(request as RequestAccountForget);
|
return this.accountsForget(request as RequestAccountForget);
|
||||||
|
|
||||||
|
case 'pri(accounts.validate)':
|
||||||
|
return this.accountsValidate(request as RequestAccountValidate);
|
||||||
|
|
||||||
case 'pri(accounts.subscribe)':
|
case 'pri(accounts.subscribe)':
|
||||||
return this.accountsSubscribe(id, port);
|
return this.accountsSubscribe(id, port);
|
||||||
|
|
||||||
@ -308,6 +381,12 @@ export default class Extension {
|
|||||||
case 'pri(metadata.requests)':
|
case 'pri(metadata.requests)':
|
||||||
return this.metadataSubscribe(id, port);
|
return this.metadataSubscribe(id, port);
|
||||||
|
|
||||||
|
case 'pri(derivation.create)':
|
||||||
|
return this.derivationCreate(request as RequestDeriveCreate);
|
||||||
|
|
||||||
|
case 'pri(derivation.validate)':
|
||||||
|
return this.derivationValidate(request as RequestDeriveValidate);
|
||||||
|
|
||||||
case 'pri(seed.create)':
|
case 'pri(seed.create)':
|
||||||
return this.seedCreate(request as RequestSeedCreate);
|
return this.seedCreate(request as RequestSeedCreate);
|
||||||
|
|
||||||
|
|||||||
@ -57,10 +57,13 @@ export interface RequestSignatures {
|
|||||||
'pri(accounts.edit)': [RequestAccountEdit, boolean];
|
'pri(accounts.edit)': [RequestAccountEdit, boolean];
|
||||||
'pri(accounts.export)': [RequestAccountExport, ResponseAccountExport];
|
'pri(accounts.export)': [RequestAccountExport, ResponseAccountExport];
|
||||||
'pri(accounts.forget)': [RequestAccountForget, boolean];
|
'pri(accounts.forget)': [RequestAccountForget, boolean];
|
||||||
|
'pri(accounts.validate)': [RequestAccountValidate, boolean];
|
||||||
'pri(accounts.subscribe)': [RequestAccountSubscribe, boolean, AccountJson[]];
|
'pri(accounts.subscribe)': [RequestAccountSubscribe, boolean, AccountJson[]];
|
||||||
'pri(authorize.approve)': [RequestAuthorizeApprove, boolean];
|
'pri(authorize.approve)': [RequestAuthorizeApprove, boolean];
|
||||||
'pri(authorize.reject)': [RequestAuthorizeReject, boolean];
|
'pri(authorize.reject)': [RequestAuthorizeReject, boolean];
|
||||||
'pri(authorize.requests)': [RequestAuthorizeSubscribe, boolean, AuthorizeRequest[]];
|
'pri(authorize.requests)': [RequestAuthorizeSubscribe, boolean, AuthorizeRequest[]];
|
||||||
|
'pri(derivation.create)': [RequestDeriveCreate, boolean];
|
||||||
|
'pri(derivation.validate)': [RequestDeriveValidate, ResponseDeriveValidate];
|
||||||
'pri(metadata.approve)': [RequestMetadataApprove, boolean];
|
'pri(metadata.approve)': [RequestMetadataApprove, boolean];
|
||||||
'pri(metadata.reject)': [RequestMetadataReject, boolean];
|
'pri(metadata.reject)': [RequestMetadataReject, boolean];
|
||||||
'pri(metadata.requests)': [RequestMetadataSubscribe, boolean, MetadataRequest[]];
|
'pri(metadata.requests)': [RequestMetadataSubscribe, boolean, MetadataRequest[]];
|
||||||
@ -153,6 +156,26 @@ export interface RequestAccountForget {
|
|||||||
address: string;
|
address: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RequestAccountValidate {
|
||||||
|
address: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestDeriveCreate {
|
||||||
|
name: string;
|
||||||
|
genesisHash?: string | null;
|
||||||
|
suri: string;
|
||||||
|
parentAddress: string;
|
||||||
|
parentPassword: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestDeriveValidate {
|
||||||
|
suri: string;
|
||||||
|
parentAddress: string;
|
||||||
|
parentPassword: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface RequestAccountExport {
|
export interface RequestAccountExport {
|
||||||
address: string;
|
address: string;
|
||||||
password: string;
|
password: string;
|
||||||
@ -210,6 +233,8 @@ export type ResponseTypes = {
|
|||||||
[MessageType in keyof RequestSignatures]: RequestSignatures[MessageType][1]
|
[MessageType in keyof RequestSignatures]: RequestSignatures[MessageType][1]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ResponseType<TMessageType extends keyof RequestSignatures> = RequestSignatures[TMessageType][1];
|
||||||
|
|
||||||
interface TransportResponseMessageSub<TMessageType extends MessageTypesWithSubscriptions> {
|
interface TransportResponseMessageSub<TMessageType extends MessageTypesWithSubscriptions> {
|
||||||
error?: string;
|
error?: string;
|
||||||
id: string;
|
id: string;
|
||||||
@ -235,6 +260,11 @@ export interface ResponseSigning {
|
|||||||
signature: string;
|
signature: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ResponseDeriveValidate {
|
||||||
|
address: string;
|
||||||
|
suri: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ResponseSeedCreate {
|
export interface ResponseSeedCreate {
|
||||||
address: string;
|
address: string;
|
||||||
seed: string;
|
seed: string;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@polkadot/extension-chains",
|
"name": "@polkadot/extension-chains",
|
||||||
"description": "Definitions for all known chains as exposed by the extension.",
|
"description": "Definitions for all known chains as exposed by the extension.",
|
||||||
"version": "0.24.0-beta.1",
|
"version": "0.24.0-beta.2",
|
||||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||||
"license": "Apache-2",
|
"license": "Apache-2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@polkadot/extension-dapp",
|
"name": "@polkadot/extension-dapp",
|
||||||
"description": "Provides an interfaces around the injected globals for ease of access by dapp developers.",
|
"description": "Provides an interfaces around the injected globals for ease of access by dapp developers.",
|
||||||
"version": "0.24.0-beta.1",
|
"version": "0.24.0-beta.2",
|
||||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||||
"license": "Apache-2",
|
"license": "Apache-2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.9.2",
|
"@babel/runtime": "^7.9.2",
|
||||||
"@polkadot/extension-inject": "0.24.0-beta.1"
|
"@polkadot/extension-inject": "0.24.0-beta.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@polkadot/api": "*"
|
"@polkadot/api": "*"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@polkadot/extension-inject",
|
"name": "@polkadot/extension-inject",
|
||||||
"description": "A generic injector (usable to any extension), that populates the base exposed interfaces to be used by dapps.",
|
"description": "A generic injector (usable to any extension), that populates the base exposed interfaces to be used by dapps.",
|
||||||
"version": "0.24.0-beta.1",
|
"version": "0.24.0-beta.2",
|
||||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||||
"license": "Apache-2",
|
"license": "Apache-2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@polkadot/extension-ui",
|
"name": "@polkadot/extension-ui",
|
||||||
"description": "A sample signer extension for the @polkadot/api",
|
"description": "A sample signer extension for the @polkadot/api",
|
||||||
"version": "0.24.0-beta.1",
|
"version": "0.24.0-beta.2",
|
||||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||||
"license": "Apache-2",
|
"license": "Apache-2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -31,10 +31,11 @@ describe('Account component', () => {
|
|||||||
wrapper = mountAccountComponent({ isExternal: false });
|
wrapper = mountAccountComponent({ isExternal: false });
|
||||||
wrapper.find('Details').simulate('click');
|
wrapper.find('Details').simulate('click');
|
||||||
|
|
||||||
expect(wrapper.find('MenuItem').length).toBe(3);
|
expect(wrapper.find('MenuItem').length).toBe(4);
|
||||||
expect(wrapper.find('MenuItem').at(0).text()).toBe('Rename');
|
expect(wrapper.find('MenuItem').at(0).text()).toBe('Rename');
|
||||||
expect(wrapper.find('MenuItem').at(1).text()).toBe('Export Account');
|
expect(wrapper.find('MenuItem').at(1).text()).toBe('Derive New Account');
|
||||||
expect(wrapper.find('MenuItem').at(2).text()).toBe('Forget Account');
|
expect(wrapper.find('MenuItem').at(2).text()).toBe('Export Account');
|
||||||
|
expect(wrapper.find('MenuItem').at(3).text()).toBe('Forget Account');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not show Export option if account is external', () => {
|
it('does not show Export option if account is external', () => {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import { AccountJson } from '@polkadot/extension-base/background/types';
|
import { AccountJson } from '@polkadot/extension-base/background/types';
|
||||||
|
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useCallback, useContext, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { ActionContext, Address, Link } from '../../components';
|
import { ActionContext, Address, Link } from '../../components';
|
||||||
@ -21,8 +21,8 @@ function Account ({ address, className, isExternal }: Props): React.ReactElement
|
|||||||
const [isEditing, setEditing] = useState(false);
|
const [isEditing, setEditing] = useState(false);
|
||||||
const [editedName, setName] = useState<string | null>(null);
|
const [editedName, setName] = useState<string | null>(null);
|
||||||
|
|
||||||
const _toggleEdit = (): void => setEditing(!isEditing);
|
const _toggleEdit = useCallback((): void => setEditing(!isEditing), [isEditing]);
|
||||||
const _saveChanges = (): void => {
|
const _saveChanges = useCallback((): void => {
|
||||||
if (editedName && editedName !== name) {
|
if (editedName && editedName !== name) {
|
||||||
editAccount(address, editedName)
|
editAccount(address, editedName)
|
||||||
.then((): void => onAction())
|
.then((): void => onAction())
|
||||||
@ -30,7 +30,7 @@ function Account ({ address, className, isExternal }: Props): React.ReactElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
_toggleEdit();
|
_toggleEdit();
|
||||||
};
|
}, [editedName, address, _toggleEdit, onAction]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
@ -41,6 +41,7 @@ function Account ({ address, className, isExternal }: Props): React.ReactElement
|
|||||||
<>
|
<>
|
||||||
<MenuGroup>
|
<MenuGroup>
|
||||||
<MenuItem onClick={_toggleEdit}>Rename</MenuItem>
|
<MenuItem onClick={_toggleEdit}>Rename</MenuItem>
|
||||||
|
{!isExternal && <MenuItem to={`/account/derive/${address}`}>Derive New Account</MenuItem>}
|
||||||
</MenuGroup>
|
</MenuGroup>
|
||||||
{!isExternal && <MenuItem isDanger to={`/account/export/${address}`}>Export Account</MenuItem>}
|
{!isExternal && <MenuItem isDanger to={`/account/export/${address}`}>Export Account</MenuItem>}
|
||||||
<MenuItem isDanger to={`/account/forget/${address}`}>Forget Account</MenuItem>
|
<MenuItem isDanger to={`/account/forget/${address}`}>Forget Account</MenuItem>
|
||||||
@ -51,7 +52,7 @@ function Account ({ address, className, isExternal }: Props): React.ReactElement
|
|||||||
<Name
|
<Name
|
||||||
address={address}
|
address={address}
|
||||||
className='edit-name'
|
className='edit-name'
|
||||||
isFocussed
|
isFocused
|
||||||
label={' '}
|
label={' '}
|
||||||
onBlur={_saveChanges}
|
onBlur={_saveChanges}
|
||||||
onChange={setName}
|
onChange={setName}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ function AccountName ({ onCreate, address }: Props): React.ReactElement<Props> {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Name
|
<Name
|
||||||
isFocussed
|
isFocused
|
||||||
onChange={setName}
|
onChange={setName}
|
||||||
/>
|
/>
|
||||||
{name && <Password onChange={setPassword} />}
|
{name && <Password onChange={setPassword} />}
|
||||||
|
|||||||
41
packages/extension-ui/src/Popup/Derive/Step.tsx
Normal file
41
packages/extension-ui/src/Popup/Derive/Step.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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 React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Header } from '@polkadot/extension-ui/components';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
step: number;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Step ({ step, className }: Props): React.ReactElement<Props> {
|
||||||
|
return (
|
||||||
|
<Header text={
|
||||||
|
<>
|
||||||
|
<span>Derive new account</span>
|
||||||
|
<CurrentStep>{step}</CurrentStep>
|
||||||
|
<TotalSteps>/2</TotalSteps>
|
||||||
|
</>
|
||||||
|
} showBackArrow className={className} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CurrentStep = styled.span`
|
||||||
|
font-size: ${({ theme }): string => theme.labelFontSize};
|
||||||
|
line-height: ${({ theme }): string => theme.labelLineHeight};
|
||||||
|
color: ${({ theme }): string => theme.primaryColor};
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TotalSteps = styled.span`
|
||||||
|
font-size: ${({ theme }): string => theme.labelFontSize};
|
||||||
|
line-height: ${({ theme }): string => theme.labelLineHeight};
|
||||||
|
color: ${({ theme }): string => theme.textColor};
|
||||||
|
font-weight: 600;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Step;
|
||||||
82
packages/extension-ui/src/Popup/Derive/index.tsx
Normal file
82
packages/extension-ui/src/Popup/Derive/index.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// 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 React, { useCallback, useContext, useState } from 'react';
|
||||||
|
import { RouteComponentProps } from 'react-router';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { ActionContext, Address, Button, ButtonArea, InputWithLabel, VerticalSpace } from '../../components';
|
||||||
|
import { deriveAccount, validateAccount } from '../../messaging';
|
||||||
|
import { DerivationPath, Name, Password } from '../../partials';
|
||||||
|
import Step from './Step';
|
||||||
|
|
||||||
|
type Props = RouteComponentProps<{ address: string }>;
|
||||||
|
|
||||||
|
export default function Derive ({ match: { params: { address: parentAddress } } }: Props): React.ReactElement<Props> {
|
||||||
|
const onAction = useContext(ActionContext);
|
||||||
|
const [account, setAccount] = useState<null | { address: string; suri: string }>(null);
|
||||||
|
const [name, setName] = useState<string | null>(null);
|
||||||
|
const [password, setPassword] = useState<string | null>(null);
|
||||||
|
const [parentPassword, setParentPassword] = useState<string | null>(null);
|
||||||
|
const [isProperParentPassword, setIsProperParentPassword] = useState(false);
|
||||||
|
const [derivationConfirmed, setDerivationConfirmed] = useState(false);
|
||||||
|
|
||||||
|
const _onCreate = useCallback(async () => {
|
||||||
|
if (!account || !name || !password || !parentPassword) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await deriveAccount(parentAddress, account.suri, parentPassword, name, password);
|
||||||
|
onAction('/');
|
||||||
|
}, [account, name, password, onAction, parentAddress, parentPassword]);
|
||||||
|
|
||||||
|
const _onParentPasswordEnter = useCallback(async (enteredPassword: string) => {
|
||||||
|
setParentPassword(enteredPassword);
|
||||||
|
setIsProperParentPassword(await validateAccount(parentAddress, enteredPassword));
|
||||||
|
}, [parentAddress]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Step step={derivationConfirmed ? 2 : 1} />
|
||||||
|
{!derivationConfirmed && <InputWithLabel
|
||||||
|
isFocused
|
||||||
|
isError={!isProperParentPassword}
|
||||||
|
label='enter the password for the account you want to derive from'
|
||||||
|
onChange={_onParentPasswordEnter}
|
||||||
|
type='password'
|
||||||
|
data-export-password
|
||||||
|
/>}
|
||||||
|
{!derivationConfirmed && <DeriveButton
|
||||||
|
isDisabled={!isProperParentPassword}
|
||||||
|
onClick={(): void => setDerivationConfirmed(true)}
|
||||||
|
>
|
||||||
|
I want to derive from this account
|
||||||
|
</DeriveButton>}
|
||||||
|
{isProperParentPassword && derivationConfirmed && <Name onChange={setName} isFocused />}
|
||||||
|
{isProperParentPassword && derivationConfirmed && parentPassword && name && <DerivationPath
|
||||||
|
onChange={setAccount}
|
||||||
|
parentAddress={parentAddress}
|
||||||
|
parentPassword={parentPassword}
|
||||||
|
/>}
|
||||||
|
{isProperParentPassword && derivationConfirmed && account && name && <Password onChange={setPassword}/>}
|
||||||
|
{isProperParentPassword && derivationConfirmed && account && name && password && (
|
||||||
|
<>
|
||||||
|
<Address
|
||||||
|
address={account.address}
|
||||||
|
name={name}
|
||||||
|
/>
|
||||||
|
<VerticalSpace/>
|
||||||
|
<ButtonArea>
|
||||||
|
<Button onClick={_onCreate}>Create derived account</Button>
|
||||||
|
</ButtonArea>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeriveButton = styled(Button)`
|
||||||
|
margin-left: 24px;
|
||||||
|
margin-right: 24px;
|
||||||
|
width: auto;
|
||||||
|
`;
|
||||||
@ -36,7 +36,7 @@ export default function ImportQr (): React.ReactElement<Props> {
|
|||||||
{account && (
|
{account && (
|
||||||
<>
|
<>
|
||||||
<Name
|
<Name
|
||||||
isFocussed
|
isFocused
|
||||||
onChange={setName}
|
onChange={setName}
|
||||||
/>
|
/>
|
||||||
<Address
|
<Address
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import { subscribeAccounts, subscribeAuthorizeRequests, subscribeMetadataRequest
|
|||||||
import Accounts from './Accounts';
|
import Accounts from './Accounts';
|
||||||
import Authorize from './Authorize';
|
import Authorize from './Authorize';
|
||||||
import CreateAccount from './CreateAccount';
|
import CreateAccount from './CreateAccount';
|
||||||
|
import Derive from './Derive';
|
||||||
import Export from './Export';
|
import Export from './Export';
|
||||||
import Forget from './Forget';
|
import Forget from './Forget';
|
||||||
import ImportQr from './ImportQr';
|
import ImportQr from './ImportQr';
|
||||||
@ -105,6 +106,7 @@ export default function Popup (): React.ReactElement<{}> {
|
|||||||
<Route path='/account/export/:address' component={Export} />
|
<Route path='/account/export/:address' component={Export} />
|
||||||
<Route path='/account/import-qr' component={ImportQr} />
|
<Route path='/account/import-qr' component={ImportQr} />
|
||||||
<Route path='/account/import-seed' component={ImportSeed} />
|
<Route path='/account/import-seed' component={ImportSeed} />
|
||||||
|
<Route path='/account/derive/:address' component={Derive} />
|
||||||
<Route exact path='/' component={Root} />
|
<Route exact path='/' component={Root} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</SigningReqContext.Provider>
|
</SigningReqContext.Provider>
|
||||||
|
|||||||
@ -17,7 +17,7 @@ interface Props {
|
|||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
showSettings?: boolean;
|
showSettings?: boolean;
|
||||||
text?: string;
|
text?: React.ReactNode;
|
||||||
showBackArrow?: boolean;
|
showBackArrow?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,9 +19,10 @@ interface Props {
|
|||||||
onChange?: (value: string) => void;
|
onChange?: (value: string) => void;
|
||||||
type?: 'text' | 'password';
|
type?: 'text' | 'password';
|
||||||
value?: string;
|
value?: string;
|
||||||
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function InputWithLabel ({ className, defaultValue, label, isFocused, isReadOnly, onBlur, onChange, type = 'text', value, isError }: Props): React.ReactElement<Props> {
|
function InputWithLabel ({ className, defaultValue, label, isError, isFocused, isReadOnly, onBlur, onChange, placeholder, type = 'text', value }: Props): React.ReactElement<Props> {
|
||||||
const _onChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>): void => {
|
const _onChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
onChange && onChange(value.trim());
|
onChange && onChange(value.trim());
|
||||||
};
|
};
|
||||||
@ -43,6 +44,7 @@ function InputWithLabel ({ className, defaultValue, label, isFocused, isReadOnly
|
|||||||
type={type}
|
type={type}
|
||||||
value={value}
|
value={value}
|
||||||
withError={isError}
|
withError={isError}
|
||||||
|
placeholder={placeholder}
|
||||||
/>
|
/>
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,7 +2,21 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { AccountJson, AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MetadataRequest, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions } from '@polkadot/extension-base/background/types';
|
import {
|
||||||
|
AccountJson,
|
||||||
|
AuthorizeRequest,
|
||||||
|
SigningRequest,
|
||||||
|
RequestTypes,
|
||||||
|
MessageTypes,
|
||||||
|
ResponseTypes,
|
||||||
|
SeedLengths,
|
||||||
|
SubscriptionMessageTypes,
|
||||||
|
MetadataRequest,
|
||||||
|
MessageTypesWithNullRequest,
|
||||||
|
MessageTypesWithNoSubscriptions,
|
||||||
|
MessageTypesWithSubscriptions,
|
||||||
|
ResponseDeriveValidate
|
||||||
|
} from '@polkadot/extension-base/background/types';
|
||||||
import { Chain } from '@polkadot/extension-chains/types';
|
import { Chain } from '@polkadot/extension-chains/types';
|
||||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||||
|
|
||||||
@ -68,6 +82,10 @@ export async function exportAccount (address: string, password: string): Promise
|
|||||||
return sendMessage('pri(accounts.export)', { address, password });
|
return sendMessage('pri(accounts.export)', { address, password });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function validateAccount (address: string, password: string): Promise<boolean> {
|
||||||
|
return sendMessage('pri(accounts.validate)', { address, password });
|
||||||
|
}
|
||||||
|
|
||||||
export async function forgetAccount (address: string): Promise<boolean> {
|
export async function forgetAccount (address: string): Promise<boolean> {
|
||||||
return sendMessage('pri(accounts.forget)', { address });
|
return sendMessage('pri(accounts.forget)', { address });
|
||||||
}
|
}
|
||||||
@ -141,3 +159,11 @@ export async function validateSeed (suri: string, type?: KeypairType): Promise<{
|
|||||||
export async function windowOpen (): Promise<boolean> {
|
export async function windowOpen (): Promise<boolean> {
|
||||||
return sendMessage('pri(window.open)', null);
|
return sendMessage('pri(window.open)', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function validateDerivationPath (parentAddress: string, suri: string, parentPassword: string): Promise<ResponseDeriveValidate> {
|
||||||
|
return sendMessage('pri(derivation.validate)', { parentAddress, suri, parentPassword });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deriveAccount (parentAddress: string, suri: string, parentPassword: string, name: string, password: string): Promise<boolean> {
|
||||||
|
return sendMessage('pri(derivation.create)', { parentAddress, suri, parentPassword, name, password });
|
||||||
|
}
|
||||||
|
|||||||
43
packages/extension-ui/src/partials/DerivationPath.tsx
Normal file
43
packages/extension-ui/src/partials/DerivationPath.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// 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 React, { useCallback, useState } from 'react';
|
||||||
|
import { InputWithLabel } from '@polkadot/extension-ui/components';
|
||||||
|
import { validateDerivationPath } from '@polkadot/extension-ui/messaging';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onChange: (derivedAccount: { address: string; suri: string } | null) => void;
|
||||||
|
parentAddress: string;
|
||||||
|
parentPassword: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DerivationPath ({ onChange, parentAddress, parentPassword }: Props): React.ReactElement<Props> {
|
||||||
|
const [path, setPath] = useState<string>('');
|
||||||
|
const [isValid, setValid] = useState(false);
|
||||||
|
|
||||||
|
const _onChange = useCallback(async (newPath: string) => {
|
||||||
|
setPath(newPath);
|
||||||
|
try {
|
||||||
|
onChange(await validateDerivationPath(parentAddress, newPath, parentPassword));
|
||||||
|
setValid(true);
|
||||||
|
} catch (err) {
|
||||||
|
onChange(null);
|
||||||
|
setValid(false);
|
||||||
|
}
|
||||||
|
}, [parentAddress, onChange, parentPassword]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<InputWithLabel
|
||||||
|
label='Derivation path'
|
||||||
|
placeholder='//hard/soft'
|
||||||
|
onChange={_onChange}
|
||||||
|
value={path}
|
||||||
|
isError={!isValid}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DerivationPath;
|
||||||
@ -10,7 +10,7 @@ interface Props {
|
|||||||
address?: string;
|
address?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
defaultValue?: string | null;
|
defaultValue?: string | null;
|
||||||
isFocussed?: boolean;
|
isFocused?: boolean;
|
||||||
label?: string;
|
label?: string;
|
||||||
onBlur?: () => void;
|
onBlur?: () => void;
|
||||||
onChange: (name: string | null) => void;
|
onChange: (name: string | null) => void;
|
||||||
@ -18,7 +18,7 @@ interface Props {
|
|||||||
|
|
||||||
const MIN_LENGTH = 3;
|
const MIN_LENGTH = 3;
|
||||||
|
|
||||||
export default function Name ({ address, className, defaultValue, isFocussed, label = 'A descriptive name for your account', onBlur, onChange }: Props): React.ReactElement<Props> {
|
export default function Name ({ address, className, defaultValue, isFocused, label = 'A descriptive name for your account', onBlur, onChange }: Props): React.ReactElement<Props> {
|
||||||
const accounts = useContext(AccountContext);
|
const accounts = useContext(AccountContext);
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const account = accounts.find((account): boolean => account.address === address);
|
const account = accounts.find((account): boolean => account.address === address);
|
||||||
@ -41,7 +41,7 @@ export default function Name ({ address, className, defaultValue, isFocussed, la
|
|||||||
className={className}
|
className={className}
|
||||||
defaultValue={startValue}
|
defaultValue={startValue}
|
||||||
isError={isError}
|
isError={isError}
|
||||||
isFocused={isFocussed}
|
isFocused={isFocused}
|
||||||
label={label}
|
label={label}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
onChange={setName}
|
onChange={setName}
|
||||||
|
|||||||
@ -5,3 +5,4 @@
|
|||||||
export { default as Back } from './Back';
|
export { default as Back } from './Back';
|
||||||
export { default as Name } from './Name';
|
export { default as Name } from './Name';
|
||||||
export { default as Password } from './Password';
|
export { default as Password } from './Password';
|
||||||
|
export { default as DerivationPath } from './DerivationPath';
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "@polkadot/extension",
|
"name": "@polkadot/extension",
|
||||||
"description": "A sample signer extension for the @polkadot/api",
|
"description": "A sample signer extension for the @polkadot/api",
|
||||||
"version": "0.24.0-beta.1",
|
"version": "0.24.0-beta.2",
|
||||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||||
"license": "Apache-2",
|
"license": "Apache-2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.9.2",
|
"@babel/runtime": "^7.9.2",
|
||||||
"@polkadot/extension-base": "0.24.0-beta.1",
|
"@polkadot/extension-base": "0.24.0-beta.2",
|
||||||
"@polkadot/extension-ui": "0.24.0-beta.1"
|
"@polkadot/extension-ui": "0.24.0-beta.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@polkadot/dev": "^0.51.9",
|
"@polkadot/dev": "^0.51.9",
|
||||||
|
|||||||
12
yarn.lock
12
yarn.lock
@ -2871,7 +2871,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@polkadot/extension-base@0.24.0-beta.1, @polkadot/extension-base@workspace:packages/extension-base":
|
"@polkadot/extension-base@0.24.0-beta.2, @polkadot/extension-base@workspace:packages/extension-base":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@polkadot/extension-base@workspace:packages/extension-base"
|
resolution: "@polkadot/extension-base@workspace:packages/extension-base"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2898,13 +2898,13 @@ __metadata:
|
|||||||
resolution: "@polkadot/extension-dapp@workspace:packages/extension-dapp"
|
resolution: "@polkadot/extension-dapp@workspace:packages/extension-dapp"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": ^7.9.2
|
"@babel/runtime": ^7.9.2
|
||||||
"@polkadot/extension-inject": 0.24.0-beta.1
|
"@polkadot/extension-inject": 0.24.0-beta.2
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@polkadot/api": "*"
|
"@polkadot/api": "*"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@polkadot/extension-inject@0.24.0-beta.1, @polkadot/extension-inject@workspace:packages/extension-inject":
|
"@polkadot/extension-inject@0.24.0-beta.2, @polkadot/extension-inject@workspace:packages/extension-inject":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@polkadot/extension-inject@workspace:packages/extension-inject"
|
resolution: "@polkadot/extension-inject@workspace:packages/extension-inject"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2914,7 +2914,7 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@polkadot/extension-ui@0.24.0-beta.1, @polkadot/extension-ui@workspace:packages/extension-ui":
|
"@polkadot/extension-ui@0.24.0-beta.2, @polkadot/extension-ui@workspace:packages/extension-ui":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@polkadot/extension-ui@workspace:packages/extension-ui"
|
resolution: "@polkadot/extension-ui@workspace:packages/extension-ui"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2950,8 +2950,8 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": ^7.9.2
|
"@babel/runtime": ^7.9.2
|
||||||
"@polkadot/dev": ^0.51.9
|
"@polkadot/dev": ^0.51.9
|
||||||
"@polkadot/extension-base": 0.24.0-beta.1
|
"@polkadot/extension-base": 0.24.0-beta.2
|
||||||
"@polkadot/extension-ui": 0.24.0-beta.1
|
"@polkadot/extension-ui": 0.24.0-beta.2
|
||||||
"@types/chrome": ^0.0.102
|
"@types/chrome": ^0.0.102
|
||||||
"@types/firefox-webext-browser": ^70.0.1
|
"@types/firefox-webext-browser": ^70.0.1
|
||||||
babel-loader: ^8.1.0
|
babel-loader: ^8.1.0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user