diff --git a/index.template.html b/index.template.html index 78e784569..172506e8c 100644 --- a/index.template.html +++ b/index.template.html @@ -779,7 +779,7 @@

diff --git a/locale/de/translation.js b/locale/de/translation.js index 877be43c0..45bc1674a 100644 --- a/locale/de/translation.js +++ b/locale/de/translation.js @@ -66,9 +66,16 @@ export const de_translation = { 'Jeder mit einer Kopie der Phrase hat Zugriff auf deine Brieftasche.', //Anyone with a copy of it can access all of your funds. doNotShare: 'Teile Sie unter keinen Umständen mit dritten.', //Do NOT share it with anybody. digitalStoreNotAdvised: 'Es wird empfohlen diese nicht digital zu sichern', //It is NOT advised to store this digitally. - optionalPassphrase: 'Optionale Pass Phrase', //Optional Passphrase + optionalPassphrase: 'Optionale Pass Phrase (BIP39)', //Optional Passphrase (BIP39) writtenDown: 'Ich bestätige, dass ich die Seed Phrase notiert habe', //I have written down my seed phrase + // Seed Phrase Import + importSeedValid: '', //Seed Phrase is valid! + importSeedError: '', //Seed Phrase is invalid! + importSeedErrorSize: '', //A Seed Phrase should be 12 or 24 words long! + importSeedErrorTypo: '', //Seed Phrase contains typing errors! Check your input carefully + importSeedErrorSkip: '', //Seed Phrase appears invalid, but the warning was skipped by the user + // Wallet Dashboard gettingStarted: 'Erste Schritte', //Getting Started secureYourWallet: 'Verschlüssel deine Geldbörse', //Secure your wallet @@ -181,6 +188,8 @@ export const de_translation = { settingsAnalytics: 'Wähle die verwendeten Analysedaten dieser Sitzung', //Choose your analytics contribution level: settingsToggleDebug: 'Debug Modus', //Debug Mode settingsToggleTestnet: 'Testnet Modus', //Testnet Mode + settingsToggleAdvancedMode: '', //Advanced Mode + settingsToggleAdvancedModeSubtext: '', //This unlocks deeper functionality and customisation, but may be overwhelming and potentially dangerous for unexperienced users! // Transparency Report transparencyReport: 'Transparenz-Bericht', //Transparency Report diff --git a/locale/en/translation.js b/locale/en/translation.js index 9484381dc..e5bd621ab 100644 --- a/locale/en/translation.js +++ b/locale/en/translation.js @@ -63,9 +63,18 @@ export const en_translation = { 'Anyone with a copy of it can access all of your funds.', // doNotShare: 'Do NOT share it with anyone.', // digitalStoreNotAdvised: 'It is NOT advised to store this digitally.', // - optionalPassphrase: 'Optional Passphrase', // + optionalPassphrase: 'Optional Passphrase (BIP39)', // writtenDown: 'I have written down my seed phrase', // + // Seed Phrase Import + importSeedValid: 'Seed Phrase is valid!', // + importSeedError: 'Seed Phrase is invalid!', // + importSeedErrorSize: 'A Seed Phrase should be 12 or 24 words long!', // + importSeedErrorTypo: + 'Seed Phrase contains typing errors! Check your input carefully', // + importSeedErrorSkip: + 'Seed Phrase appears invalid, but the warning was skipped by the user', // + // Wallet Dashboard gettingStarted: 'Getting Started', // secureYourWallet: 'Secure your wallet', // @@ -182,6 +191,9 @@ export const en_translation = { settingsAnalytics: 'Choose your analytics contribution level:', // settingsToggleDebug: 'Debug Mode', // settingsToggleTestnet: 'Testnet Mode', // + settingsToggleAdvancedMode: 'Advanced Mode', // + settingsToggleAdvancedModeSubtext: + 'This unlocks deeper functionality and customisation, but may be overwhelming and potentially dangerous for unexperienced users!', // // Network switching (mainnet <---> testnet) netSwitchUnsavedWarningTitle: "Your {network} wallet isn't saved!", // diff --git a/locale/fr/translation.js b/locale/fr/translation.js index a1b16b34f..e4df4d7f9 100644 --- a/locale/fr/translation.js +++ b/locale/fr/translation.js @@ -66,9 +66,16 @@ export const fr_translation = { doNotShare: 'Ne le partagez avec personne.', //Do NOT share it with anybody. digitalStoreNotAdvised: 'NON il est conseillé de les stocker sous forme numérique.', //It is NOT advised to store this digitally. - optionalPassphrase: 'Phrase mot de passe Facultatif', //Optional Passphrase + optionalPassphrase: 'Phrase mot de passe Facultatif (BIP39)', //Optional Passphrase writtenDown: "J'ai écrit ma phrase d'introduction", //I have written down my seed phrase + // Seed Phrase Import + importSeedValid: '', //Seed Phrase is valid! + importSeedError: '', //Seed Phrase is invalid! + importSeedErrorSize: '', //A Seed Phrase should be 12 or 24 words long! + importSeedErrorTypo: '', //Seed Phrase contains typing errors! Check your input carefully + importSeedErrorSkip: '', //Seed Phrase appears invalid, but the warning was skipped by the user + // Wallet Dashboard gettingStarted: 'Démarrer', //Getting Started secureYourWallet: 'Protégez votre portefeuille', //Secure your wallet @@ -181,6 +188,8 @@ export const fr_translation = { settingsAnalytics: "Choisissez votre niveau d'analyse :", //Choose your analytics contribution level: settingsToggleDebug: 'Mode de débogage', //Debug Mode settingsToggleTestnet: 'Mode testnet', //Testnet Mode + settingsToggleAdvancedMode: '', //Advanced Mode + settingsToggleAdvancedModeSubtext: '', //This unlocks deeper functionality and customisation, but may be overwhelming and potentially dangerous for unexperienced users! // Network switching (mainnet <---> testnet) netSwitchUnsavedWarningTitle: '', //Your {network} wallet isn\'t saved! diff --git a/locale/ph/translation.js b/locale/ph/translation.js index 5d856a17e..672b2f35b 100644 --- a/locale/ph/translation.js +++ b/locale/ph/translation.js @@ -67,9 +67,16 @@ export const ph_translation = { doNotShare: 'WAG mo itong ibibigay kahit kanino', //Do NOT share it with anybody. digitalStoreNotAdvised: 'Ito ay HINDI payo upang itago ito digitally', //It is NOT advised to store this digitally. - optionalPassphrase: 'Optional Passphrase', //Optional Passphrase + optionalPassphrase: 'Optional Passphrase (BIP39)', //Optional Passphrase writtenDown: 'Isinulat ko na ang aking seed phrase', //I have written down my seed phrase + // Seed Phrase Import + importSeedValid: '', //Seed Phrase is valid! + importSeedError: '', //Seed Phrase is invalid! + importSeedErrorSize: '', //A Seed Phrase should be 12 or 24 words long! + importSeedErrorTypo: '', //Seed Phrase contains typing errors! Check your input carefully + importSeedErrorSkip: '', //Seed Phrase appears invalid, but the warning was skipped by the user + // Wallet Dashboard gettingStarted: 'Magsimula', //Getting Started secureYourWallet: 'I-secure ang iyong wallet', //Secure your wallet @@ -182,6 +189,8 @@ export const ph_translation = { settingsAnalytics: 'Pumili ng iyong analytics contribution level:', //Choose your analytics contribution level: settingsToggleDebug: 'Debug Mode', //Debug Mode settingsToggleTestnet: 'Testnet Mode', //Testnet Mode + settingsToggleAdvancedMode: '', //Advanced Mode + settingsToggleAdvancedModeSubtext: '', //This unlocks deeper functionality and customisation, but may be overwhelming and potentially dangerous for unexperienced users! // Network switching (mainnet <---> testnet) netSwitchUnsavedWarningTitle: '', //Your {network} wallet isn\'t saved! diff --git a/locale/pt-br/translation.js b/locale/pt-br/translation.js index 1a3262d27..1e7bb99d9 100644 --- a/locale/pt-br/translation.js +++ b/locale/pt-br/translation.js @@ -66,9 +66,16 @@ export const pt_br_translation = { doNotShare: 'NÃO a compartilhe com ninguém.', //Do NOT share it with anybody. digitalStoreNotAdvised: 'NÃO é aconselhável armazená-la digitalmente.', //It is NOT advised to store this digitally. - optionalPassphrase: 'Frasse-Passe Opcional', //Optional Passphrase + optionalPassphrase: 'Frasse-Passe Opcional (BIP39)', //Optional Passphrase writtenDown: 'Eu escrevi a minha Seed Phrase', //I have written down my seed phrase + // Seed Phrase Import + importSeedValid: '', //Seed Phrase is valid! + importSeedError: '', //Seed Phrase is invalid! + importSeedErrorSize: '', //A Seed Phrase should be 12 or 24 words long! + importSeedErrorTypo: '', //Seed Phrase contains typing errors! Check your input carefully + importSeedErrorSkip: '', //Seed Phrase appears invalid, but the warning was skipped by the user + // Wallet Dashboard gettingStarted: 'Começar', //Getting Started secureYourWallet: 'Proteja a sua carteira', //Secure your wallet @@ -182,6 +189,8 @@ export const pt_br_translation = { settingsAnalytics: 'Escolha o seu nível de contribuição analítica:', //Choose your analytics contribution level: settingsToggleDebug: 'Modo de depuração', //Debug Mode settingsToggleTestnet: 'Modo Testnet', //Testnet Mode + settingsToggleAdvancedMode: '', //Advanced Mode + settingsToggleAdvancedModeSubtext: '', //This unlocks deeper functionality and customisation, but may be overwhelming and potentially dangerous for unexperienced users! // Network switching (mainnet <---> testnet) netSwitchUnsavedWarningTitle: '', //Your {network} wallet isn\'t saved! diff --git a/locale/pt-pt/translation.js b/locale/pt-pt/translation.js index 1ffefc698..59ad33f10 100644 --- a/locale/pt-pt/translation.js +++ b/locale/pt-pt/translation.js @@ -65,9 +65,16 @@ export const pt_pt_translation = { doNotShare: 'NÃO a compartilhe com ninguém.', //Do NOT share it with anybody. digitalStoreNotAdvised: 'NÃO é aconselhável armazená-lo digitalmente.', //It is NOT advised to store this digitally. - optionalPassphrase: 'Frase Senha Opcional', //Optional Passphrase + optionalPassphrase: 'Frase Senha Opcional (BIP39)', //Optional Passphrase writtenDown: 'Eu escrevi a minha frase-inicial', //I have written down my seed phrase + // Seed Phrase Import + importSeedValid: '', //Seed Phrase is valid! + importSeedError: '', //Seed Phrase is invalid! + importSeedErrorSize: '', //A Seed Phrase should be 12 or 24 words long! + importSeedErrorTypo: '', //Seed Phrase contains typing errors! Check your input carefully + importSeedErrorSkip: '', //Seed Phrase appears invalid, but the warning was skipped by the user + // Wallet Dashboard gettingStarted: 'A Começar', //Getting Started secureYourWallet: 'Proteja a sua carteira', //Secure your wallet @@ -181,6 +188,8 @@ export const pt_pt_translation = { settingsAnalytics: 'Escolha o seu nível de contribuição analítica:', //Choose your analytics contribution level: settingsToggleDebug: 'Modo de depuração', //Debug Mode settingsToggleTestnet: 'Modo Testnet', //Testnet Mode + settingsToggleAdvancedMode: '', //Advanced Mode + settingsToggleAdvancedModeSubtext: '', //This unlocks deeper functionality and customisation, but may be overwhelming and potentially dangerous for unexperienced users! // Network switching (mainnet <---> testnet) netSwitchUnsavedWarningTitle: '', //Your {network} wallet isn\'t saved! diff --git a/locale/template/translation.js b/locale/template/translation.js index a99d67995..63a85d736 100644 --- a/locale/template/translation.js +++ b/locale/template/translation.js @@ -78,9 +78,16 @@ var translation = { doNotShareWarning: '', //Anyone with a copy of it can access all of your funds. doNotShare: '', //Do NOT share it with anybody. digitalStoreNotAdvised: '', //It is NOT advised to store this digitally. - optionalPassphrase: '', //Optional Passphrase + optionalPassphrase: '', //Optional Passphrase (BIP39) writtenDown: '', //I have written down my seed phrase + // Seed Phrase Import + importSeedValid: '', //Seed Phrase is valid! + importSeedError: '', //Seed Phrase is invalid! + importSeedErrorSize: '', //A Seed Phrase should be 12 or 24 words long! + importSeedErrorTypo: '', //Seed Phrase contains typing errors! Check your input carefully + importSeedErrorSkip: '', //Seed Phrase appears invalid, but the warning was skipped by the user + // Wallet Dashboard gettingStarted: '', //Getting Started secureYourWallet: '', //Secure your wallet @@ -188,6 +195,8 @@ var translation = { settingsAnalytics: '', //Choose your analytics contribution level: settingsToggleDebug: '', //Debug Mode settingsToggleTestnet: '', //Testnet Mode + settingsToggleAdvancedMode: '', //Advanced Mode + settingsToggleAdvancedModeSubtext: '', //This unlocks deeper functionality and customisation, but may be overwhelming and potentially dangerous for unexperienced users! // Network switching (mainnet <---> testnet) netSwitchUnsavedWarningTitle: '', //Your {network} wallet isn\'t saved! diff --git a/locale/uwu/translation.js b/locale/uwu/translation.js index 744d39214..ec83afd0d 100644 --- a/locale/uwu/translation.js +++ b/locale/uwu/translation.js @@ -65,9 +65,18 @@ export const uwu_translation = { doNotShare: 'Do NOT share it with anyuwu.', //Do NOT share it with anybody. digitalStoreNotAdvised: 'It is NAWT advised to store this digitally.', //It is NOT advised to store this digitally. - optionalPassphrase: 'Optional Passphwase', //Optional Passphrase + optionalPassphrase: 'Optional Passphwase (BIP39)', //Optional Passphrase writtenDown: 'I haz written down my seed phrase', //I have written down my seed phrase + // Seed Phrase Import + importSeedValid: 'Seed Phwase iz valid!', //Seed Phrase is valid! + importSeedError: 'Seed Phwase iz invalid!', //Seed Phrase is invalid! + importSeedErrorSize: 'A Seed Phwase shwould be 12 or 24 words long!', //A Seed Phrase should be 12 or 24 words long! + importSeedErrorTypo: + 'Seed Phwase contains typing ewrrors! Check ur input carefully', //Seed Phrase contains typing errors! Check your input carefully + importSeedErrorSkip: + 'Seed Phwase appears invalid, but da warning was skipped by da user', //Seed Phrase appears invalid, but the warning was skipped by the user + // Wallet Dashboard gettingStarted: 'Getting Stwarted', //Getting Started secureYourWallet: 'Secure ur wawwet', //Secure your wallet @@ -185,6 +194,9 @@ export const uwu_translation = { settingsAutoSelectNet: 'Auto-select Expwowers and Nowodes', // Auto-select Explorers and Nodes settingsToggleDebug: 'Debug Mowode', //Debug Mode settingsToggleTestnet: 'Testnet Mowode', //Testnet Mode + settingsToggleAdvancedMode: 'Advwanced Mowode', //Advanced Mode + settingsToggleAdvancedModeSubtext: + 'Dis unlocks deeper fwunctionality and cuwustomisatwion, but may be oveuhwhelming and potentially dangerwus for unexperienced bakas!', //This unlocks deeper functionality and customisation, but may be overwhelming and potentially dangerous for unexperienced users! // Network switching (mainnet <---> testnet) netSwitchUnsavedWarningTitle: "Ur {network} wawwet isn't saved!", //Your {network} wallet isn\'t saved! diff --git a/scripts/global.js b/scripts/global.js index d3a6f345c..777445bea 100644 --- a/scripts/global.js +++ b/scripts/global.js @@ -22,6 +22,7 @@ import { setColdStakingAddress, strColdStakingAddress, nDisplayDecimals, + fAdvancedMode, } from './settings.js'; import { createAndSendTransaction, signTransaction } from './transactions.js'; import { @@ -319,6 +320,7 @@ export async function start() { domVersion: document.getElementById('version'), domFlipdown: document.getElementById('flipdown'), domTestnetToggler: document.getElementById('testnetToggler'), + domAdvancedModeToggler: document.getElementById('advancedModeToggler'), }; await i18nStart(); await loadImages(); @@ -1529,19 +1531,28 @@ export async function accessOrImportWallet() { * * Useful for adjusting the input types or displaying password prompts depending on the import scheme */ -export async function onPrivateKeyChanged() { +export async function guiUpdateImportInput() { if (await hasEncryptedWallet()) return; // Check whether the string is Base64 (would likely be an MPW-encrypted import) // and it doesn't have any spaces (would be a mnemonic seed) - const fContainsSpaces = doms.domPrivKey.value.includes(' '); + const fContainsSpaces = doms.domPrivKey.value.trim().includes(' '); + + // If this could require a Seed Passphrase (BIP39 Passphrase) and Advanced Mode is enabled + // ...or if this is an Encrypted Import (Encrypted Base64 MPW key) + const fBIP39Passphrase = fContainsSpaces && fAdvancedMode; doms.domPrivKeyPassword.hidden = (doms.domPrivKey.value.length < 128 || !isBase64(doms.domPrivKey.value)) && - !fContainsSpaces; + !fBIP39Passphrase; doms.domPrivKeyPassword.placeholder = fContainsSpaces ? translation.optionalPassphrase : translation.password; + + // If the "Import Password/Passphrase" is hidden, we'll also wipe it's input, in the + // ... edge-case that a passphrase was entered, then the import key had changed. + if (doms.domPrivKeyPassword.hidden) doms.domPrivKeyPassword.value = ''; + // Uncloak the private input IF spaces are detected, to make Seed Phrases easier to input and verify doms.domPrivKey.setAttribute('type', fContainsSpaces ? 'text' : 'password'); } diff --git a/scripts/index.js b/scripts/index.js index 9187e943d..6610efcee 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -19,7 +19,7 @@ export { accessOrImportWallet, guiImportWallet, guiSetColdStakingAddress, - onPrivateKeyChanged, + guiUpdateImportInput, toClipboard, toggleExportUI, wipePrivateData, @@ -46,7 +46,12 @@ export { govVote, } from './global.js'; export { generateWallet, getNewAddress, importWallet } from './wallet.js'; -export { toggleTestnet, toggleDebug, toggleAutoSwitch } from './settings.js'; +export { + toggleTestnet, + toggleDebug, + toggleAutoSwitch, + toggleAdvancedMode, +} from './settings.js'; export { createTxGUI, undelegateGUI, diff --git a/scripts/settings.js b/scripts/settings.js index 23b8ed361..9beaf8bc7 100644 --- a/scripts/settings.js +++ b/scripts/settings.js @@ -2,6 +2,7 @@ import { doms, getBalance, getStakingBalance, + guiUpdateImportInput, mempool, refreshChainData, renderActivityGUI, @@ -52,6 +53,8 @@ export let fAutoSwitch = true; export let strColdStakingAddress = 'SdgQDpS8jDRJDX8yK8m9KnTMarsE84zdsy'; /** The decimals to display for the wallet balance */ export let nDisplayDecimals = 2; +/** A mode which configures MPW towards Advanced users, with low-level feature access and less restrictions (Potentially dangerous) */ +export let fAdvancedMode = false; let transparencyReport; @@ -88,6 +91,10 @@ export class Settings { * @type {number} The decimals to display for the wallet balance */ displayDecimals; + /** + * @type {boolean} Whether Advanced Mode is enabled or disabled + */ + advancedMode; constructor({ analytics, explorer, @@ -97,6 +104,7 @@ export class Settings { translation = '', displayCurrency = 'usd', displayDecimals = nDisplayDecimals, + advancedMode = false, } = {}) { this.analytics = analytics; this.explorer = explorer; @@ -106,6 +114,7 @@ export class Settings { this.translation = translation; this.displayCurrency = displayCurrency; this.displayDecimals = displayDecimals; + this.advancedMode = advancedMode; } } @@ -196,15 +205,22 @@ export async function start() { coldAddress, displayCurrency, displayDecimals, + advancedMode, } = await database.getSettings(); // Set the Cold Staking address strColdStakingAddress = coldAddress; // Set any Toggles to their default or DB state + // Network Auto-Switch fAutoSwitch = autoswitch; doms.domAutoSwitchToggle.checked = fAutoSwitch; + // Advanced Mode + fAdvancedMode = advancedMode; + doms.domAdvancedModeToggler.checked = fAdvancedMode; + await configureAdvancedMode(); + // Set the display currency strCurrency = doms.domCurrencySelect.value = displayCurrency; @@ -620,3 +636,29 @@ async function fillNodeSelect() { // And update the UI to reflect them doms.domNodeSelect.value = cNode.url; } + +/** + * Toggle Advanced Mode at runtime and in DB + */ +export async function toggleAdvancedMode() { + fAdvancedMode = !fAdvancedMode; + + // Configure the app accordingly + await configureAdvancedMode(); + + // Update the setting in the DB + const database = await Database.getInstance(); + await database.setSettings({ advancedMode: fAdvancedMode }); +} + +/** + * Configure the app functionality and UI for the current mode + */ +async function configureAdvancedMode() { + // Re-render the Import Input UI + await guiUpdateImportInput(); + + // Hide or Show the "Mnemonic Passphrase" in the Seed Creation modal, and reset it's input + doms.domMnemonicModalPassphrase.value = ''; + doms.domMnemonicModalPassphrase.hidden = !fAdvancedMode; +} diff --git a/scripts/wallet.js b/scripts/wallet.js index 0c1d52727..fc91fbe76 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -38,6 +38,7 @@ import * as jdenticon from 'jdenticon'; import { Database } from './database.js'; import { guiRenderCurrentReceiveModal } from './contacts-book.js'; import { Account } from './accounts.js'; +import { debug, fAdvancedMode } from './settings.js'; export let fWalletLoaded = false; @@ -657,39 +658,57 @@ export async function importWallet({ doms.domPrivKey.value = ''; doms.domPrivKeyPassword.value = ''; - if (await verifyMnemonic(privateImportValue)) { - // Generate our masterkey via Mnemonic Phrase + // Clean and verify the Seed Phrase (if one exists) + const cPhraseValidator = await cleanAndVerifySeedPhrase( + privateImportValue, + true + ); + + // If Debugging is enabled, show what the validator returned + if (debug) { + const fnLog = cPhraseValidator.ok ? console.log : console.warn; + fnLog('Seed Import Validator: ' + cPhraseValidator.msg); + } + + // If the Seed is OK, proceed + if (cPhraseValidator.ok) { + // Generate our HD MasterKey with the cleaned (Mnemonic) Seed Phrase const seed = await mnemonicToSeed( - privateImportValue, + cPhraseValidator.phrase, passphrase ); await setMasterKey(new HdMasterKey({ seed })); + } else if (cPhraseValidator.phrase.includes(' ')) { + // The Phrase Validator failed, but the input contains at least one space; possibly a Seed Typo? + return createAlert('warning', cPhraseValidator.msg, 5000); } else { - // Public Key Derivation + // The input definitely isn't a seed, so we'll try every other import method try { + // XPub import (HD view only) if (isXPub(privateImportValue)) { await setMasterKey( new HdMasterKey({ xpub: privateImportValue, }) ); + // XPrv import (HD full access) } else if (privateImportValue.startsWith('xprv')) { await setMasterKey( new HdMasterKey({ xpriv: privateImportValue, }) ); + // Pubkey import (non-HD view only) } else if (isStandardAddress(privateImportValue)) { await setMasterKey( new LegacyMasterKey({ address: privateImportValue, }) ); + // WIF import (non-HD full access) } else { - // Lastly, attempt to parse as a WIF private key + // Attempt to import a raw WIF private key const pkBytes = parseWIF(privateImportValue); - - // Import the raw private key await setMasterKey(new LegacyMasterKey({ pkBytes })); } } catch (e) { @@ -813,37 +832,86 @@ export async function generateWallet(noUI = false) { return masterKey; } -export async function verifyMnemonic(strMnemonic = '', fPopupConfirm = true) { - const nWordCount = strMnemonic.trim().split(/\s+/g).length; +/** + * Clean a Seed Phrase string and verify it's integrity + * + * This returns an object of the validation status and the cleaned Seed Phrase for safe low-level usage. + * @param {String} strPhraseInput - The Seed Phrase string + * @param {Boolean} fPopupConfirm - Allow a warning bypass popup if the Seed Phrase is unusual + */ +export async function cleanAndVerifySeedPhrase( + strPhraseInput = '', + fPopupConfirm = true +) { + // Clean the phrase (removing unnecessary spaces) and force to lowercase + const strPhrase = strPhraseInput.trim().replace(/\s+/g, ' ').toLowerCase(); - // Sanity check: Convert to lowercase - strMnemonic = strMnemonic.toLowerCase(); + // Count the Words + const nWordCount = strPhrase.trim().split(' ').length; // Ensure it's a word count that makes sense - if (nWordCount >= 12 && nWordCount <= 24) { - if (!validateMnemonic(strMnemonic)) { + if (nWordCount === 12 || nWordCount === 24) { + if (!validateMnemonic(strPhrase)) { + // If a popup is allowed and Advanced Mode is enabled, warn the user that the + // ... seed phrase is potentially bad, and ask for confirmation to proceed + if (!fPopupConfirm || !fAdvancedMode) + return { + ok: false, + msg: translation.importSeedErrorTypo, + phrase: strPhrase, + }; + // The reason we want to ask the user for confirmation is that the mnemonic - // Could have been generated with another app that has a different dictionary - return ( - fPopupConfirm && - (await confirmPopup({ - title: translation.popupSeedPhraseBad, - html: translation.popupSeedPhraseBadNote, - })) - ); + // could have been generated with another app that has a different dictionary + const fSkipWarning = await confirmPopup({ + title: translation.popupSeedPhraseBad, + html: translation.popupSeedPhraseBadNote, + }); + + if (fSkipWarning) { + // User is probably an Arch Linux user and used `-f` + return { + ok: true, + msg: translation.importSeedErrorSkip, + phrase: strPhrase, + }; + } else { + // User heeded the warning and rejected the phrase + return { + ok: false, + msg: translation.importSeedError, + phrase: strPhrase, + }; + } } else { // Valid count and mnemonic - return true; + return { + ok: true, + msg: translation.importSeedValid, + phrase: strPhrase, + }; } } else { // Invalid count - return false; + return { + ok: false, + msg: translation.importSeedErrorSize, + phrase: strPhrase, + }; } } +/** + * Display a Seed Phrase popup to the user and optionally wait for a Seed Passphrase + * @param {string} mnemonic - The Seed Phrase to display to the user + * @returns {Promise} - The Mnemonic Passphrase (empty string if omitted by user) + */ function informUserOfMnemonic(mnemonic) { return new Promise((res, _) => { + // Configure the modal $('#mnemonicModal').modal({ keyboard: false }); + + // Render the Seed Phrase and configure the button doms.domMnemonicModalContent.innerText = mnemonic; doms.domMnemonicModalButton.onclick = () => { res(doms.domMnemonicModalPassphrase.value); @@ -853,6 +921,8 @@ function informUserOfMnemonic(mnemonic) { doms.domMnemonicModalContent.innerText = ''; doms.domMnemonicModalPassphrase.value = ''; }; + + // Display the modal $('#mnemonicModal').modal('show'); }); }