Correctly use chain type in account creation (#882)
* Correctly use chain type in account creation * CHANGELOG
This commit is contained in:
parent
822b2dc6cd
commit
cf503c6662
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,5 +1,21 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 0.41.1 Nov 8, 2021
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add search functionality (Thanks to https://github.com/Tbaut)
|
||||
|
||||
Changes:
|
||||
|
||||
- Detect Ascii bytes (& display) when signing
|
||||
- Correctly detect and create Ethereum-compatible chain accounts
|
||||
- Ensure site authorization toggle is saved
|
||||
- Optimize metadata conversion process
|
||||
- Bump `@polkadot/util` to 7.8.2
|
||||
- Bump `@polkadot/api` to 6.7.1
|
||||
|
||||
|
||||
## 0.40.4 Oct 25, 2021
|
||||
|
||||
**Important** Not published to the stores, aligns with latest released packages.
|
||||
|
||||
@ -5,6 +5,7 @@ import type { MetadataDef } from '@polkadot/extension-inject/types';
|
||||
import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@polkadot/keyring/types';
|
||||
import type { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types';
|
||||
import type { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { AccountJson, AllowedPath, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountBatchExport, RequestAccountChangePassword, RequestAccountCreateExternal, RequestAccountCreateHardware, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountForget, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestAuthorizeApprove, RequestAuthorizeReject, RequestBatchRestore, RequestDeriveCreate, RequestDeriveValidate, RequestJsonRestore, RequestMetadataApprove, RequestMetadataReject, RequestSeedCreate, RequestSeedValidate, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestTypes, ResponseAccountExport, ResponseAccountsExport, ResponseAuthorizeList, ResponseDeriveValidate, ResponseJsonGetAccountInfo, ResponseSeedCreate, ResponseSeedValidate, ResponseSigningIsLocked, ResponseType, SigningRequest } from '../types';
|
||||
|
||||
import { ALLOWED_PATH, PASSWORD_EXPIRY_MS } from '@polkadot/extension-base/defaults';
|
||||
@ -22,10 +23,17 @@ type CachedUnlocks = Record<string, number>;
|
||||
|
||||
const SEED_DEFAULT_LENGTH = 12;
|
||||
const SEED_LENGTHS = [12, 15, 18, 21, 24];
|
||||
const ETH_DERIVE_DEFAULT = "/m/44'/60'/0'/0/0";
|
||||
|
||||
// a global registry to use internally
|
||||
const registry = new TypeRegistry();
|
||||
|
||||
function getSuri (seed: string, type?: KeypairType): string {
|
||||
return type === 'ethereum'
|
||||
? `${seed}${ETH_DERIVE_DEFAULT}`
|
||||
: seed;
|
||||
}
|
||||
|
||||
function transformAccounts (accounts: SubjectInfo): AccountJson[] {
|
||||
return Object.values(accounts).map(({ json: { address, meta }, type }): AccountJson => ({
|
||||
address,
|
||||
@ -61,7 +69,7 @@ export default class Extension {
|
||||
}
|
||||
|
||||
private accountsCreateSuri ({ genesisHash, name, password, suri, type }: RequestAccountCreateSuri): boolean {
|
||||
keyring.addUri(suri, password, { genesisHash, name }, type);
|
||||
keyring.addUri(getSuri(suri, type), password, { genesisHash, name }, type);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -300,7 +308,7 @@ export default class Extension {
|
||||
const seed = mnemonicGenerate(length);
|
||||
|
||||
return {
|
||||
address: keyring.createFromUri(seed, {}, type).address,
|
||||
address: keyring.createFromUri(getSuri(seed, type), {}, type).address,
|
||||
seed
|
||||
};
|
||||
}
|
||||
@ -317,7 +325,7 @@ export default class Extension {
|
||||
}
|
||||
|
||||
return {
|
||||
address: keyring.createFromUri(suri, {}, type).address,
|
||||
address: keyring.createFromUri(getSuri(suri, type), {}, type).address,
|
||||
suri
|
||||
};
|
||||
}
|
||||
|
||||
@ -7,8 +7,9 @@ import styled from 'styled-components';
|
||||
import { ActionContext, Address, Dropdown, Loading } from '../../components';
|
||||
import AccountNamePasswordCreation from '../../components/AccountNamePasswordCreation';
|
||||
import useGenesisHashOptions from '../../hooks/useGenesisHashOptions';
|
||||
import useMetadata from '../../hooks/useMetadata';
|
||||
import useTranslation from '../../hooks/useTranslation';
|
||||
import { createAccountSuri, createSeed } from '../../messaging';
|
||||
import { createAccountSuri, createSeed, validateSeed } from '../../messaging';
|
||||
import { HeaderWithSteps } from '../../partials';
|
||||
import { DEFAULT_TYPE } from '../../util/defaultType';
|
||||
import Mnemonic from './Mnemonic';
|
||||
@ -22,25 +23,44 @@ function CreateAccount ({ className }: Props): React.ReactElement {
|
||||
const onAction = useContext(ActionContext);
|
||||
const [isBusy, setIsBusy] = useState(false);
|
||||
const [step, setStep] = useState(1);
|
||||
const [account, setAccount] = useState<null | { address: string; seed: string }>(null);
|
||||
const [address, setAddress] = useState<null | string>(null);
|
||||
const [seed, setSeed] = useState<null | string>(null);
|
||||
const [type, setType] = useState(DEFAULT_TYPE);
|
||||
const [name, setName] = useState('');
|
||||
const options = useGenesisHashOptions();
|
||||
const [genesisHash, setGenesis] = useState('');
|
||||
const chain = useMetadata(genesisHash, true);
|
||||
|
||||
useEffect((): void => {
|
||||
createSeed(undefined, type)
|
||||
.then(setAccount)
|
||||
.catch((error: Error) => console.error(error));
|
||||
}, [type]);
|
||||
createSeed(undefined)
|
||||
.then(({ address, seed }): void => {
|
||||
setAddress(address);
|
||||
setSeed(seed);
|
||||
})
|
||||
.catch(console.error);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect((): void => {
|
||||
if (seed) {
|
||||
const type = chain && chain.definition.chainType === 'ethereum'
|
||||
? 'ethereum'
|
||||
: DEFAULT_TYPE;
|
||||
|
||||
setType(type);
|
||||
validateSeed(seed, type)
|
||||
.then(({ address }) => setAddress(address))
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [seed, chain]);
|
||||
|
||||
const _onCreate = useCallback(
|
||||
(name: string, password: string): void => {
|
||||
// this should always be the case
|
||||
if (name && password && account) {
|
||||
if (name && password && seed) {
|
||||
setIsBusy(true);
|
||||
|
||||
createAccountSuri(name, password, account.seed, type, genesisHash)
|
||||
createAccountSuri(name, password, seed, type, genesisHash)
|
||||
.then(() => onAction('/'))
|
||||
.catch((error: Error): void => {
|
||||
setIsBusy(false);
|
||||
@ -48,23 +68,23 @@ function CreateAccount ({ className }: Props): React.ReactElement {
|
||||
});
|
||||
}
|
||||
},
|
||||
[account, genesisHash, onAction, type]
|
||||
[genesisHash, onAction, seed, type]
|
||||
);
|
||||
|
||||
const _onNextStep = useCallback(() => setStep((step) => step + 1), []);
|
||||
const _onPreviousStep = useCallback(() => setStep((step) => step - 1), []);
|
||||
const _onNextStep = useCallback(
|
||||
() => setStep((step) => step + 1),
|
||||
[]
|
||||
);
|
||||
|
||||
const _onChangeNetwork = useCallback((newGenesisHash: string) => {
|
||||
const chain = options.find(({ value }) => {
|
||||
return newGenesisHash === value;
|
||||
});
|
||||
const _onPreviousStep = useCallback(
|
||||
() => setStep((step) => step - 1),
|
||||
[]
|
||||
);
|
||||
|
||||
if (chain?.text === 'Moonbase Alpha') { // TODO: use list
|
||||
setType('ethereum');
|
||||
}
|
||||
|
||||
setGenesis(newGenesisHash);
|
||||
}, [options]);
|
||||
const _onChangeNetwork = useCallback(
|
||||
(newGenesisHash: string) => setGenesis(newGenesisHash),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -75,17 +95,17 @@ function CreateAccount ({ className }: Props): React.ReactElement {
|
||||
<Loading>
|
||||
<div>
|
||||
<Address
|
||||
address={account?.address}
|
||||
address={address}
|
||||
genesisHash={genesisHash}
|
||||
name={name}
|
||||
/>
|
||||
</div>
|
||||
{account && (
|
||||
{seed && (
|
||||
step === 1
|
||||
? (
|
||||
<Mnemonic
|
||||
onNextStep={_onNextStep}
|
||||
seed={account.seed}
|
||||
seed={seed}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// Copyright 2019-2021 @polkadot/extension-ui authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { ThemeProps } from '../../types';
|
||||
import type { AccountInfo } from '.';
|
||||
|
||||
@ -10,6 +11,7 @@ import React, { useCallback, useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { validateSeed } from '@polkadot/extension-ui/messaging';
|
||||
import { objectSpread } from '@polkadot/util';
|
||||
|
||||
import { ButtonArea, Dropdown, InputWithLabel, NextStepButton, TextAreaWithLabel, VerticalSpace, Warning } from '../../components';
|
||||
import useGenesisHashOptions from '../../hooks/useGenesisHashOptions';
|
||||
@ -19,9 +21,10 @@ interface Props {
|
||||
className?: string;
|
||||
onNextStep: () => void;
|
||||
onAccountChange: (account: AccountInfo | null) => void;
|
||||
type: KeypairType;
|
||||
}
|
||||
|
||||
function SeedAndPath ({ className, onAccountChange, onNextStep }: Props): React.ReactElement {
|
||||
function SeedAndPath ({ className, onAccountChange, onNextStep, type }: Props): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const genesisOptions = useGenesisHashOptions();
|
||||
const [address, setAddress] = useState('');
|
||||
@ -42,22 +45,23 @@ function SeedAndPath ({ className, onAccountChange, onNextStep }: Props): React.
|
||||
|
||||
const suri = `${seed || ''}${path || ''}`;
|
||||
|
||||
validateSeed(suri).then((validatedAccount) => {
|
||||
setError('');
|
||||
setAddress(validatedAccount.address);
|
||||
// a spread operator here breaks tests, using Object.assign as a workaround
|
||||
const newAccount: AccountInfo = Object.assign(validatedAccount, { genesis });
|
||||
|
||||
onAccountChange(newAccount);
|
||||
}).catch(() => {
|
||||
setAddress('');
|
||||
onAccountChange(null);
|
||||
setError(path
|
||||
? t<string>('Invalid mnemonic seed or derivation path')
|
||||
: t<string>('Invalid mnemonic seed')
|
||||
);
|
||||
});
|
||||
}, [t, seed, path, onAccountChange, genesis]);
|
||||
validateSeed(suri, type)
|
||||
.then((validatedAccount) => {
|
||||
setError('');
|
||||
setAddress(validatedAccount.address);
|
||||
onAccountChange(
|
||||
objectSpread<AccountInfo>({}, validatedAccount, { genesis, type })
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
setAddress('');
|
||||
onAccountChange(null);
|
||||
setError(path
|
||||
? t<string>('Invalid mnemonic seed or derivation path')
|
||||
: t<string>('Invalid mnemonic seed')
|
||||
);
|
||||
});
|
||||
}, [t, genesis, seed, path, onAccountChange, type]);
|
||||
|
||||
const _onToggleAdvanced = useCallback(() => {
|
||||
setAdvances(!advanced);
|
||||
|
||||
@ -5,9 +5,11 @@ import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { AccountContext, ActionContext, Address } from '../../components';
|
||||
import AccountNamePasswordCreation from '../../components/AccountNamePasswordCreation';
|
||||
import useMetadata from '../../hooks/useMetadata';
|
||||
import useTranslation from '../../hooks/useTranslation';
|
||||
import { createAccountSuri } from '../../messaging';
|
||||
import { HeaderWithSteps } from '../../partials';
|
||||
import { DEFAULT_TYPE } from '../../util/defaultType';
|
||||
import SeedAndPath from './SeedAndPath';
|
||||
|
||||
export interface AccountInfo {
|
||||
@ -24,27 +26,44 @@ function ImportSeed (): React.ReactElement {
|
||||
const [account, setAccount] = useState<AccountInfo | null>(null);
|
||||
const [name, setName] = useState<string | null>(null);
|
||||
const [step1, setStep1] = useState(true);
|
||||
const [type, setType] = useState(DEFAULT_TYPE);
|
||||
const chain = useMetadata(account && account.genesis, true);
|
||||
|
||||
useEffect((): void => {
|
||||
!accounts.length && onAction();
|
||||
}, [accounts, onAction]);
|
||||
|
||||
useEffect((): void => {
|
||||
setType(
|
||||
chain && chain.definition.chainType === 'ethereum'
|
||||
? 'ethereum'
|
||||
: DEFAULT_TYPE
|
||||
);
|
||||
}, [chain]);
|
||||
|
||||
const _onCreate = useCallback((name: string, password: string): void => {
|
||||
// this should always be the case
|
||||
if (name && password && account) {
|
||||
setIsBusy(true);
|
||||
|
||||
createAccountSuri(name, password, account.suri, undefined, account.genesis)
|
||||
createAccountSuri(name, password, account.suri, type, account.genesis)
|
||||
.then(() => onAction('/'))
|
||||
.catch((error): void => {
|
||||
setIsBusy(false);
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}, [account, onAction]);
|
||||
}, [account, onAction, type]);
|
||||
|
||||
const _onNextStep = useCallback(() => { setStep1(false); }, []);
|
||||
const _onBackClick = useCallback(() => { setStep1(true); }, []);
|
||||
const _onNextStep = useCallback(
|
||||
() => setStep1(false),
|
||||
[]
|
||||
);
|
||||
|
||||
const _onBackClick = useCallback(
|
||||
() => setStep1(true),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -64,6 +83,7 @@ function ImportSeed (): React.ReactElement {
|
||||
<SeedAndPath
|
||||
onAccountChange={setAccount}
|
||||
onNextStep={_onNextStep}
|
||||
type={type}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
|
||||
@ -83,7 +83,9 @@ function recodeAddress (address: string, accounts: AccountWithChildren[], chain:
|
||||
// always allow the actual settings to override the display
|
||||
return {
|
||||
account,
|
||||
formatted: encodeAddress(publicKey, prefix),
|
||||
formatted: account?.type === 'ethereum'
|
||||
? address
|
||||
: encodeAddress(publicKey, prefix),
|
||||
genesisHash: account?.genesisHash,
|
||||
prefix,
|
||||
type: account?.type || DEFAULT_TYPE
|
||||
@ -116,11 +118,25 @@ function Address ({ actions, address, children, className, genesisHash, isExtern
|
||||
|
||||
const accountByAddress = findAccountByAddress(accounts, address);
|
||||
|
||||
const recoded = (chain?.definition.chainType === 'ethereum' || accountByAddress?.type === 'ethereum' || (!accountByAddress && givenType === 'ethereum'))
|
||||
? { account: accountByAddress, formatted: address, type: 'ethereum' } as Recoded
|
||||
: recodeAddress(address, accounts, chain, settings);
|
||||
console.log(
|
||||
address,
|
||||
(
|
||||
chain?.definition.chainType === 'ethereum' ||
|
||||
accountByAddress?.type === 'ethereum' ||
|
||||
(!accountByAddress && givenType === 'ethereum')
|
||||
)
|
||||
? { account: accountByAddress, formatted: address, type: 'ethereum' }
|
||||
: recodeAddress(address, accounts, chain, settings)
|
||||
);
|
||||
|
||||
setRecoded(recoded || defaultRecoded);
|
||||
setRecoded(
|
||||
(
|
||||
chain?.definition.chainType === 'ethereum' ||
|
||||
accountByAddress?.type === 'ethereum' ||
|
||||
(!accountByAddress && givenType === 'ethereum')
|
||||
)
|
||||
? { account: accountByAddress, formatted: address, type: 'ethereum' }
|
||||
: recodeAddress(address, accounts, chain, settings));
|
||||
}, [accounts, address, chain, givenType, settings]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -140,20 +156,23 @@ function Address ({ actions, address, children, className, genesisHash, isExtern
|
||||
}, [toggleActions]);
|
||||
|
||||
const theme = (
|
||||
chain?.icon
|
||||
? chain.icon
|
||||
: type === 'ethereum'
|
||||
? 'ethereum'
|
||||
: 'polkadot'
|
||||
type === 'ethereum'
|
||||
? 'ethereum'
|
||||
: (chain?.icon || 'polkadot')
|
||||
) as IconTheme;
|
||||
|
||||
const _onClick = useCallback((): void => setShowActionsMenu(!showActionsMenu), [showActionsMenu]);
|
||||
const _onCopy = useCallback((): void => show(t('Copied')), [show, t]);
|
||||
const _onClick = useCallback(
|
||||
() => setShowActionsMenu(!showActionsMenu),
|
||||
[showActionsMenu]
|
||||
);
|
||||
|
||||
const _onCopy = useCallback(
|
||||
() => show(t('Copied')),
|
||||
[show, t]
|
||||
);
|
||||
|
||||
const _toggleVisibility = useCallback(
|
||||
(): void => {
|
||||
address && showAccount(address, isHidden || false)
|
||||
.catch(console.error);
|
||||
},
|
||||
() => address && showAccount(address, isHidden || false).catch(console.error),
|
||||
[address, isHidden]
|
||||
);
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Copyright 2019-2021 @polkadot/extension-ui authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
export const DEFAULT_TYPE = 'sr25519' as KeypairType;
|
||||
export const DEFAULT_TYPE: KeypairType = 'sr25519';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user