diff --git a/jest.config.js b/jest.config.js index 3c770fe7a2..8b0fbabf67 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,11 +16,11 @@ const internalModules = findPackages() const defaultConfig = { moduleNameMapper: { ...internalModules, + '@polkadot/apps/(.*)$': '/packages/apps/src/$1', '\\.(css|less)$': 'empty/object', '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'empty/object', '\\.(md)$': '/__mocks__/fileMock.js' }, - testTimeout: 25000, transformIgnorePatterns: [ '/node_modules' ] @@ -38,5 +38,6 @@ module.exports = Object.assign({}, config, { ...defaultConfig, displayName: 'fast-tests' } - ] + ], + testTimeout: 25000 }); diff --git a/packages/apps/public/locales/ar/translation.json b/packages/apps/public/locales/ar/translation.json index 2df57206a7..339da7764e 100644 --- a/packages/apps/public/locales/ar/translation.json +++ b/packages/apps/public/locales/ar/translation.json @@ -36,7 +36,7 @@ "Add a tip to this extrinsic, paying the block author for greater priority": "أضف جائزة إلى هذا البرنامج الخارجي، لتدفع لكاتب الكتلة في مقابل أولوية أكبر", "Add account": "أضف حسابًا", "Add account via Qr": "QR أضف حسابًا عبر كود", - "Add an account via seed": "seed أضف حسابًا باستخدام الجملة البذرة", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "{{step}}/{{STEPS_COUNT}} seed أضف حسابًا باستخدام الجملة البذرة", "Add an address": "أضف عنوانا", "Add an existing code hash": "إضافة تجزئة رمز موجودة", "Add an existing contract": "إضافة عقد موجود", diff --git a/packages/apps/public/locales/en/app-accounts.json b/packages/apps/public/locales/en/app-accounts.json index 5096324efb..e3ff9acbc4 100644 --- a/packages/apps/public/locales/en/app-accounts.json +++ b/packages/apps/public/locales/en/app-accounts.json @@ -14,7 +14,7 @@ "Add account": "Add account", "Add account via Ledger": "Add account via Ledger", "Add account via Qr": "Add account via Qr", - "Add an account via seed": "Add an account via seed", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "Add an account via seed {{step}}/{{STEPS_COUNT}}", "Add identity judgment": "Add identity judgment", "Add multisig": "Add multisig", "Add proxied account": "Add proxied account", @@ -63,6 +63,7 @@ "Forget this account": "Forget this account", "Full Legal Name": "Full Legal Name", "Future versions of the web-only interface will drop support for non-external accounts, much like the IPFS version.": "Future versions of the web-only interface will drop support for non-external accounts, much like the IPFS version.", + "I have saved my mnemonic seed safely": "I have saved my mnemonic seed safely", "If the delegated account is currently voting in a referendum, the delegating vote and conviction will be added.": "If the delegated account is currently voting in a referendum, the delegating vote and conviction will be added.", "If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.": "If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.", "If you add several proxy accounts for the same proxy type (e.g 2 accounts set as proxy for Governance), then any of those 2 accounts will be able to perfom governance actions on behalf of the proxied account": "If you add several proxy accounts for the same proxy type (e.g 2 accounts set as proxy for Governance), then any of those 2 accounts will be able to perfom governance actions on behalf of the proxied account", diff --git a/packages/apps/public/locales/en/react-components.json b/packages/apps/public/locales/en/react-components.json index 057ed6920c..9f334e1bb6 100644 --- a/packages/apps/public/locales/en/react-components.json +++ b/packages/apps/public/locales/en/react-components.json @@ -41,6 +41,7 @@ "lifetime": "lifetime", "lock expired": "lock expired", "locked": "locked", + "mnemonic copied": "mnemonic copied", "no tags": "no tags", "ongoing referendum": "ongoing referendum", "only this network": "only this network", @@ -50,6 +51,7 @@ "reserved": "reserved", "session keys": "session keys", "session next": "session next", + "strong": "strong", "tip": "tip", "total": "total", "transactions": "transactions", @@ -68,6 +70,7 @@ "vote value": "vote value", "vote with account": "vote with account", "voting balance": "voting balance", + "weak": "weak", "{{blocks}} blocks": "{{blocks}} blocks", "{{eras}} eras remaining": "{{eras}} eras remaining", "{{name}} ({{size}} bytes)": "{{name}} ({{size}} bytes)", diff --git a/packages/apps/public/locales/es/translation.json b/packages/apps/public/locales/es/translation.json index e4e2d02005..ea82072582 100644 --- a/packages/apps/public/locales/es/translation.json +++ b/packages/apps/public/locales/es/translation.json @@ -27,7 +27,7 @@ "Add a tip to this extrinsic, paying the block author for greater priority": "Añade una propina a este extrinsic, pagando al autor del bloque por una mayor prioridad", "Add account": "Añadir cuenta", "Add account via Qr": "Añadir cuenta desde el Qr", - "Add an account via seed": "Añadir cuenta desde la semilla", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "Añadir cuenta desde la semilla {{step}}/{{STEPS_COUNT}}", "Add an address": "Añadir una dirección", "Add an existing code hash": "Añadir un hash de código existente", "Add an existing contract": "Añadir un contrato existente", diff --git a/packages/apps/public/locales/fr/translation.json b/packages/apps/public/locales/fr/translation.json index fb0a4b6717..35bd1b5991 100644 --- a/packages/apps/public/locales/fr/translation.json +++ b/packages/apps/public/locales/fr/translation.json @@ -43,7 +43,7 @@ "Add account": "Ajouter un compte", "Add account via Ledger": "Ajouter un compte via Ledger", "Add account via Qr": "Ajouter via Code QR", - "Add an account via seed": "Ajouter un compte via phrase secrète", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "Ajouter un compte via phrase secrète {{step}}/{{STEPS_COUNT}}", "Add an address": "Ajouter une adresse", "Add an existing code hash": "Ajouter un code de hachage existant", "Add an existing contract": "Ajouter un contrat existant", diff --git a/packages/apps/public/locales/id/translation.json b/packages/apps/public/locales/id/translation.json index 7f5e1dcdc0..ed3524457f 100644 --- a/packages/apps/public/locales/id/translation.json +++ b/packages/apps/public/locales/id/translation.json @@ -42,7 +42,7 @@ "Add account": "Tambahkan akun", "Add account via Ledger": "Tambah akun melalui Ledger", "Add account via Qr": "Tambahkan akun melalui Qr", - "Add an account via seed": "Tambahkan akun via seed", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "Tambahkan akun via seed {{step}}/{{STEPS_COUNT}}", "Add an address": "Tambah sebuah alamat", "Add an existing code hash": "Tambahkan kode hash yang ada", "Add an existing contract": "Tambahkan kontrak yang ada", diff --git a/packages/apps/public/locales/it/translation.json b/packages/apps/public/locales/it/translation.json index 1ed0a48975..5854363d98 100644 --- a/packages/apps/public/locales/it/translation.json +++ b/packages/apps/public/locales/it/translation.json @@ -42,7 +42,7 @@ "Add account": "Aggiungi account", "Add account via Ledger": "Aggiungi account via Ledger", "Add account via Qr": "Aggiungi account via QR Code", - "Add an account via seed": "Aggiungi account via seed", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "Aggiungi account via seed {{step}}/{{STEPS_COUNT}}", "Add an address": "Aggiungi un indirizzo", "Add an existing code hash": "Aggiungi un codice hash", "Add an existing contract": "Aggiungi un contratto", diff --git a/packages/apps/public/locales/ja/translation.json b/packages/apps/public/locales/ja/translation.json index d3185b272d..54d1ceef3f 100644 --- a/packages/apps/public/locales/ja/translation.json +++ b/packages/apps/public/locales/ja/translation.json @@ -18,7 +18,7 @@ "Add Ledger": "クエリーレッジャー", "Add account": "アカウントを追加する", "Add account via Qr": "QRを追加する", - "Add an account via seed": "シード経由でアカウントを追加する", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "シード経由でアカウントを追加する{{step}}/{{STEPS_COUNT}}", "Add an address": "アドレスを追加する", "Add an existing code hash": "既存のコードハッシュを追加する", "Add an existing contract": "既存のコントラクトを追加する", diff --git a/packages/apps/public/locales/ko/translation.json b/packages/apps/public/locales/ko/translation.json index 8958f0942c..635b60490c 100644 --- a/packages/apps/public/locales/ko/translation.json +++ b/packages/apps/public/locales/ko/translation.json @@ -41,7 +41,7 @@ "Add account": "계정 추가하기", "Add account via Ledger": "원장 통해서 계정을 추가하다", "Add account via Qr": "QR코드로 계정 추가하기", - "Add an account via seed": "난수 값을 이용해서 계정 생성하기", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "난수 값을 이용해서 계정 생성하기{{step}}/{{STEPS_COUNT}}", "Add an address": "주소 추가하기", "Add an existing code hash": "현재 존재하는 코드 해시를 추가하다", "Add an existing contract": "현재 존재하는 계약서를 추가하다", diff --git a/packages/apps/public/locales/pt/translation.json b/packages/apps/public/locales/pt/translation.json index feae8eb0a5..037866355b 100644 --- a/packages/apps/public/locales/pt/translation.json +++ b/packages/apps/public/locales/pt/translation.json @@ -27,7 +27,7 @@ "Add a tip to this extrinsic, paying the block author for greater priority": "Adicione uma gorjeta a esse extrínseco, pagando ao autor do bloco por maior prioridade", "Add account": "Adicionar Conta", "Add account via Qr": "Adicionar conta via código QR", - "Add an account via seed": "Adicionar uma conta via semente", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "Adicionar uma conta via semente {{step}}/{{STEPS_COUNT}}", "Add an address": "Adicionar um endereço", "Add an existing code hash": "Adicione um hash de código existente", "Add an existing contract": "Adicionar um contrato existente", diff --git a/packages/apps/public/locales/ru/translation.json b/packages/apps/public/locales/ru/translation.json index 6434a72788..c6bc9c42df 100644 --- a/packages/apps/public/locales/ru/translation.json +++ b/packages/apps/public/locales/ru/translation.json @@ -27,7 +27,7 @@ "Add a tip to this extrinsic, paying the block author for greater priority": "Добавыть чаевые для автора блока для большей приоритезации", "Add account": "Добавить аккаунт", "Add account via Qr": "Добавить аккаунт с помощью QR-кода", - "Add an account via seed": "Добавить аккаунт с помощью секретной фразы", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "Добавить аккаунт с помощью секретной фразы {{step}}/{{STEPS_COUNT}}", "Add an address": "Добавить адрес", "Add an existing code hash": "Добавить существующий хэш кода", "Add an existing contract": "Добавить существующий контракт", diff --git a/packages/apps/public/locales/zh/translation.json b/packages/apps/public/locales/zh/translation.json index 7d910878d6..3eb62b1e9a 100644 --- a/packages/apps/public/locales/zh/translation.json +++ b/packages/apps/public/locales/zh/translation.json @@ -39,7 +39,7 @@ "Add a tip to this extrinsic, paying the block author for greater priority": "添加小费到这个外部,为获得更高的优先级去支付区块生产者", "Add account": "添加账户", "Add account via Qr": "通过二维码添加账户", - "Add an account via seed": "通过种子添加账户", + "Add an account via seed {{step}}/{{STEPS_COUNT}}": "通过种子添加账户 {{step}}/{{STEPS_COUNT}}", "Add an address": "添加一个地址", "Add an existing code hash": "添加一个已经存在的代码哈希", "Add an existing contract": "添加一个已存在的合约", @@ -1380,4 +1380,4 @@ "{{threshold}}, not passing": "{{threshold}},没有通过", "{{threshold}}, passing": "{{threshold}}, 通过", "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "{{value}}x投票余额,锁定 {{lock}}x 执行 ({{period}} days)" -} \ No newline at end of file +} diff --git a/packages/apps/src/themes.ts b/packages/apps/src/themes.ts index 5d1b9fcd7e..4629902c30 100644 --- a/packages/apps/src/themes.ts +++ b/packages/apps/src/themes.ts @@ -16,6 +16,7 @@ export const darkTheme: ThemeDef = { borderTable: 'transparent', borderTabs: 'transparent', color: 'rgba(244, 242, 240, 0.9)', + colorCheckbox: 'rgba(78, 78, 78, 0.66)', colorError: 'rgba(255, 0, 0, 0.85)', colorLabel: 'rgba(244, 242, 240, 0.45)', colorSummary: 'rgba(244, 242, 240, 0.75)', @@ -35,6 +36,7 @@ export const lightTheme: ThemeDef = { borderTable: '#eeecea', borderTabs: '#eeecea', color: '#4e4e4e', + colorCheckbox: 'rgba(34, 36, 38, 0.15)', colorError: 'rgba(139, 0, 0)', colorLabel: 'rgba(78, 78, 78, 0.66)', colorSummary: 'rgba(0, 0, 0, 0.6)', diff --git a/packages/page-accounts/src/CreateAccount.spec.tsx b/packages/page-accounts/src/CreateAccount.spec.tsx index 3d3b141bca..9bc311583c 100644 --- a/packages/page-accounts/src/CreateAccount.spec.tsx +++ b/packages/page-accounts/src/CreateAccount.spec.tsx @@ -3,12 +3,14 @@ import AccountsApp from '@polkadot/app-accounts'; import { MemoryStore } from '@polkadot/app-accounts/test-support/MemoryStore'; +import { lightTheme } from '@polkadot/apps/themes'; import { Api } from '@polkadot/react-api'; import '@polkadot/react-components/i18n'; import { useApi } from '@polkadot/react-hooks'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, waitForElementToBeRemoved } from '@testing-library/react'; import React, { PropsWithChildren } from 'react'; import { MemoryRouter } from 'react-router-dom'; +import { ThemeProvider } from 'styled-components'; const SUBSTRATE_PORT = Number.parseInt(process.env.TEST_SUBSTRATE_PORT || '30333'); @@ -18,12 +20,12 @@ const WaitForApi = ({ children }: { children: React.ReactNode }): PropsWithChild return api.isApiReady ? (children) : null; }; -describe('--SLOW--: Account Create', () => { - it('asks for confirmation after saving new account', async () => { - const memoryStore = new MemoryStore(); +const renderAccounts = () => { + const memoryStore = new MemoryStore(); - const { findByPlaceholderText, findByTestId, findByText } = render( - + return render( + + @@ -34,29 +36,70 @@ describe('--SLOW--: Account Create', () => { - - ); + + + ); +}; - const addAccountButton = await findByText('Add account', {}, { timeout: 4000 }); +describe.only('--SLOW--: Account Create', () => { + it('new create modal', async () => { + const { findByTestId, findByText, queryByText } = renderAccounts(); + + const addAccountButton = await findByText('Add account', {}, { timeout: 5000 }); fireEvent.click(addAccountButton); - const nameInput = await findByPlaceholderText('new account'); + const isSeedSavedCheckbox = await findByText('I have saved my mnemonic seed safely'); + const hiddenCheckbox = isSeedSavedCheckbox as HTMLInputElement; - fireEvent.change(nameInput, { target: { value: 'my super account' } }); + fireEvent.click(hiddenCheckbox); + + const nextStepButton = await findByText('Next', {}, { timeout: 4000 }); + + fireEvent.click(nextStepButton); + + const accountNameInput = await findByTestId('name'); + + fireEvent.change(accountNameInput, { target: { value: 'my new account' } }); const passwordInput = await findByTestId('password'); - fireEvent.change(passwordInput, { target: { value: 'a' } }); + fireEvent.change(passwordInput, { target: { value: 'password' } }); - const passwordRepeatInput = await findByTestId('password (repeat)'); + const passwordInput2 = await findByTestId('password (repeat)'); - fireEvent.change(passwordRepeatInput, { target: { value: 'a' } }); + fireEvent.change(passwordInput2, { target: { value: 'password' } }); - const saveButton = await findByText('Save'); + const toStep3Button = await findByText('Next', {}, { timeout: 4000 }); - fireEvent.click(saveButton); + fireEvent.click(toStep3Button); - expect(await findByText('Create and backup account')).toBeTruthy(); + const createAnAccountButton = await findByText('Save', {}, { timeout: 4000 }); + + fireEvent.click(createAnAccountButton); + + await waitForElementToBeRemoved(() => queryByText('Add an account via seed')); + + expect(await findByText('MY NEW ACCOUNT')).toBeTruthy(); + }); + + it('error message for derivation path', async () => { + const { findByTestId, findByText } = renderAccounts(); + + const addAccountButton = await findByText('Add account', {}, { timeout: 5000 }); + + fireEvent.click(addAccountButton); + + const showAdvancedOptionsButton = await findByText('Advanced creation options', {}, { timeout: 5000 }); + + fireEvent.click(showAdvancedOptionsButton); + + const derivationPathInput = await findByTestId('secret derivation path', {}, { timeout: 5000 }); + + fireEvent.change(derivationPathInput, { target: { value: '//abc//' } }); + + const errorMsg = await findByText('Unable to match provided value to a secret URI', {}, { timeout: 5000 }); + + expect(errorMsg).toBeTruthy(); }); }); diff --git a/packages/page-accounts/src/modals/Create.tsx b/packages/page-accounts/src/modals/Create.tsx index 2e8d6f9ba0..06e2ad7a72 100644 --- a/packages/page-accounts/src/modals/Create.tsx +++ b/packages/page-accounts/src/modals/Create.tsx @@ -10,8 +10,8 @@ import FileSaver from 'file-saver'; import React, { useCallback, useMemo, useState } from 'react'; import styled from 'styled-components'; import { DEV_PHRASE } from '@polkadot/keyring/defaults'; -import { AddressRow, Button, CopyButton, Dropdown, Expander, Input, InputAddress, Modal } from '@polkadot/react-components'; -import { useApi, useToggle } from '@polkadot/react-hooks'; +import { AddressRow, Button, Checkbox, CopyButton, Dropdown, Expander, Input, InputAddress, Modal, TextArea } from '@polkadot/react-components'; +import { useApi } from '@polkadot/react-hooks'; import keyring from '@polkadot/ui-keyring'; import uiSettings from '@polkadot/ui-settings'; import { isHex, u8aToHex } from '@polkadot/util'; @@ -55,6 +55,7 @@ interface DeriveValidationOutput { } const DEFAULT_PAIR_TYPE = 'sr25519'; +const STEPS_COUNT = 3; function deriveValidate (seed: string, seedType: SeedType, derivePath: string, pairType: KeypairType): DeriveValidationOutput { try { @@ -183,11 +184,14 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ const { t } = useTranslation(); const { api, isDevelopment } = useApi(); const [{ address, derivePath, deriveValidation, isSeedValid, pairType, seed, seedType }, setAddress] = useState(generateSeed(propsSeed, '', propsSeed ? 'raw' : 'bip', propsType)); - const [isConfirmationOpen, toggleConfirmation] = useToggle(); + const [isMnemonicSaved, setIsMnemonicSaved] = useState(false); + const [step, setStep] = useState(1); const [isBusy, setIsBusy] = useState(false); const [{ isNameValid, name }, setName] = useState({ isNameValid: false, name: '' }); const [{ isPasswordValid, password }, setPassword] = useState({ isPasswordValid: false, password: '' }); - const isValid = !!address && !deriveValidation?.error && isNameValid && isPasswordValid && isSeedValid; + const isFirstStepValid = !!address && isMnemonicSaved && !deriveValidation?.error && isSeedValid; + const isSecondStepValid = isNameValid && isPasswordValid; + const isValid = isFirstStepValid && isSecondStepValid; const errorIndex: Record = useMemo(() => ({ PASSWORD_IGNORED: t('Password are ignored for hex seed'), SOFT_NOT_ALLOWED: t('Soft derivation paths are not allowed on ed25519'), @@ -237,6 +241,19 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ [] ); + const _toggleMnemonicSaved = () => { + setIsMnemonicSaved(!isMnemonicSaved); + }; + + const _nextStep = useCallback( + () => setStep((step) => step + 1), + [] + ); + + const _previousStep = useCallback( + () => setStep((step) => step - 1), + [] + ); const _onCommit = useCallback( (): void => { if (!isValid) { @@ -259,150 +276,181 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ return ( ('Add an account via seed')} + header={t('Add an account via seed {{step}}/{{STEPS_COUNT}}', { replace: { STEPS_COUNT, step } })} size='large' > - {address && isConfirmationOpen - ? ( - - ) - : ( - - - - - - - - - ('Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g "name_you_want - stash".')} - isError={!isNameValid} - label={t('name')} - onChange={_onChangeName} - onEnter={_onCommit} - placeholder={t('new account')} - value={name} - /> - - -

{t('The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.')}

-
-
- - - ('The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the "--key" parameter of your node.')} - isAction - isError={!isSeedValid} - isReadOnly={seedType === 'dev'} - label={ - seedType === 'bip' - ? t('mnemonic seed') - : seedType === 'dev' - ? t('development seed') - : t('seed (hex or string)') - } - onChange={_onChangeSeed} - onEnter={_onCommit} - value={seed} - > - - - - - -

{t('The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.')}

-
-
- + + + - - ('Advanced creation options')} - > - - - ('Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use "ed25519".')} - label={t('keypair crypto type')} - onChange={_onChangePairType} - options={uiSettings.availableCryptos} - tabIndex={-1} - /> - - -

{t('If you are moving accounts between applications, ensure that you use the correct type.')}

-
-
- - - ('You can set a custom derivation path for this account using the following syntax "///". The "/" and "//" may be repeated and mixed`. An optional "///" can be used with a mnemonic seed, and may only be specified once.')} - isError={!!deriveValidation?.error} - label={t('secret derivation path')} - onChange={_onChangeDerive} - onEnter={_onCommit} - placeholder={ - seedType === 'raw' - ? pairType === 'sr25519' - ? t('//hard/soft') - : t('//hard') - : pairType === 'sr25519' - ? t('//hard/soft///password') - : t('//hard///password') - } - tabIndex={-1} - value={derivePath} - /> - {deriveValidation?.error && ( -
{errorIndex[deriveValidation.error] || deriveValidation.error}
- )} - {deriveValidation?.warning && ( -
{errorIndex[deriveValidation.warning]}
- )} -
- -

{t('The derivation path allows you to create different accounts from the same base mnemonic.')}

-
-
-
-
- ) - } + + + {step === 1 && <> + + + + + +

{t('The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.')}

+
+
+ ('Advanced creation options')} + > + + + ('Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use "ed25519".')} + label={t('keypair crypto type')} + onChange={_onChangePairType} + options={uiSettings.availableCryptos} + tabIndex={-1} + /> + + +

{t('If you are moving accounts between applications, ensure that you use the correct type.')}

+
+
+ + + ('You can set a custom derivation path for this account using the following syntax "///". The "/" and "//" may be repeated and mixed`. An optional "///" can be used with a mnemonic seed, and may only be specified once.')} + isError={!!deriveValidation?.error} + label={t('secret derivation path')} + onChange={_onChangeDerive} + onEnter={_onCommit} + placeholder={ + seedType === 'raw' + ? pairType === 'sr25519' + ? t('//hard/soft') + : t('//hard') + : pairType === 'sr25519' + ? t('//hard/soft///password') + : t('//hard///password') + } + tabIndex={-1} + value={derivePath} + /> + {deriveValidation?.error && ( +
{errorIndex[deriveValidation.error] || deriveValidation.error}
+ )} + {deriveValidation?.warning && ( +
{errorIndex[deriveValidation.warning]}
+ )} +
+ +

{t('The derivation path allows you to create different accounts from the same base mnemonic.')}

+
+
+
+ + +   + + {t('I have saved my mnemonic seed safely')}} + onChange={_toggleMnemonicSaved} + value={isMnemonicSaved} + /> + + + } + {step === 2 && <> + + + ('Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g "name_you_want - stash".')} + isError={!isNameValid} + label={t('name')} + onChange={_onChangeName} + onEnter={_onCommit} + placeholder={t('new account')} + value={name} + /> + + +

{t('The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.')}

+
+
+ + + } + {step === 3 && address && + } + - {isConfirmationOpen - ? ( + {step === 1 && +