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>
This commit is contained in:
Konstantin Ullrich
2025-10-01 18:20:40 +03:00
committed by GitHub
co-authored by Czarek Nakamoto Omar Hatem
parent e1bb0e4a69
commit e4672c75b7
117 changed files with 2048 additions and 723 deletions
@@ -28,7 +28,7 @@ abstract class ReceiveOptionViewModelBase with Store {
break;
case WalletType.litecoin:
_options = [
...bitcoin!.getLitecoinReceivePageOptions(),
...bitcoin!.getLitecoinReceivePageOptions(_wallet),
...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet)
];
break;
@@ -0,0 +1,81 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:ur/cbor_lite.dart';
import 'package:ur/ur.dart';
import 'package:mobx/mobx.dart';
import 'package:ur/ur_decoder.dart';
part 'qr_tools_view_model.g.dart';
class QRToolsViewModel = QRToolsViewModelBase with _$QRToolsViewModel;
enum DevQrType {
zpub,
xpub,
bbqr,
bcur,
unknown,
}
abstract class QRToolsViewModelBase with Store {
@observable
String input = '';
@computed
Map<String, dynamic> get data => _getResult(input);
Uint8List _decodeCBOR(Uint8List cbor) {
final cborDecoder = CBORDecoder(cbor);
final out = cborDecoder.decodeBytes();
return out.$1;
}
Map<String, dynamic> _getResult(String input) {
try {
final decoder = URDecoder();
for (var part in input.split('\n')) {
part = part.toLowerCase();
part = part.trim();
if (part.startsWith('-')) {
part = part.substring(1).trim();
}
if (!part.startsWith('ur:')) {
continue;
}
decoder.receivePart(part);
}
return {
"result": (decoder.result != null) ? switch (decoder.result.runtimeType) {
UR => {
"cbor": (decoder.result as UR).cbor,
"_cbor.decode.base64": base64.encode(_decodeCBOR((decoder.result as UR).cbor)),
"type": (decoder.result as UR).type,
"toString": (decoder.result as UR).toString(),
},
_ => "unknown type: ${decoder.result.runtimeType}"
} : null,
"expectedType": decoder.expectedType,
"estimatedPercentComplete": decoder.estimatedPercentComplete(),
"processedPartsCount": decoder.processedPartsCount(),
"expectedPartCount": decoder.expectedPartCount(),
"isComplete": decoder.isComplete(),
"isFailure": decoder.isFailure(),
"isSuccess": decoder.isSuccess(),
"lastPartIndexes": decoder.lastPartIndexes(),
"receivedPartIndexes": decoder.receivedPartIndexes(),
"resultError": decoder.resultError(),
"resultMessage": decoder.resultMessage(),
"toString": decoder.toString(),
};
} catch (e) {
return {
"error": e.toString(),
};
}
}
}
@@ -0,0 +1,110 @@
import 'dart:async';
import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/root_dir.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:bitbox_flutter/bitbox_flutter.dart' as sdk;
import 'package:mobx/mobx.dart';
part 'bitbox_view_model.g.dart';
class BitboxViewModel = BitboxViewModelBase with _$BitboxViewModel;
abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
late final sdk.BitboxManager bitboxManager;
BitboxViewModelBase() {
if (!Platform.isIOS && !isMoneroOnly) {
bitboxManager = sdk.BitboxManager();
}
}
@override
@observable
bool isBleEnabled = false;
@override
bool get hasBluetooth => Platform.isIOS && false; // TODO: remove when we enable bluetooth
@override
Future<void> updateBleState() async {}
@override
Stream<HardwareWalletDevice> scanForBleDevices() => throw UnimplementedError();
@override
Future<List<HardwareWalletDevice>> getAllUsbDevices() => bitboxManager.devices
.then((devices) => devices.map((d) => BitboxHardwareWalletDevice(d)).toList());
@override
Future<void> stopScanning() async {}
@override
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async {
if (!(device is BitboxHardwareWalletDevice)) return false;
if (_isConnecting) return false;
_isConnecting = true;
try {
final appDocDir = await getAppDir();
await bitboxManager.connect(device.device);
printV("Got Connection", file: '${appDocDir.path}/error.txt');
await bitboxManager.initBitBox();
printV("Bitbox initialized!", file: '${appDocDir.path}/error.txt');
await bitboxManager.channelHashVerify();
printV("Bitbox channel-hash verified!", file: '${appDocDir.path}/error.txt');
_isConnecting = false;
return true;
} catch (e) {
printV(e);
}
_isConnecting = false;
return false;
}
bool _isConnecting = false;
@override
bool get isConnected => false;
@override
HardwareWalletService getHardwareWalletService(WalletType type) {
switch (type) {
case WalletType.bitcoin:
return bitcoin!.getBitboxHardwareWalletService(bitboxManager, true);
case WalletType.litecoin:
return bitcoin!.getBitboxHardwareWalletService(bitboxManager, false);
case WalletType.ethereum:
return ethereum!.getBitboxHardwareWalletService(bitboxManager);
case WalletType.polygon:
return polygon!.getBitboxHardwareWalletService(bitboxManager);
default:
throw UnimplementedError();
}
}
@override
void initWallet(WalletBase wallet) {
switch (wallet.type) {
case WalletType.bitcoin:
case WalletType.litecoin:
return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
case WalletType.ethereum:
return ethereum!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
case WalletType.polygon:
return polygon!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
default:
throw Exception('Unexpected wallet type: ${wallet.type}');
}
}
}
@@ -0,0 +1,27 @@
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
abstract class HardwareWalletViewModel {
bool get isConnected;
bool get isBleEnabled;
bool get hasBluetooth;
Future<void> updateBleState();
Stream<HardwareWalletDevice> scanForBleDevices();
Future<List<HardwareWalletDevice>> getAllUsbDevices();
Future<void> stopScanning();
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type);
HardwareWalletService getHardwareWalletService(WalletType type);
void initWallet(WalletBase wallet);
String? interpretErrorCode(String error) => null;
}
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/main.dart';
@@ -9,10 +10,13 @@ import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/hardware/device_connection_type.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/widgets.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as sdk;
@@ -23,15 +27,14 @@ part 'ledger_view_model.g.dart';
class LedgerViewModel = LedgerViewModelBase with _$LedgerViewModel;
abstract class LedgerViewModelBase with Store {
// late final Ledger ledger;
abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
late final sdk.LedgerInterface ledgerPlusBLE;
late final sdk.LedgerInterface ledgerPlusUSB;
bool get _doesSupportHardwareWallets {
if (isMoneroOnly) {
return DeviceConnectionType.supportedConnectionTypes(
WalletType.monero, Platform.isIOS)
WalletType.monero, HardwareWalletType.ledger, Platform.isIOS)
.isNotEmpty;
}
@@ -40,8 +43,8 @@ abstract class LedgerViewModelBase with Store {
LedgerViewModelBase() {
if (_doesSupportHardwareWallets) {
reaction((_) => bleIsEnabled, (_) {
if (bleIsEnabled) _initBLE();
reaction((_) => isBleEnabled, (_) {
if (isBleEnabled) _initBLE();
});
updateBleState();
@@ -51,13 +54,17 @@ abstract class LedgerViewModelBase with Store {
}
}
@override
@observable
bool bleIsEnabled = false;
bool isBleEnabled = false;
@override
bool get hasBluetooth => true;
bool _bleIsInitialized = false;
Future<void> _initBLE() async {
if (bleIsEnabled && !_bleIsInitialized) {
if (isBleEnabled && !_bleIsInitialized) {
ledgerPlusBLE = sdk.LedgerInterface.ble(
onPermissionRequest: (_) async {
if (Platform.isMacOS) return true;
@@ -76,24 +83,32 @@ abstract class LedgerViewModelBase with Store {
}
}
@override
Future<void> updateBleState() async {
final bleState = await sdk.UniversalBle.getBluetoothAvailabilityState();
final newState = bleState == sdk.AvailabilityState.poweredOn;
if (newState != bleIsEnabled) bleIsEnabled = newState;
if (newState != isBleEnabled) isBleEnabled = newState;
}
Stream<sdk.LedgerDevice> scanForBleDevices() => ledgerPlusBLE.scan();
@override
Stream<HardwareWalletDevice> scanForBleDevices() =>
ledgerPlusBLE.scan().map((d) => LedgerHardwareWalletDevice(d));
Stream<sdk.LedgerDevice> scanForUsbDevices() => ledgerPlusUSB.scan();
@override
Future<List<HardwareWalletDevice>> getAllUsbDevices() => ledgerPlusUSB.devices
.then((devices) => devices.map((d) => LedgerHardwareWalletDevice(d)).toList());
@override
Future<void> stopScanning() async {
if (_bleIsInitialized) await ledgerPlusBLE.stopScanning();
if (!Platform.isIOS) await ledgerPlusUSB.stopScanning();
}
Future<bool> connectLedger(sdk.LedgerDevice device, WalletType type) async {
@override
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async {
if (!(device is LedgerHardwareWalletDevice)) return false;
if (_isConnecting) return false;
_isConnecting = true;
_connectingWalletType = type;
@@ -103,18 +118,18 @@ abstract class LedgerViewModelBase with Store {
} catch (_) {}
}
final ledger = device.connectionType == sdk.ConnectionType.ble
final ledger = device.connectionType == HardwareWalletConnectionType.ble
? ledgerPlusBLE
: ledgerPlusUSB;
if (_connectionChangeSubscription == null) {
_connectionChangeSubscription = ledger
.deviceStateChanges(device.id)
.deviceStateChanges(device.device.id)
.listen(_connectionChangeListener);
}
try {
_connection = await ledger.connect(device);
_connection = await ledger.connect(device.device);
_isConnecting = false;
return true;
} catch (e) {
@@ -140,6 +155,7 @@ abstract class LedgerViewModelBase with Store {
Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: WalletType.monero,
hardwareWalletType: HardwareWalletType.ledger,
allowChangeWallet: true,
isReconnect: true,
onConnectDevice: (context, ledgerVM) async {
@@ -153,26 +169,47 @@ abstract class LedgerViewModelBase with Store {
}
}
@override
bool get isConnected => _connection != null && !(_connection!.isDisconnected);
sdk.LedgerConnection get connection => _connection!;
void setLedger(WalletBase wallet) {
@override
void initWallet(WalletBase wallet) {
switch (wallet.type) {
case WalletType.monero:
return monero!.setLedgerConnection(wallet, connection);
case WalletType.bitcoin:
return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
case WalletType.litecoin:
return bitcoin!.setLedgerConnection(wallet, connection);
return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
case WalletType.ethereum:
return ethereum!.setLedgerConnection(wallet, connection);
return ethereum!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
case WalletType.polygon:
return polygon!.setLedgerConnection(wallet, connection);
return polygon!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
default:
throw Exception('Unexpected wallet type: ${wallet.type}');
}
}
@override
HardwareWalletService getHardwareWalletService(WalletType type) {
switch (type) {
case WalletType.bitcoin:
return bitcoin!.getLedgerHardwareWalletService(connection, true);
case WalletType.litecoin:
return bitcoin!.getLedgerHardwareWalletService(connection, false);
case WalletType.ethereum:
return ethereum!.getLedgerHardwareWalletService(connection);
case WalletType.polygon:
return polygon!.getLedgerHardwareWalletService(connection);
default:
throw UnimplementedError();
}
}
@override
String? interpretErrorCode(String error) {
if (error.contains("Make sure no other program is communicating with the Ledger")) {
return error;
@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
import 'package:cw_core/utils/zpub.dart';
import 'package:cw_core/wallet_type.dart';
class RestoredWallet {
@@ -44,6 +45,9 @@ class RestoredWallet {
} catch (e) {
// fine, we don't care, it is only for monero anyway
}
if (json['zpub'] != null) {
json['xpub'] = convertZpubToXpub(json['zpub'] as String);
}
json['view_key'] ??= json['xpub'];
final height = json['height'] as String?;
return RestoredWallet(
@@ -58,6 +58,7 @@ class WalletRestoreFromQRCode {
final extracted = sortedKeys.firstWhereOrNull((key) => code.toLowerCase().contains(key));
if (code.startsWith("xpub")) return WalletType.bitcoin;
if (code.startsWith("zpub")) return WalletType.bitcoin;
if (extracted == null) {
// Special case for view-only monero wallet
@@ -109,9 +110,11 @@ class WalletRestoreFromQRCode {
if (code == null) throw Exception("Unexpected scan QR code value: aborted");
if (code.isEmpty) throw Exception('Unexpected scan QR code value: value is empty');
if (code.startsWith("[")) code = code.substring(code.indexOf("]") + 1);
String formattedUri = '';
WalletType? walletType = _extractWalletType(code);
final prefix = code.startsWith('xpub') ? 'xpub' : code.startsWith('zpub') ? 'zpub' : '????';
if (walletType == null) {
await _specifyWalletAssets(context, "Can't determine wallet type, please pick it manually");
walletType =
@@ -122,14 +125,14 @@ class WalletRestoreFromQRCode {
formattedUri = seedPhrase != null
? '$walletType:?seed=$seedPhrase'
: code.startsWith('xpub')
? '$walletType:?xpub=$code'
: code.startsWith(prefix)
? '$walletType:?$prefix=$code'
: throw Exception('Failed to determine valid seed phrase');
} else {
final index = code.indexOf(':');
final query = code.substring(index + 1).replaceAll('?', '&');
formattedUri = code.startsWith('xpub')
? '$walletType:?xpub=$code'
formattedUri = code.startsWith(prefix)
? '$walletType:?$prefix=$code'
:'$walletType:?$query';
}
@@ -167,7 +170,8 @@ class WalletRestoreFromQRCode {
throw Exception('Unexpected restore mode: tx_payment_id is invalid');
}
if (credentials.containsKey("xpub")) {
if (credentials.containsKey("xpub") ||
credentials.containsKey("zpub")) {
return WalletRestoreMode.keys;
}
+3 -2
View File
@@ -36,6 +36,7 @@ import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/send/fees_view_model.dart';
import 'package:cake_wallet/view_model/send/output.dart';
@@ -93,7 +94,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
this.balanceViewModel,
this.contactListViewModel,
this.transactionDescriptionBox,
this.ledgerViewModel,
this.hardwareWalletViewModel,
this.unspentCoinsListViewModel,
this.feesViewModel,
this.walletInfoSource, {
@@ -368,7 +369,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
final SendTemplateViewModel sendTemplateViewModel;
final BalanceViewModel balanceViewModel;
final ContactListViewModel contactListViewModel;
final LedgerViewModel? ledgerViewModel;
final HardwareWalletViewModel? hardwareWalletViewModel;
final FeesViewModel feesViewModel;
final FiatConversionStore _fiatConversationStore;
final Box<TransactionDescription> transactionDescriptionBox;
@@ -1,17 +1,15 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/entities/provider_types.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/package_info.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
// import 'package:package_info/package_info.dart';
import 'package:collection/collection.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
@@ -58,20 +56,28 @@ abstract class OtherSettingsViewModelBase with Store {
@computed
bool get changeRepresentativeEnabled =>
_wallet.type == WalletType.nano || _wallet.type == WalletType.banano;
[WalletType.nano, WalletType.banano].contains(_wallet.type);
@computed
bool get displayTransactionPriority => !(changeRepresentativeEnabled ||
_wallet.type == WalletType.solana ||
_wallet.type == WalletType.tron);
bool get changeHardwareWalletTypeEnabled => [
HardwareWalletType.cupcake,
HardwareWalletType.coldcard,
HardwareWalletType.seedsigner,
].contains(_wallet.hardwareWalletType);
@computed
bool get displayTransactionPriority =>
!(changeRepresentativeEnabled || [WalletType.solana, WalletType.tron].contains(_wallet.type));
String getDisplayPriority(dynamic priority) {
final _priority = priority as TransactionPriority;
if (_wallet.type == WalletType.bitcoin ||
_wallet.type == WalletType.litecoin ||
_wallet.type == WalletType.bitcoinCash ||
_wallet.type == WalletType.dogecoin) {
if ([
WalletType.bitcoin,
WalletType.litecoin,
WalletType.bitcoinCash,
WalletType.dogecoin,
].contains(_wallet.type)) {
final rate = bitcoin!.getFeeRate(_wallet, _priority);
return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate);
}
@@ -82,10 +88,12 @@ abstract class OtherSettingsViewModelBase with Store {
String getDisplayBitcoinPriority(dynamic priority, int customValue) {
final _priority = priority as TransactionPriority;
if (_wallet.type == WalletType.bitcoin ||
_wallet.type == WalletType.litecoin ||
_wallet.type == WalletType.bitcoinCash ||
_wallet.type == WalletType.dogecoin) {
if ([
WalletType.bitcoin,
WalletType.litecoin,
WalletType.bitcoinCash,
WalletType.dogecoin,
].contains(_wallet.type)) {
final rate = bitcoin!.getFeeRate(_wallet, _priority);
return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate,
customRate: customValue);
@@ -105,6 +113,12 @@ abstract class OtherSettingsViewModelBase with Store {
_settingsStore.priority[_wallet.type] = priority;
}
@action
Future<void> onHardwareWalletTypeChanged(HardwareWalletType hwType) async {
_wallet.walletInfo.hardwareWalletType = hwType;
return _wallet.walletInfo.save();
}
@computed
double get customBitcoinFeeRate =>
_settingsStore.customBitcoinFeeRate.toDouble();
@@ -473,7 +473,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
wallet.type == WalletType.bitcoin && bitcoin!.hasSelectedSilentPayments(wallet);
@computed
bool get isCupcake =>
bool get isBitcoinViewOnly =>
wallet.type == WalletType.bitcoin &&
(bitcoin!.getWalletKeys(wallet)["privateKey"] ?? "").isEmpty;
@@ -6,6 +6,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
@@ -24,12 +25,12 @@ class WalletHardwareRestoreViewModel = WalletHardwareRestoreViewModelBase
with _$WalletHardwareRestoreViewModel;
abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with Store {
final LedgerViewModel ledgerViewModel;
final HardwareWalletViewModel hardwareWalletVM;
int _nextIndex = 0;
WalletHardwareRestoreViewModelBase(
this.ledgerViewModel,
this.hardwareWalletVM,
AppStore appStore,
WalletCreationService walletCreationService,
Box<WalletInfo> walletInfoSource,
@@ -56,33 +57,16 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with
@action
Future<void> getNextAvailableAccounts(int limit) async {
try {
List<HardwareAccountData> accounts;
switch (type) {
case WalletType.bitcoin:
accounts = await bitcoin!
.getHardwareWalletBitcoinAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
break;
case WalletType.litecoin:
accounts = await bitcoin!
.getHardwareWalletLitecoinAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
break;
case WalletType.ethereum:
accounts = await ethereum!
.getHardwareWalletAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
break;
case WalletType.polygon:
accounts = await polygon!
.getHardwareWalletAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
break;
default:
return;
}
List<HardwareAccountData> accounts = await hardwareWalletVM
.getHardwareWalletService(type)
.getAvailableAccounts(index: _nextIndex, limit: limit);
availableAccounts.addAll(accounts);
_nextIndex += limit;
} catch (e) {
printV(e);
error = ledgerViewModel.interpretErrorCode(e.toString()) ?? S.current.ledger_connection_error;
error =
hardwareWalletVM.interpretErrorCode(e.toString()) ?? S.current.ledger_connection_error;
}
isLoadingMoreAccounts = false;
@@ -110,7 +94,7 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with
credentials = monero!.createMoneroRestoreWalletFromHardwareCredentials(
name: name,
ledgerConnection: ledgerViewModel.connection,
ledgerConnection: (hardwareWalletVM as LedgerViewModel).connection,
password: password,
height: _options['height'] as int? ?? 0,
);
@@ -118,7 +102,10 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with
throw Exception('Unexpected type: ${type.toString()}');
}
credentials.hardwareWalletType = HardwareWalletType.ledger;
if (hardwareWalletVM is LedgerViewModel)
credentials.hardwareWalletType = HardwareWalletType.ledger;
else
credentials.hardwareWalletType = HardwareWalletType.bitbox;
return credentials;
}
@@ -33,7 +33,7 @@ class WalletRestoreViewModel = WalletRestoreViewModelBase with _$WalletRestoreVi
abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
Box<WalletInfo> walletInfoSource, SeedSettingsViewModel seedSettingsViewModel,
{required WalletType type, this.restoredWallet})
{required WalletType type, this.restoredWallet, this.hardwareWalletType})
: isButtonEnabled = restoredWallet != null,
hasPassphrase = false,
mode = restoredWallet?.restoreMode ?? WalletRestoreMode.seed,
@@ -102,6 +102,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
].contains(type);
final RestoredWallet? restoredWallet;
final HardwareWalletType? hardwareWalletType;
@observable
WalletRestoreMode mode;
@@ -226,6 +227,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
name: name,
password: password,
xpub: viewKey!,
hardwareWalletType: hardwareWalletType,
);
case WalletType.monero: