import "dart:async"; import "dart:io"; import "package:bitbox_flutter/bitbox_flutter.dart" as sdk; import "package:hash_wallet/bitcoin/bitcoin.dart"; import "package:hash_wallet/entities/hardware_wallet/hardware_wallet_device.dart"; import "package:hash_wallet/evm/evm.dart"; import "package:hash_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart"; import "package:hash_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_info.dart"; import "package:cw_core/wallet_type.dart"; import "package:mobx/mobx.dart"; part "bitbox_view_model.g.dart"; class BitboxViewModel = BitboxViewModelBase with _$BitboxViewModel; abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store { BitboxViewModelBase() { if (!Platform.isIOS && !isMoneroOnly) { bitboxManager = sdk.BitboxManager(); } } late final sdk.BitboxManager bitboxManager; BitboxHardwareWalletDevice? _connectedDevice; Timer? _isConnectedTimer; @override HardwareWalletType get hardwareWalletType => HardwareWalletType.bitbox; @override @observable bool isBleEnabled = false; @override @observable bool isConnecting = false; @override bool get hasBluetooth => Platform.isIOS && false; // TODO(Konsti): remove when we enable bluetooth @override Future updateBleState() async {} @override Stream scanForBleDevices() => throw UnimplementedError(); @override Future> getAllUsbDevices() => bitboxManager.devices .then((devices) => devices.map(BitboxHardwareWalletDevice.new).toList()); @override Future stopScanning() async {} @override @action Future 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"); _connectedDevice = device; _isConnectedTimer = Timer.periodic(const Duration(seconds: 1), (_) => _isStillConnected()); isConnecting = false; return true; } catch (e) { printV(e); } isConnecting = false; _connectedDevice = null; _isConnectedTimer?.cancel(); _isConnectedTimer = null; return false; } @override bool isConnected(WalletType type) => _connectedDevice != null; Future _isStillConnected() async { final devices = await getAllUsbDevices(); if (!devices.any((d) => d.name == _connectedDevice?.name)) { _connectedDevice = null; _isConnectedTimer?.cancel(); _isConnectedTimer = null; } } @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: case WalletType.polygon: return evm!.getBitboxHardwareWalletService(bitboxManager); default: throw UnimplementedError(); } } @override Future initWallet(WalletBase wallet) { switch (wallet.type) { case WalletType.bitcoin: case WalletType.litecoin: return bitcoin! .setHardwareWalletService(wallet, getHardwareWalletService(wallet.type)); case WalletType.ethereum: case WalletType.polygon: return evm!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type)); default: throw Exception("Unexpected wallet type: ${wallet.type} for bitbox"); } } }