diff --git a/cw_evm/lib/evm_chain_exceptions.dart b/cw_evm/lib/evm_chain_exceptions.dart index 4e5f4df08..62dfb78b5 100644 --- a/cw_evm/lib/evm_chain_exceptions.dart +++ b/cw_evm/lib/evm_chain_exceptions.dart @@ -1,5 +1,11 @@ import 'package:cw_core/crypto_currency.dart'; +class EVMChainMnemonicIsIncorrectException implements Exception { + @override + String toString() => + 'EVM mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.'; +} + class EVMChainTransactionCreationException implements Exception { final String exceptionMessage; diff --git a/cw_evm/lib/evm_chain_wallet_service.dart b/cw_evm/lib/evm_chain_wallet_service.dart index 425bdceb8..5a2930012 100644 --- a/cw_evm/lib/evm_chain_wallet_service.dart +++ b/cw_evm/lib/evm_chain_wallet_service.dart @@ -11,6 +11,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:path/path.dart' as p; import 'package:cw_evm/clients/evm_chain_client.dart'; import 'package:cw_evm/evm_chain_client_factory.dart'; +import 'package:cw_evm/evm_chain_exceptions.dart'; import 'package:cw_evm/evm_chain_registry.dart'; import 'package:cw_evm/evm_chain_wallet.dart'; import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart'; @@ -199,6 +200,10 @@ class EVMChainWalletService extends WalletService< EVMChainRestoreWalletFromSeedCredentials credentials, { bool? isTestnet, }) async { + if (!bip39.validateMnemonic(credentials.mnemonic)) { + throw EVMChainMnemonicIsIncorrectException(); + } + final walletInfo = credentials.walletInfo!; // Get chainId from wallet type diff --git a/lib/view_model/restore/wallet_restore_from_qr_code.dart b/lib/view_model/restore/wallet_restore_from_qr_code.dart index 732729d6c..f1456b9b5 100644 --- a/lib/view_model/restore/wallet_restore_from_qr_code.dart +++ b/lib/view_model/restore/wallet_restore_from_qr_code.dart @@ -11,6 +11,7 @@ import 'package:cake_wallet/view_model/restore/restore_mode.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; import 'package:cw_core/currency_for_wallet_type.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:bip39/bip39.dart' as bip39; import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:collection/collection.dart'; @@ -89,9 +90,7 @@ class WalletRestoreFromQRCode { try { return AddressResolverUtils.extractAddressByType( raw: rawString, - type: walletTypeToCryptoCurrency( - type, - ), + type: walletTypeToCryptoCurrency(type), requireSurroundingWhitespaces: false, ); } catch (_) { @@ -127,8 +126,8 @@ class WalletRestoreFromQRCode { final prefix = code.startsWith('xpub') ? 'xpub' : code.startsWith('zpub') - ? 'zpub' - : '????'; + ? 'zpub' + : '????'; if (walletType == null) { await _specifyWalletAssets(context, "Can't determine wallet type, please pick it manually"); walletType = @@ -140,8 +139,8 @@ class WalletRestoreFromQRCode { formattedUri = seedPhrase != null ? '$walletType:?seed=$seedPhrase' : code.startsWith(prefix) - ? '$walletType:?$prefix=$code' - : throw Exception('Failed to determine valid seed phrase'); + ? '$walletType:?$prefix=$code' + : throw Exception('Failed to determine valid seed phrase'); } else { final index = code.indexOf(':'); final query = code.substring(index + 1).replaceAll('?', '&'); @@ -202,9 +201,15 @@ class WalletRestoreFromQRCode { seedValue.split(' ').forEach((element) { if (!words.contains(element)) { throw Exception( - "Unexpected restore mode: mnemonic_seed is invalid or doesn't match wallet type"); + "Unexpected restore mode: mnemonic_seed is invalid or doesn't match wallet type", + ); } }); + if (isEVMCompatibleChain(type) && !bip39.validateMnemonic(seedValue)) { + throw Exception( + 'EVM mnemonic has an invalid checksum. Please check the seed phrase for typos.', + ); + } return WalletRestoreMode.seed; } @@ -268,12 +273,14 @@ class WalletRestoreFromQRCode { Future _specifyWalletAssets(BuildContext context, String error) async { await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.current.error, - alertContent: error, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.current.error, + alertContent: error, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop(), + ); + }, + ); }