Files
hash-wallet/lib/view_model/wallet_restore_view_model.dart
T
e4672c75b7 CW-1135-add-support-for-bitbox-hww (#2476)
* chore: add ffi and ffigen dependencies to pubspec.yaml

* refactor: replace method channel with FFI implementation for MWeb module

* fix: correct `cd` command in build_mwebd.sh script

* chore: add go.sum file with dependency checksums

* fix: ensure ANDROID_NDK_VERSION is set before building and dynamically update NDK_BIN path

* fix: update NDK_BIN path to dynamically detect platform in build_mwebd.sh script

* refactor: remove unused mwebd dependencies and clean up CwMwebPlugin code

* Update scripts/android/build_mwebd.sh

Co-authored-by: cyan <cyjan@mrcyjanek.net>

* refactor: replace iOS MoneroWallet framework with Mwebd.xcframework and clean up plugin implementation [skip-ci]

* refactor: extract hardware wallet service abstraction

* refactor: extend BitcoinHardwareWalletService from HardwareWalletService and override account retrieval method

* feat: add BitBox hardware wallet support and implement manufacturer selection page with custom UI components

* refactor: update manufacturer selection UI and integrate custom gradient styles for ManufacturerOptionTile widget

* fix: mwebd ffi ios

* feat: add BitBox hardware wallet support, update derivation paths, and refactor hardware wallet credential implementations

* feat: add bitbox support and refactor the old ledger implementation

* feat: add BitBox hardware wallet support and refactor hardware wallet service implementations

* feat: refactor hardware wallet support to introduce abstraction for BitBox and Ledger; update device scanning, initialization, and UI integration

* fix: BitBox hardware wallet ethereum transaction signing

* chore: update BitBox Flutter path references and include build steps in CI workflows

* chore: update CI workflows to include `--dont-install` flag for BitBox Flutter builds

* refactor: replace if-else logic with switch-case for HardwareWalletViewModel factory

* feat: refactor hardware wallet services to introduce shared interface, improve ledger and bitbox implementations, and support additional transaction signing operations

* feat: add SVG assets for hardware wallet manufacturers (Foundation, Keystone, SeedSigner)

* feat: redesign hardware wallet restoration flow with improved routing, UI updates, and manufacturer-specific device selection handling

* fix: validate required BIP353 fields in transaction builder to prevent potential null value errors

* feat: improve BitBox transaction signing logs with detailed PSBT information

* refactor: update MWebFfi class to use non-nullable `lib` and improve address debug logging

* feat: add BitBox coin type constant and improve hardware wallet service extensibility

* chore: remove unused iOS/Android build scripts for mwebd

* feat: add BitBox SVG asset and update localization for hardware wallet connection instructions

* feat: update manufacturer tags and add Toast Easter egg for device selection interactions

* refactor: replace LedgerViewModel with HardwareWalletViewModel across providers and improve hardware wallet handling

* refactor: replace mweb.so with libmweb.so, use sync methods in MWebFfi, and update iOS/Android frameworks/scripts accordingly

* feat: add Ledger Nano X SVG asset to hardware wallet resources

* chore: unify mwebd build process in workflows, remove unused gomobile init command

* refactor: remove redundant print statement and unnecessary whitespace

* chore: add `gomobile init` to Android PR test build workflow

* chore: update Android PR test build workflow and add file existence check in `build_bitbox_flutter.sh`

* chore: override `ledger_usb_plus` dependency in pubspec_base.yaml

* feat: add verbose logging for BitBox connection and initialization process

* Update lib/view_model/hardware_wallet/bitbox_view_model.dart [skip ci]

* CW-1206-refactor-hardware-wallets-code (#2507)

* feat: add support for new hardware wallet types (Cupcake, Coldcard, SeedSigner) and improve QR Code handling

- Introduced new hardware wallet types (Cupcake, Coldcard, SeedSigner) with corresponding assets and localization updates.
- Updated QR Code format selection for hardware wallets with new formats (BCUR, BBQR) specific to devices.
- Improved hardware wallet restoration flow to include airgapped wallet handling via QR Code scanning.
- Refactored routing, view models, and dependency injection to accommodate new hardware wallet types.
- Enhanced UI for device manufacturer selection and QR Code format dialogs.

* feat: update hardware wallet assets and restoration flow

* feat: add changing airgapped wallet type

* feat: add Cupcake Man SVG asset to hardware wallet resources

* refactor: update Litecoin receive options to require wallet parameter and handle hardware wallets

* fix: debugging keystone integration
feat: support for zpub view only restore

* fix: seed signer restore

* fix: disable bitbox restoring (for now) [skip-ci]

* refactor: rename `isCupcake` to `isBitcoinViewOnly` for clarity [skip ci]

---------

Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* refactor: standardize hardware wallet connection handling across wallet types

---------

Co-authored-by: cyan <cyjan@mrcyjanek.net>
Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
2025-10-01 18:20:40 +03:00

339 lines
12 KiB
Dart

import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/core/generate_wallet_password.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/dogecoin/dogecoin.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/decred/decred.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cake_wallet/zano/zano.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
part 'wallet_restore_view_model.g.dart';
class WalletRestoreViewModel = WalletRestoreViewModelBase with _$WalletRestoreViewModel;
abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
Box<WalletInfo> walletInfoSource, SeedSettingsViewModel seedSettingsViewModel,
{required WalletType type, this.restoredWallet, this.hardwareWalletType})
: isButtonEnabled = restoredWallet != null,
hasPassphrase = false,
mode = restoredWallet?.restoreMode ?? WalletRestoreMode.seed,
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
type: type, isRecovery: true) {
switch (type) {
case WalletType.monero:
availableModes = WalletRestoreMode.values;
break;
case WalletType.nano:
case WalletType.banano:
case WalletType.solana:
case WalletType.tron:
case WalletType.wownero:
case WalletType.haven:
case WalletType.ethereum:
case WalletType.polygon:
case WalletType.decred:
case WalletType.bitcoin:
availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys];
break;
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.zano:
case WalletType.none:
case WalletType.dogecoin:
availableModes = [WalletRestoreMode.seed];
break;
}
walletCreationService.changeWalletType(type: type);
if (restoredWallet != null) {
if(restoredWallet!.restoreMode == WalletRestoreMode.seed) {
seedSettingsViewModel.setPassphrase(restoredWallet!.passphrase);
}
}
}
static const moneroSeedMnemonicLength = 25;
static const decredSeedMnemonicLength = 15;
late List<WalletRestoreMode> availableModes;
late final bool hasSeedLanguageSelector = [
WalletType.monero,
WalletType.haven,
WalletType.wownero
].contains(type);
late final bool hasBlockchainHeightSelector = [
WalletType.monero,
WalletType.haven,
WalletType.wownero
].contains(type);
late final bool hasRestoreFromPrivateKey = [
WalletType.ethereum,
WalletType.polygon,
WalletType.nano,
WalletType.banano,
WalletType.solana,
WalletType.tron
].contains(type);
late final bool onlyViewKeyRestore = [
if (FeatureFlag.hasBitcoinViewOnly) WalletType.bitcoin,
WalletType.decred
].contains(type);
final RestoredWallet? restoredWallet;
final HardwareWalletType? hardwareWalletType;
@observable
WalletRestoreMode mode;
@observable
bool hasPassphrase;
@observable
bool isButtonEnabled;
@override
WalletCredentials getCredentials(dynamic options) {
final password = walletPassword ?? generateWalletPassword();
String? passphrase = options['passphrase'] as String?;
final height = options['height'] as int? ?? 0;
name = options['name'] as String;
DerivationInfo? derivationInfo = options["derivationInfo"] as DerivationInfo?;
if (mode == WalletRestoreMode.seed) {
final seed = options['seed'] as String;
switch (type) {
case WalletType.monero:
return monero!.createMoneroRestoreWalletFromSeedCredentials(
name: name, height: height, mnemonic: seed, password: password, passphrase: passphrase??'');
case WalletType.bitcoin:
case WalletType.litecoin:
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
passphrase: passphrase,
derivationType: derivationInfo!.derivationType!,
derivationPath: derivationInfo.derivationPath!,
);
case WalletType.ethereum:
return ethereum!.createEthereumRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
passphrase: passphrase,
);
case WalletType.bitcoinCash:
return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
passphrase: passphrase,
);
case WalletType.dogecoin:
return dogecoin!.createDogeCoinRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
passphrase: passphrase,
);
case WalletType.nano:
case WalletType.banano:
return nano!.createNanoRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
derivationType: derivationInfo!.derivationType!,
passphrase: passphrase,
);
case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
passphrase: passphrase,
);
case WalletType.solana:
return solana!.createSolanaRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
passphrase: passphrase,
);
case WalletType.tron:
return tron!.createTronRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
passphrase: passphrase,
);
case WalletType.wownero:
return wownero!.createWowneroRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
passphrase: passphrase??'',
height: height,
);
case WalletType.zano:
return zano!.createZanoRestoreWalletFromSeedCredentials(
name: name,
password: password,
height: height,
passphrase: passphrase??'',
mnemonic: seed,
);
case WalletType.decred:
return decred!.createDecredRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
);
case WalletType.none:
case WalletType.haven:
break;
}
}
if (mode == WalletRestoreMode.keys) {
final viewKey = options['viewKey'] as String?;
final spendKey = options['spendKey'] as String?;
final address = options['address'] as String?;
switch (type) {
case WalletType.bitcoin:
return bitcoin!.createBitcoinWalletFromKeys(
name: name,
password: password,
xpub: viewKey!,
hardwareWalletType: hardwareWalletType,
);
case WalletType.monero:
return monero!.createMoneroRestoreWalletFromKeysCredentials(
name: name,
height: height,
spendKey: spendKey!,
viewKey: viewKey!,
address: address!,
password: password,
language: 'English',
);
case WalletType.ethereum:
return ethereum!.createEthereumRestoreWalletFromPrivateKey(
name: name,
privateKey: options['private_key'] as String,
password: password,
);
case WalletType.nano:
return nano!.createNanoRestoreWalletFromKeysCredentials(
name: name,
password: password,
seedKey: options['private_key'] as String,
derivationType: derivationInfo!.derivationType!,
);
case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromPrivateKey(
name: name,
password: password,
privateKey: options['private_key'] as String,
);
case WalletType.solana:
return solana!.createSolanaRestoreWalletFromPrivateKey(
name: name,
password: password,
privateKey: options['private_key'] as String,
);
case WalletType.tron:
return tron!.createTronRestoreWalletFromPrivateKey(
name: name,
password: password,
privateKey: options['private_key'] as String,
);
case WalletType.wownero:
return wownero!.createWowneroRestoreWalletFromKeysCredentials(
name: name,
height: height,
spendKey: spendKey!,
viewKey: viewKey!,
address: address!,
password: password,
language: 'English',
);
case WalletType.decred:
return decred!.createDecredRestoreWalletFromPubkeyCredentials(
name: name,
password: password,
pubkey: viewKey!,
);
default:
break;
}
}
throw Exception('Unexpected type: ${type.toString()}');
}
Future<List<DerivationInfo>> getDerivationInfo(dynamic credentials) async {
var list = <DerivationInfo>[];
var walletType = credentials["walletType"] as WalletType;
var appStore = getIt.get<AppStore>();
var node = appStore.settingsStore.getCurrentNode(walletType);
switch (walletType) {
case WalletType.bitcoin:
case WalletType.litecoin:
String? mnemonic = credentials['seed'] as String?;
String? passphrase = credentials['passphrase'] as String?;
if (mnemonic == null) break;
return bitcoin!.getDerivationsFromMnemonic(
mnemonic: mnemonic,
node: node,
passphrase: passphrase,
);
case WalletType.nano:
String? mnemonic = credentials['seed'] as String?;
String? seedKey = credentials['private_key'] as String?;
return nanoUtil!.getDerivationsFromMnemonic(
mnemonic: mnemonic,
seedKey: seedKey,
node: node,
);
default:
break;
}
return list;
}
@override
Future<WalletBase> process(WalletCredentials credentials) async {
if (mode == WalletRestoreMode.keys) {
return walletCreationService.restoreFromKeys(credentials, isTestnet: useTestnet);
}
return walletCreationService.restoreFromSeed(credentials, isTestnet: useTestnet);
}
}