Add insert key functionality (#1683)
This commit is contained in:
parent
38faa6adb9
commit
7c1ff04500
138
packages/app-staking/src/Actions/Account/InjectKeys.tsx
Normal file
138
packages/app-staking/src/Actions/Account/InjectKeys.tsx
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright 2017-2019 @polkadot/app-staking 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 { I18nProps } from '@polkadot/react-components/types';
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Button, Dropdown, Icon, Input, Modal } from '@polkadot/react-components';
|
||||
import { QueueContext } from '@polkadot/react-components/Status';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import { assert, u8aToHex } from '@polkadot/util';
|
||||
import { keyExtractSuri, mnemonicValidate } from '@polkadot/util-crypto';
|
||||
|
||||
import translate from '../../translate';
|
||||
|
||||
interface Props extends I18nProps {
|
||||
isOpen?: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const CRYPTO_MAP: Record<string, KeypairType[]> = {
|
||||
aura: ['ed25519', 'sr25519'],
|
||||
babe: ['sr25519'],
|
||||
gran: ['ed25519'],
|
||||
imon: ['ed25519', 'sr25519'],
|
||||
para: ['sr25519']
|
||||
};
|
||||
|
||||
const EMPTY_KEY = '0x';
|
||||
|
||||
function InjectKeys ({ isOpen = true, onClose, t }: Props): React.ReactElement<Props> | null {
|
||||
const { queueRpc } = useContext(QueueContext);
|
||||
// this needs to align with what is set as the first value in `type`
|
||||
const [crypto, setCrypto] = useState<KeypairType>('sr25519');
|
||||
const [publicKey, setPublicKey] = useState(EMPTY_KEY);
|
||||
const [suri, setSuri] = useState('');
|
||||
const [type, setType] = useState('babe');
|
||||
|
||||
useEffect((): void => {
|
||||
setCrypto(CRYPTO_MAP[type][0]);
|
||||
}, [type]);
|
||||
|
||||
useEffect((): void => {
|
||||
try {
|
||||
const { phrase } = keyExtractSuri(suri);
|
||||
|
||||
assert(mnemonicValidate(phrase), 'Invalid mnemonic phrase');
|
||||
|
||||
setPublicKey(u8aToHex(keyring.createFromUri(suri, {}, crypto).publicKey));
|
||||
} catch (error) {
|
||||
setPublicKey(EMPTY_KEY);
|
||||
}
|
||||
}, [crypto, suri]);
|
||||
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const _onSubmit = (): void => {
|
||||
queueRpc({
|
||||
rpc: { section: 'author', method: 'insertKey' } as any,
|
||||
values: [type, suri, publicKey]
|
||||
});
|
||||
};
|
||||
const _cryptoOptions = CRYPTO_MAP[type].map((value): { text: string; value: KeypairType } => ({
|
||||
text: value === 'ed25519'
|
||||
? t('ed25519, Edwards')
|
||||
: t('sr15519, Schnorrkel'),
|
||||
value
|
||||
}));
|
||||
|
||||
return (
|
||||
<Modal
|
||||
dimmer='inverted'
|
||||
open
|
||||
size='small'
|
||||
>
|
||||
<Modal.Header>
|
||||
{t('Inject Keys')}
|
||||
</Modal.Header>
|
||||
<Modal.Content>
|
||||
<Input
|
||||
isError={publicKey.length !== 66}
|
||||
label={t('suri (seed & derivation)')}
|
||||
onChange={setSuri}
|
||||
value={suri}
|
||||
/>
|
||||
<Dropdown
|
||||
label={t('key type to set')}
|
||||
onChange={setType}
|
||||
options={[
|
||||
{ text: t('Aura'), value: 'aura' },
|
||||
{ text: t('Babe'), value: 'babe' },
|
||||
{ text: t('Grandpa'), value: 'gran' },
|
||||
{ text: t('I\'m Online'), value: 'imon' },
|
||||
{ text: t('Parachains'), value: 'para' }
|
||||
]}
|
||||
value={type}
|
||||
/>
|
||||
<Dropdown
|
||||
isDisabled={_cryptoOptions.length === 1}
|
||||
label={t('crypto type to use')}
|
||||
onChange={setCrypto}
|
||||
options={_cryptoOptions}
|
||||
value={crypto}
|
||||
/>
|
||||
<Input
|
||||
isDisabled
|
||||
label={t('generated public key')}
|
||||
value={publicKey}
|
||||
/>
|
||||
<article className='warning'>
|
||||
<div><Icon name='warning sign' />{t('This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.')}</div>
|
||||
</article>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
icon='cancel'
|
||||
isNegative
|
||||
label={t('Cancel')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
icon='sign-in'
|
||||
isPrimary
|
||||
label={t('Submit key')}
|
||||
onClick={_onSubmit}
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(InjectKeys);
|
||||
@ -16,6 +16,7 @@ import { AddressCard, AddressInfo, AddressMini, AddressRow, Button, Menu, Online
|
||||
import { withCalls, withMulti } from '@polkadot/react-api';
|
||||
|
||||
import BondExtra from './BondExtra';
|
||||
import InjectKeys from './InjectKeys';
|
||||
import Nominate from './Nominate';
|
||||
import SetControllerAccount from './SetControllerAccount';
|
||||
import SetRewardDestination from './SetRewardDestination';
|
||||
@ -42,6 +43,7 @@ interface State {
|
||||
destination: number;
|
||||
hexSessionId: string | null;
|
||||
isBondExtraOpen: boolean;
|
||||
isInjectOpen: boolean;
|
||||
isNominateOpen: boolean;
|
||||
isSetControllerAccountOpen: boolean;
|
||||
isSetRewardDestinationOpen: boolean;
|
||||
@ -72,6 +74,7 @@ class Account extends React.PureComponent<Props, State> {
|
||||
destination: 0,
|
||||
hexSessionId: null,
|
||||
isBondExtraOpen: false,
|
||||
isInjectOpen: false,
|
||||
isNominateOpen: false,
|
||||
isSetControllerAccountOpen: false,
|
||||
isSettingPopupOpen: false,
|
||||
@ -122,7 +125,7 @@ class Account extends React.PureComponent<Props, State> {
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, isSubstrateV2, t } = this.props;
|
||||
const { controllerId, hexSessionId, isBondExtraOpen, isStashValidating, isUnbondOpen, nominees, sessionIds, stashId } = this.state;
|
||||
const { controllerId, hexSessionId, isBondExtraOpen, isInjectOpen, isStashValidating, isUnbondOpen, nominees, sessionIds, stashId } = this.state;
|
||||
|
||||
if (!stashId) {
|
||||
return null;
|
||||
@ -160,6 +163,9 @@ class Account extends React.PureComponent<Props, State> {
|
||||
onClose={this.toggleUnbond}
|
||||
stashId={stashId}
|
||||
/>
|
||||
{isInjectOpen && (
|
||||
<InjectKeys onClose={this.toggleInject} />
|
||||
)}
|
||||
{this.renderSetValidatorPrefs()}
|
||||
{this.renderNominate()}
|
||||
{this.renderSetControllerAccount()}
|
||||
@ -418,9 +424,9 @@ class Account extends React.PureComponent<Props, State> {
|
||||
{t('Change validator preferences')}
|
||||
</Menu.Item>
|
||||
}
|
||||
{(!!sessionIds.length || (isSubstrateV2 && hexSessionId !== '0x')) &&
|
||||
{!isStashNominating && (!!sessionIds.length || (isSubstrateV2 && hexSessionId !== '0x')) &&
|
||||
<Menu.Item onClick={this.toggleSetSessionAccount}>
|
||||
{isSubstrateV2 ? t('Change session keys') : t('Change session account')}
|
||||
{isSubstrateV2 ? t('Rotate session keys') : t('Change session account')}
|
||||
</Menu.Item>
|
||||
}
|
||||
{isStashNominating &&
|
||||
@ -428,6 +434,11 @@ class Account extends React.PureComponent<Props, State> {
|
||||
{t('Change nominee(s)')}
|
||||
</Menu.Item>
|
||||
}
|
||||
{!isStashNominating && isSubstrateV2 &&
|
||||
<Menu.Item onClick={this.toggleInject}>
|
||||
{t('Inject session keys (advanced)')}
|
||||
</Menu.Item>
|
||||
}
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@ -507,6 +518,12 @@ class Account extends React.PureComponent<Props, State> {
|
||||
}));
|
||||
}
|
||||
|
||||
private toggleInject = (): void => {
|
||||
this.setState(({ isInjectOpen }): Pick<State, never> => ({
|
||||
isInjectOpen: !isInjectOpen
|
||||
}));
|
||||
}
|
||||
|
||||
private toggleNominate = (): void => {
|
||||
this.setState(({ isNominateOpen }): Pick<State, never> => ({
|
||||
isNominateOpen: !isNominateOpen
|
||||
|
||||
@ -14,8 +14,11 @@ import Button from '../Button';
|
||||
import Icon from '../Icon';
|
||||
import translate from '../translate';
|
||||
import { classes } from '../util';
|
||||
import QueueContext from './Context';
|
||||
import { STATUS_COMPLETE } from './constants';
|
||||
|
||||
export { QueueContext };
|
||||
|
||||
interface Props extends I18nProps {
|
||||
stqueue?: QueueStatus[];
|
||||
txqueue?: QueueTx[];
|
||||
@ -188,7 +191,7 @@ export default translate(
|
||||
top: 0.25rem;
|
||||
width: 20rem;
|
||||
z-index: 1001;
|
||||
|
||||
|
||||
.dismiss {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
@ -227,11 +230,11 @@ export default translate(
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.padded {
|
||||
padding: 0.25rem 0 0 0 !important;
|
||||
}
|
||||
|
||||
|
||||
i.close {
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
|
||||
@ -60,6 +60,11 @@ export default createGlobalStyle`
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.error,
|
||||
&.warning {
|
||||
margin-left: 2.25rem;
|
||||
}
|
||||
|
||||
&.error {
|
||||
background: #fff6f6;
|
||||
border-color: #e0b4b4;
|
||||
|
||||
@ -486,6 +486,8 @@ class Signer extends React.PureComponent<Props, State> {
|
||||
const { api } = this.props;
|
||||
|
||||
try {
|
||||
assert(isFunction((api.rpc as any)[section] && (api.rpc as any)[section][method]), `api.rpc.${section}.${method} does not exist`);
|
||||
|
||||
const result = await (api.rpc as any)[section][method](...values);
|
||||
|
||||
console.log('submitRpc: result ::', format(result));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user