diff --git a/packages/extension-base/src/background/handlers/Extension.ts b/packages/extension-base/src/background/handlers/Extension.ts index e8f1de09..4aee06f7 100644 --- a/packages/extension-base/src/background/handlers/Extension.ts +++ b/packages/extension-base/src/background/handlers/Extension.ts @@ -26,9 +26,10 @@ const SEED_LENGTHS = [12, 15, 18, 21, 24]; const registry = new TypeRegistry(); function transformAccounts (accounts: SubjectInfo): AccountJson[] { - return Object.values(accounts).map(({ json: { address, meta } }): AccountJson => ({ + return Object.values(accounts).map(({ json: { address, meta }, type }): AccountJson => ({ address, - ...meta + ...meta, + type })); } @@ -256,12 +257,13 @@ export default class Extension { private jsonGetAccountInfo (json: KeyringPair$Json): ResponseJsonGetAccountInfo { try { - const pair = keyring.createFromJson(json); + const { address, meta: { genesisHash, name }, type } = keyring.createFromJson(json); return { - address: pair.address, - genesisHash: pair.meta.genesisHash, - name: pair.meta.name + address, + genesisHash, + name, + type } as ResponseJsonGetAccountInfo; } catch (e) { console.error(e); diff --git a/packages/extension-base/src/background/types.ts b/packages/extension-base/src/background/types.ts index de12c50d..cdbe4888 100644 --- a/packages/extension-base/src/background/types.ts +++ b/packages/extension-base/src/background/types.ts @@ -36,6 +36,7 @@ export interface AccountJson extends KeyringPair$Meta { name?: string; parentAddress?: string; suri?: string; + type?: KeypairType; whenCreated?: number; } @@ -367,6 +368,7 @@ export interface ResponseJsonGetAccountInfo { address: string; name: string; genesisHash: string; + type: KeypairType; } export interface ResponseAuthorizeList { diff --git a/packages/extension-ui/src/Popup/Accounts/Account.tsx b/packages/extension-ui/src/Popup/Accounts/Account.tsx index cb29476f..30a8d798 100644 --- a/packages/extension-ui/src/Popup/Accounts/Account.tsx +++ b/packages/extension-ui/src/Popup/Accounts/Account.tsx @@ -23,7 +23,7 @@ interface EditState { toggleActions: number; } -function Account ({ address, className, genesisHash, isExternal, isHidden, parentName, suri }: Props): React.ReactElement { +function Account ({ address, className, genesisHash, isExternal, isHidden, parentName, suri, type }: Props): React.ReactElement { const { t } = useTranslation(); const onAction = useContext(ActionContext); const [{ isEditing, toggleActions }, setEditing] = useState({ isEditing: false, toggleActions: 0 }); @@ -62,7 +62,7 @@ function Account ({ address, className, genesisHash, isExternal, isHidden, paren > {t('Rename')} - {!isExternal && ( + {!isExternal && type !== 'ethereum' && ( - ), [_onChangeGenesis, _toggleEdit, address, genesisHash, isExternal, t]); + ), [_onChangeGenesis, _toggleEdit, address, genesisHash, isExternal, t, type]); return (
diff --git a/packages/extension-ui/src/Popup/CreateAccount/index.tsx b/packages/extension-ui/src/Popup/CreateAccount/index.tsx index 13dae479..8a6c8b86 100644 --- a/packages/extension-ui/src/Popup/CreateAccount/index.tsx +++ b/packages/extension-ui/src/Popup/CreateAccount/index.tsx @@ -7,6 +7,7 @@ import { ActionContext, Address, Loading } from '../../components'; import useTranslation from '../../hooks/useTranslation'; import { createAccountSuri, createSeed } from '../../messaging'; import { HeaderWithSteps } from '../../partials'; +import { DEFAULT_TYPE } from '../../util/defaultType'; import AccountName from './AccountName'; import Mnemonic from './Mnemonic'; @@ -16,13 +17,14 @@ export default function CreateAccount (): React.ReactElement { const [isBusy, setIsBusy] = useState(false); const [step, setStep] = useState(1); const [account, setAccount] = useState(null); + const type = DEFAULT_TYPE; const [name, setName] = useState(''); useEffect((): void => { - createSeed() + createSeed(undefined, type) .then(setAccount) .catch((error: Error) => console.error(error)); - }, []); + }, [type]); // FIXME Duplicated between here and Import.tsx const _onCreate = useCallback( diff --git a/packages/extension-ui/src/Popup/RestoreJson.tsx b/packages/extension-ui/src/Popup/RestoreJson.tsx index 9449ab41..ca86e2f7 100644 --- a/packages/extension-ui/src/Popup/RestoreJson.tsx +++ b/packages/extension-ui/src/Popup/RestoreJson.tsx @@ -1,18 +1,19 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { ResponseJsonGetAccountInfo } from '@polkadot/extension-base/background/types'; import type { KeyringPair$Json } from '@polkadot/keyring/types'; import React, { useCallback, useContext, useEffect, useState } from 'react'; import styled from 'styled-components'; -import { ResponseJsonGetAccountInfo } from '@polkadot/extension-base/background/types'; import { u8aToString } from '@polkadot/util'; import { AccountContext, ActionContext, Address, Button, InputFileWithLabel, InputWithLabel, Warning } from '../components'; import useTranslation from '../hooks/useTranslation'; import { jsonGetAccountInfo, jsonRestore } from '../messaging'; import { Header } from '../partials'; +import { DEFAULT_TYPE } from '../util/defaultType'; const acceptedFormats = ['application/json', 'text/plain'].join(', '); @@ -25,7 +26,7 @@ function Upload ({ className } : Props): React.ReactElement { const { accounts } = useContext(AccountContext); const onAction = useContext(ActionContext); const [isBusy, setIsBusy] = useState(false); - const [{ address, genesisHash, name }, setAccountInfo] = useState({ address: '', genesisHash: '', name: '' }); + const [{ address, genesisHash, name, type }, setAccountInfo] = useState({ address: '', genesisHash: '', name: '', type: DEFAULT_TYPE }); const [password, setPassword] = useState(''); const [isFileError, setFileError] = useState(false); const [isPasswordError, setIsPasswordError] = useState(false); @@ -97,6 +98,7 @@ function Upload ({ className } : Props): React.ReactElement { address={address} genesisHash={genesisHash} name={name} + type={type} />
@@ -57,13 +62,20 @@ function findAccount (accounts: AccountJson[], publicKey: Uint8Array): AccountJs ) || null; } +// find an account in our list +function findAccountByAddress (accounts: AccountJson[], _address: string): AccountJson | null { + return accounts.find(({ address }): boolean => + address === _address + ) || null; +} + // recodes an supplied address using the prefix/genesisHash, include the actual saved account & chain function recodeAddress (address: string, accounts: AccountWithChildren[], chain: Chain | null, settings: SettingsStruct): Recoded { // decode and create a shortcut for the encoded address const publicKey = decodeAddress(address); // find our account using the actual publicKey, and then find the associated chain - const account = findAccount(accounts, publicKey); + const account = findSubstrateAccount(accounts, publicKey); const prefix = chain ? chain.ss58Format : (settings.prefix === -1 ? 42 : settings.prefix); // always allow the actual settings to override the display @@ -71,19 +83,20 @@ function recodeAddress (address: string, accounts: AccountWithChildren[], chain: account, formatted: encodeAddress(publicKey, prefix), genesisHash: account?.genesisHash, - prefix + prefix, + type: account?.type || DEFAULT_TYPE }; } const ACCOUNTS_SCREEN_HEIGHT = 550; +const defaultRecoded = { account: null, formatted: null, prefix: 42, type: DEFAULT_TYPE }; -function Address ({ actions, address, children, className, genesisHash, isExternal, isHidden, name, parentName, suri, toggleActions }: Props): React.ReactElement { +function Address ({ actions, address, children, className, genesisHash, isExternal, isHidden, name, parentName, suri, toggleActions, type: givenType }: Props): React.ReactElement { const { t } = useTranslation(); const { accounts } = useContext(AccountContext); const settings = useContext(SettingsContext); - const [{ account, formatted, genesisHash: recodedGenesis, prefix }, setRecoded] = useState({ account: null, formatted: null, prefix: 42 }); + const [{ account, formatted, genesisHash: recodedGenesis, prefix, type }, setRecoded] = useState(defaultRecoded); const chain = useMetadata(genesisHash || recodedGenesis, true); - const [showActionsMenu, setShowActionsMenu] = useState(false); const [moveMenuUp, setIsMovedMenu] = useState(false); const actionsRef = useRef(null); @@ -92,10 +105,17 @@ function Address ({ actions, address, children, className, genesisHash, isExtern useOutsideClick(actionsRef, () => (showActionsMenu && setShowActionsMenu(!showActionsMenu))); useEffect((): void => { - address && setRecoded( - recodeAddress(address, accounts, chain, settings) - ); - }, [accounts, address, chain, settings]); + if (!address) { + return; + } + + const accountByAddress = findAccountByAddress(accounts, address); + const recoded = (accountByAddress?.type === 'ethereum' || (!accountByAddress && givenType === 'ethereum')) + ? { account: accountByAddress, formatted: address, type: 'ethereum' } as Recoded + : recodeAddress(address, accounts, chain, settings); + + setRecoded(recoded || defaultRecoded); + }, [accounts, address, chain, givenType, settings]); useEffect(() => { if (!showActionsMenu) { @@ -113,7 +133,14 @@ function Address ({ actions, address, children, className, genesisHash, isExtern setShowActionsMenu(false); }, [toggleActions]); - const theme = ((chain && chain.icon) || 'polkadot') as 'polkadot'; + const theme = ( + chain?.icon + ? chain.icon + : type === 'ethereum' + ? 'ethereum' + : 'polkadot' + ) as IconTheme; + const _onClick = useCallback((): void => setShowActionsMenu(!showActionsMenu), [showActionsMenu]); const _onCopy = useCallback((): void => show(t('Copied')), [show, t]); const _toggleVisibility = useCallback( @@ -202,7 +229,7 @@ function Address ({ actions, address, children, className, genesisHash, isExtern className='fullAddress' data-field='address' > - {formatted || t('')} + {formatted || address || t('')} ` border-radius: 4px; } + img { + max-width: 50px; + max-height: 50px; + border-radius: 50%; + } + .name { font-size: 16px; line-height: 22px; diff --git a/packages/extension-ui/src/components/Identicon.tsx b/packages/extension-ui/src/components/Identicon.tsx index f4810f4a..510e7e3d 100644 --- a/packages/extension-ui/src/components/Identicon.tsx +++ b/packages/extension-ui/src/components/Identicon.tsx @@ -1,6 +1,7 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { IconTheme } from '@polkadot/react-identicon/types'; import type { ThemeProps } from '../types'; import React from 'react'; @@ -10,7 +11,7 @@ import Icon from '@polkadot/react-identicon'; interface Props { className?: string; - iconTheme?: 'beachball' | 'empty' | 'jdenticon' | 'polkadot' | 'substrate'; + iconTheme?: IconTheme; isExternal?: boolean | null; onCopy?: () => void; prefix?: number; diff --git a/packages/extension-ui/src/util/defaultType.ts b/packages/extension-ui/src/util/defaultType.ts new file mode 100644 index 00000000..9256da88 --- /dev/null +++ b/packages/extension-ui/src/util/defaultType.ts @@ -0,0 +1,6 @@ +// Copyright 2019-2020 @polkadot/extension-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { KeypairType } from '@polkadot/util-crypto/types'; + +export const DEFAULT_TYPE = 'sr25519' as KeypairType;