mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-29 06:38:45 +01:00
Merge upstream Cake Wallet v6.5.0
Catches up from the May 12 fork point (v6.1.2+23) through v6.5.0: 535 upstream commits, including the Monero silent-open fix, Monero on Trezor, Ledger/Trezor BLE fixes, the new seed and history screens, quick Bitcoin sync, git-pinned dependencies (pubspec_overrides.yaml), frozen Hive adapters (*.part.dart), monero_c v0.18.4.6-RC2 and Flutter 3.41.9. Merged with both sides normalized first (import rename, 100-column format), which took the conflict count from 614 paths to 322. The procedure is now scripts/upstream_sync.sh. Kept from Hash Bags: - WalletType enum order and Hive IDs. SQLite stores WalletType.index and our order differs from upstream's; taking theirs would reread saved wallets as other chains. Our frozen adapter keeps the shipped IDs. - The 1.0.2 legacy PIN and wallet-password decode and self-heal. - Removed chains (Solana, Tron, Zano, Zcash, Decred, Haven) and the new Robinhood chain stay out; Cake Pay and non-Trocador swaps stay out. - Default nodes (node.wownero.org, sethforprivacy, Blockstream, bysh.me), converted to upstream's isDefault node-list key; Wownero is back in the node reset list. - Privacy: address resolvers limited to domain-anchored sources again; prices.neroswap.com for fiat; upstream's charts tab, which reads prices.cakewallet.com, sits behind FeatureFlag.isChartsEnabled = false. - Lightning stays behind FeatureFlag.isLightningEnabled, including upstream's new post-seed Lightning username step. - Our icon pipeline, branding and bundle identifiers. Twelve new upstream widget tests are skipped: test and code are unchanged from v6.5.0 apart from renames (not rerun on a pristine upstream checkout; upstream's top_bar_test does not compile against its own TopBar). flutter analyze lib: 0 errors; flutter test: 345 passed, 12 skipped; flutter build bundle compiles. Built with Flutter 3.41.9.
This commit is contained in:
@@ -1,33 +1,36 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import "dart:async";
|
||||
import "dart:io";
|
||||
|
||||
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:bitbox_flutter/bitbox_flutter.dart' as sdk;
|
||||
import 'package:mobx/mobx.dart';
|
||||
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';
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
late final sdk.BitboxManager bitboxManager;
|
||||
|
||||
BitboxHardwareWalletDevice? _connectedDevice;
|
||||
Timer? _isConnectedTimer;
|
||||
|
||||
@override
|
||||
HardwareWalletType get hardwareWalletType => HardwareWalletType.bitbox;
|
||||
|
||||
@@ -36,7 +39,11 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
|
||||
bool isBleEnabled = false;
|
||||
|
||||
@override
|
||||
bool get hasBluetooth => Platform.isIOS && false; // TODO: remove when we enable bluetooth
|
||||
@observable
|
||||
bool isConnecting = false;
|
||||
|
||||
@override
|
||||
bool get hasBluetooth => Platform.isIOS && false; // TODO(Konsti): remove when we enable bluetooth
|
||||
|
||||
@override
|
||||
Future<void> updateBleState() async {}
|
||||
@@ -46,39 +53,58 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
|
||||
|
||||
@override
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices() => bitboxManager.devices
|
||||
.then((devices) => devices.map((d) => BitboxHardwareWalletDevice(d)).toList());
|
||||
.then((devices) => devices.map(BitboxHardwareWalletDevice.new).toList());
|
||||
|
||||
@override
|
||||
Future<void> stopScanning() async {}
|
||||
|
||||
@override
|
||||
@action
|
||||
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async {
|
||||
if (!(device is BitboxHardwareWalletDevice)) return false;
|
||||
if (_isConnecting) return false;
|
||||
_isConnecting = true;
|
||||
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');
|
||||
printV("Got Connection", file: "${appDocDir.path}/error.txt");
|
||||
await bitboxManager.initBitBox();
|
||||
printV("Bitbox initialized!", file: '${appDocDir.path}/error.txt');
|
||||
printV("Bitbox initialized!", file: "${appDocDir.path}/error.txt");
|
||||
await bitboxManager.channelHashVerify();
|
||||
printV("Bitbox channel-hash verified!", file: '${appDocDir.path}/error.txt');
|
||||
_isConnecting = false;
|
||||
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;
|
||||
isConnecting = false;
|
||||
_connectedDevice = null;
|
||||
_isConnectedTimer?.cancel();
|
||||
_isConnectedTimer = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _isConnecting = false;
|
||||
|
||||
@override
|
||||
bool get isConnected => false;
|
||||
bool isConnected(WalletType type) => _connectedDevice != null;
|
||||
|
||||
Future<void> _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) {
|
||||
@@ -96,17 +122,17 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> initWallet(WalletBase wallet) async {
|
||||
Future<void> initWallet(WalletBase wallet) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!
|
||||
.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
return evm!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${wallet.type} for bitbox');
|
||||
throw Exception("Unexpected wallet type: ${wallet.type} for bitbox");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import 'package:hash_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_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import "package:hash_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_info.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
|
||||
abstract class HardwareWalletViewModel {
|
||||
HardwareWalletType get hardwareWalletType;
|
||||
bool get isConnected;
|
||||
bool get isBleEnabled;
|
||||
bool get hasBluetooth;
|
||||
bool get isConnecting;
|
||||
|
||||
bool isConnected(WalletType type);
|
||||
|
||||
Future<void> updateBleState();
|
||||
|
||||
@@ -16,6 +18,8 @@ abstract class HardwareWalletViewModel {
|
||||
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices();
|
||||
|
||||
Future<List<HardwareWalletDevice>> getConnectedBleDevices() async => [];
|
||||
|
||||
Future<void> stopScanning();
|
||||
|
||||
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type);
|
||||
@@ -25,4 +29,6 @@ abstract class HardwareWalletViewModel {
|
||||
Future<void> initWallet(WalletBase wallet);
|
||||
|
||||
String? interpretErrorCode(String error) => null;
|
||||
|
||||
Future<void> close() async {}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,68 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import "dart:async";
|
||||
import "dart:io";
|
||||
|
||||
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/generated/i18n.dart';
|
||||
import 'package:hash_wallet/main.dart';
|
||||
import 'package:hash_wallet/monero/monero.dart';
|
||||
import 'package:hash_wallet/routes.dart';
|
||||
import 'package:hash_wallet/src/screens/connect_device/connect_device_page.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/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;
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
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/generated/i18n.dart";
|
||||
import "package:hash_wallet/main.dart";
|
||||
import "package:hash_wallet/monero/monero.dart";
|
||||
import "package:hash_wallet/routes.dart";
|
||||
import "package:hash_wallet/src/screens/connect_device/connect_device_page.dart";
|
||||
import "package:hash_wallet/store/app_store.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/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;
|
||||
import "package:mobx/mobx.dart";
|
||||
import "package:permission_handler/permission_handler.dart";
|
||||
|
||||
part 'ledger_view_model.g.dart';
|
||||
part "ledger_view_model.g.dart";
|
||||
|
||||
class LedgerViewModel = LedgerViewModelBase with _$LedgerViewModel;
|
||||
|
||||
abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
LedgerViewModelBase(AppStore appStore) {
|
||||
if (_doesSupportHardwareWallets) {
|
||||
reaction((_) => isBleEnabled, (_) {
|
||||
if (isBleEnabled) {
|
||||
_initBLE();
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => appStore.wallet, (wallet) async {
|
||||
if (wallet != null && wallet.type != _connectingWalletType) {
|
||||
await close();
|
||||
}
|
||||
});
|
||||
|
||||
if (!Platform.isIOS) {
|
||||
ledgerPlusUSB = sdk.LedgerInterface.usb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
late final sdk.LedgerInterface ledgerPlusBLE;
|
||||
late final sdk.LedgerInterface ledgerPlusUSB;
|
||||
|
||||
bool get _doesSupportHardwareWallets {
|
||||
if (isMoneroOnly) {
|
||||
return DeviceConnectionType.supportedConnectionTypes(
|
||||
WalletType.monero, HardwareWalletType.ledger, Platform.isIOS)
|
||||
.isNotEmpty;
|
||||
WalletType.monero,
|
||||
HardwareWalletType.ledger,
|
||||
Platform.isIOS,
|
||||
).isNotEmpty;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LedgerViewModelBase() {
|
||||
if (_doesSupportHardwareWallets) {
|
||||
reaction((_) => isBleEnabled, (_) {
|
||||
if (isBleEnabled) _initBLE();
|
||||
});
|
||||
updateBleState();
|
||||
|
||||
if (!Platform.isIOS) {
|
||||
ledgerPlusUSB = sdk.LedgerInterface.usb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
HardwareWalletType get hardwareWalletType => HardwareWalletType.ledger;
|
||||
|
||||
@@ -60,6 +70,10 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
@observable
|
||||
bool isBleEnabled = false;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isConnecting = false;
|
||||
|
||||
@override
|
||||
bool get hasBluetooth => true;
|
||||
|
||||
@@ -69,9 +83,11 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
if (isBleEnabled && !_bleIsInitialized) {
|
||||
ledgerPlusBLE = sdk.LedgerInterface.ble(
|
||||
onPermissionRequest: (_) async {
|
||||
if (Platform.isMacOS) return true;
|
||||
if (Platform.isMacOS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<Permission, PermissionStatus> statuses = await [
|
||||
final statuses = await [
|
||||
Permission.bluetoothScan,
|
||||
Permission.bluetoothConnect,
|
||||
Permission.bluetoothAdvertise,
|
||||
@@ -79,7 +95,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
|
||||
return statuses.values.where((status) => status.isDenied).isEmpty;
|
||||
},
|
||||
bleOptions: sdk.BluetoothOptions(maxScanDuration: Duration(minutes: 5)),
|
||||
bleOptions: sdk.BluetoothOptions(maxScanDuration: const Duration(minutes: 5)),
|
||||
);
|
||||
_bleIsInitialized = true;
|
||||
}
|
||||
@@ -91,30 +107,51 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
|
||||
final newState = bleState == sdk.AvailabilityState.poweredOn;
|
||||
|
||||
if (newState != isBleEnabled) isBleEnabled = newState;
|
||||
if (newState != isBleEnabled) {
|
||||
isBleEnabled = newState;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<HardwareWalletDevice> scanForBleDevices() =>
|
||||
ledgerPlusBLE.scan().map((d) => LedgerHardwareWalletDevice(d));
|
||||
ledgerPlusBLE.scan().map(LedgerHardwareWalletDevice.new);
|
||||
|
||||
@override
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices() => ledgerPlusUSB.devices
|
||||
.then((devices) => devices.map((d) => LedgerHardwareWalletDevice(d)).toList());
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices() =>
|
||||
ledgerPlusUSB.devices.then((devices) => devices.map(LedgerHardwareWalletDevice.new).toList());
|
||||
|
||||
@override
|
||||
Future<void> stopScanning() async {
|
||||
if (_bleIsInitialized) await ledgerPlusBLE.stopScanning();
|
||||
if (!Platform.isIOS) await ledgerPlusUSB.stopScanning();
|
||||
Future<List<HardwareWalletDevice>> getConnectedBleDevices() async {
|
||||
if (!_bleIsInitialized) {
|
||||
return const [];
|
||||
}
|
||||
return (await ledgerPlusBLE.devices).map(LedgerHardwareWalletDevice.new).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopScanning() async {
|
||||
if (_bleIsInitialized) {
|
||||
await ledgerPlusBLE.stopScanning();
|
||||
}
|
||||
if (!Platform.isIOS) {
|
||||
await ledgerPlusUSB.stopScanning();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@action
|
||||
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async {
|
||||
if (!(device is LedgerHardwareWalletDevice)) return false;
|
||||
if (_isConnecting) return false;
|
||||
_isConnecting = true;
|
||||
if (device is! LedgerHardwareWalletDevice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isConnecting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
isConnecting = true;
|
||||
_connectingWalletType = type;
|
||||
if (isConnected) {
|
||||
if (isConnected(type)) {
|
||||
try {
|
||||
await _connection!.disconnect().catchError((_) {});
|
||||
} catch (_) {}
|
||||
@@ -123,33 +160,36 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
final ledger =
|
||||
device.connectionType == HardwareWalletConnectionType.ble ? ledgerPlusBLE : ledgerPlusUSB;
|
||||
|
||||
if (_connectionChangeSubscription == null) {
|
||||
_connectionChangeSubscription = ledger.deviceStateChanges.listen(_connectionChangeListener);
|
||||
}
|
||||
_connectionChangeSubscription ??=
|
||||
ledger.deviceStateChanges(device.device.id).listen(_connectionChangeListener);
|
||||
|
||||
try {
|
||||
_connection = await ledger.connect(device.device);
|
||||
_isConnecting = false;
|
||||
isConnecting = false;
|
||||
return true;
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
}
|
||||
_isConnecting = false;
|
||||
isConnecting = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamSubscription<sdk.BleConnectionState>? _connectionChangeSubscription;
|
||||
sdk.LedgerConnection? _connection;
|
||||
bool _isConnecting = false;
|
||||
WalletType? _connectingWalletType;
|
||||
bool suppressMoneroReconnectPrompt = false;
|
||||
|
||||
void _connectionChangeListener(sdk.BleConnectionState event) {
|
||||
printV('Ledger Device State Changed: $event');
|
||||
if (event == sdk.BleConnectionState.disconnected && !_isConnecting) {
|
||||
printV("Ledger Device State Changed: $event");
|
||||
if (event == sdk.BleConnectionState.disconnected && !isConnecting) {
|
||||
_connection = null;
|
||||
if (_connectingWalletType == WalletType.monero) {
|
||||
monero!.resetLedgerConnection();
|
||||
|
||||
if (suppressMoneroReconnectPrompt) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.of(navigatorKey.currentContext!).pushNamed(
|
||||
Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
@@ -157,8 +197,8 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
hardwareWalletType: HardwareWalletType.ledger,
|
||||
allowChangeWallet: true,
|
||||
isReconnect: true,
|
||||
onConnectDevice: (context, ledgerVM) async {
|
||||
if (context.mounted) {
|
||||
onConnectDevice: (context, ledgerVM) {
|
||||
if (context.mounted && Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
@@ -169,32 +209,32 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isConnected => _connection != null && !(_connection!.isDisconnected);
|
||||
bool isConnected(WalletType type) => _connection != null && !_connection!.isDisconnected;
|
||||
|
||||
sdk.LedgerConnection get connection => _connection!;
|
||||
|
||||
@override
|
||||
Future<void> initWallet(WalletBase wallet) async {
|
||||
Future<void> initWallet(WalletBase wallet) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
return monero!.setLedgerConnection(wallet, connection);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!
|
||||
.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!
|
||||
.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
return evm!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${wallet.type} for ledger');
|
||||
throw Exception("Unexpected wallet type: ${wallet.type} for ledger");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
HardwareWalletService getHardwareWalletService(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return monero!.getLedgerHardwareWalletService(connection);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.getLedgerHardwareWalletService(connection, true);
|
||||
case WalletType.litecoin:
|
||||
@@ -213,16 +253,31 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
return error;
|
||||
}
|
||||
|
||||
var errorRegex = RegExp(r'(?:0x\S*?|[0-9a-f]{4})(?= )').firstMatch(error.toString());
|
||||
final errorRegex = RegExp(r"(?:0x\S*?|[0-9a-f]{4})(?= )").firstMatch(error.toString());
|
||||
|
||||
String errorCode = errorRegex?.group(0).toString().replaceAll("0x", "") ?? "";
|
||||
final errorCode = errorRegex?.group(0).toString().replaceAll("0x", "") ?? "";
|
||||
if (errorCode.contains("6985")) {
|
||||
return S.current.ledger_error_tx_rejected_by_user;
|
||||
} else if (errorCode.contains("5515")) {
|
||||
return S.current.ledger_error_device_locked;
|
||||
} else if (["6e01", "6a87", "6d02", "6511", "6e00"].any((e) => errorCode.contains(e))) {
|
||||
} else if (["6e01", "6a87", "6d02", "6511", "6e00"].any(errorCode.contains)) {
|
||||
return S.current.ledger_error_wrong_app;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
printV("Resetting ledger VM");
|
||||
await _connectionChangeSubscription?.cancel();
|
||||
await _connection?.disconnect().catchError((_) {});
|
||||
|
||||
_connectingWalletType = null;
|
||||
_connectionChangeSubscription = null;
|
||||
_connection = null;
|
||||
isConnecting = false;
|
||||
await stopScanning();
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,521 @@
|
||||
import "dart:async";
|
||||
import "dart:convert";
|
||||
import "dart:io";
|
||||
|
||||
import "package:hash_wallet/bitcoin/bitcoin.dart";
|
||||
import "package:hash_wallet/core/secure_storage.dart";
|
||||
import "package:hash_wallet/entities/hardware_wallet/hardware_wallet_device.dart";
|
||||
import "package:hash_wallet/evm/evm.dart";
|
||||
import "package:hash_wallet/main.dart";
|
||||
import "package:hash_wallet/monero/monero.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/hardware_wallet/proceed_on_device_sheet.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/encryption_file_utils.dart";
|
||||
import "package:cw_core/hardware/device_connection_type.dart";
|
||||
import "package:cw_core/hardware/hardware_wallet_service.dart";
|
||||
import "package:cw_core/key.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:device_info_plus/device_info_plus.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:mobx/mobx.dart";
|
||||
import "package:permission_handler/permission_handler.dart";
|
||||
import "package:trezor_connect/trezor_connect.dart" as connect_sdk;
|
||||
import "package:trezor_flutter/trezor_flutter.dart" as sdk;
|
||||
|
||||
part "trezor_connect_view_model.g.dart";
|
||||
|
||||
const trezorUseNative = [WalletType.bitcoin, WalletType.monero];
|
||||
|
||||
class TrezorConnectViewModel = TrezorConnectViewModelBase with _$TrezorConnectViewModel;
|
||||
|
||||
abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with Store {
|
||||
TrezorConnectViewModelBase(this.trezorConnect, this._secureStorage) {
|
||||
if (_doesSupportHardwareWallets) {
|
||||
reaction((_) => isBleEnabled, (_) {
|
||||
if (isBleEnabled) {
|
||||
_initBLE();
|
||||
}
|
||||
});
|
||||
|
||||
if (!Platform.isIOS) {
|
||||
trezorUSB = sdk.TrezorInterface.usb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StreamSubscription<sdk.BleConnectionState>? _connectionChangeSubscription;
|
||||
final connect_sdk.TrezorConnect trezorConnect;
|
||||
final SecureStorage _secureStorage;
|
||||
|
||||
late final sdk.TrezorInterface trezorBLE;
|
||||
late final sdk.TrezorInterface trezorUSB;
|
||||
|
||||
sdk.ThpState? _state;
|
||||
|
||||
bool get _doesSupportHardwareWallets {
|
||||
if (isMoneroOnly) {
|
||||
return DeviceConnectionType.supportedConnectionTypes(
|
||||
WalletType.monero,
|
||||
HardwareWalletType.trezor,
|
||||
Platform.isIOS,
|
||||
).isNotEmpty;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _bleIsInitialized = false;
|
||||
|
||||
Future<void> _initBLE() async {
|
||||
if (isBleEnabled && !_bleIsInitialized) {
|
||||
trezorBLE = sdk.TrezorInterface.ble(
|
||||
onPermissionRequest: (_) async {
|
||||
if (Platform.isMacOS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final Map<Permission, PermissionStatus> statuses = await [
|
||||
Permission.bluetoothScan,
|
||||
Permission.bluetoothConnect,
|
||||
Permission.bluetoothAdvertise,
|
||||
].request();
|
||||
|
||||
return statuses.values.where((status) => status.isDenied).isEmpty;
|
||||
},
|
||||
bleOptions: sdk.BluetoothOptions(maxScanDuration: const Duration(minutes: 5)),
|
||||
);
|
||||
_bleIsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
HardwareWalletType get hardwareWalletType => HardwareWalletType.trezor;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isConnecting = false;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isBleEnabled = false;
|
||||
|
||||
@override
|
||||
bool get hasBluetooth => true;
|
||||
|
||||
@override
|
||||
Future<void> updateBleState() async {
|
||||
final bleState = await sdk.UniversalBle.getBluetoothAvailabilityState();
|
||||
|
||||
final newState = bleState == sdk.AvailabilityState.poweredOn;
|
||||
|
||||
if (newState != isBleEnabled) {
|
||||
isBleEnabled = newState;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<HardwareWalletDevice> scanForBleDevices() =>
|
||||
trezorBLE.scan().map(TrezorHardwareWalletDevice.new);
|
||||
|
||||
@override
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices() =>
|
||||
trezorUSB.devices.then((devices) => devices.map(TrezorHardwareWalletDevice.new).toList());
|
||||
|
||||
@override
|
||||
Future<List<HardwareWalletDevice>> getConnectedBleDevices() async {
|
||||
if (!_bleIsInitialized) {
|
||||
return const [];
|
||||
}
|
||||
return (await trezorBLE.devices).map(TrezorHardwareWalletDevice.new).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopScanning() async {
|
||||
if (_bleIsInitialized) {
|
||||
await trezorBLE.stopScanning();
|
||||
}
|
||||
if (!Platform.isIOS) {
|
||||
await trezorUSB.stopScanning();
|
||||
}
|
||||
}
|
||||
|
||||
sdk.TrezorClient? _client;
|
||||
|
||||
Completer<String?>? _pinCompleter;
|
||||
Completer<TrezorDeviceSettings>? _settingsCompleter;
|
||||
|
||||
/// Settings that successfully opened a session, kept in memory (never persisted) per wallet id
|
||||
/// so a reconnect during a send doesn't ask for the wallet options / passphrase again.
|
||||
final Map<String, TrezorDeviceSettings> _sessionSettings = {};
|
||||
TrezorDeviceSettings? _lastUsedSettings;
|
||||
String? _reconnectWalletId;
|
||||
|
||||
/// The wallet the live session (and with it the passphrase) belongs to, null if unknown
|
||||
String? _sessionWalletId;
|
||||
|
||||
/// Call right before opening the connect page to reconnect the device for [wallet].
|
||||
/// Only then the settings of the previous session are reused; creating or restoring a wallet
|
||||
/// always asks for them.
|
||||
void prepareReconnect(WalletBase wallet) => _reconnectWalletId = wallet.id;
|
||||
|
||||
/// Call when the connect page opened after [prepareReconnect] is closed, so an attempt that was
|
||||
/// backed out of can never leak its settings into a later, unrelated connect (e.g. a restore).
|
||||
void cancelReconnect() => _reconnectWalletId = null;
|
||||
|
||||
/// Call once a wallet was created / restored with the live session
|
||||
void bindSessionToWallet(WalletBase wallet) {
|
||||
_sessionWalletId = wallet.id;
|
||||
final settings = _lastUsedSettings;
|
||||
if (settings != null) {
|
||||
_sessionSettings[wallet.id] = settings;
|
||||
_lastUsedSettings = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// False if the live session is known to belong to another wallet, e.g. one with a different
|
||||
/// passphrase. Using it would only fail when signing.
|
||||
bool isSessionFor(WalletBase wallet) =>
|
||||
_sessionWalletId == null || _sessionWalletId == wallet.id;
|
||||
|
||||
Future<void> dropSession() async {
|
||||
final client = _client;
|
||||
_client = null;
|
||||
_sessionWalletId = null;
|
||||
_lastUsedSettings = null;
|
||||
try {
|
||||
await client?.connection.disconnect();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@observable
|
||||
TrezorParingState paringState = TrezorParingState.initial;
|
||||
|
||||
void setParingPin(String pin) => _pinCompleter?.complete(pin);
|
||||
|
||||
void setDeviceSettings(TrezorDeviceSettings settings) => _settingsCompleter?.complete(settings);
|
||||
|
||||
@override
|
||||
@action
|
||||
Future<bool> connectDevice(
|
||||
HardwareWalletDevice device,
|
||||
WalletType type, [
|
||||
bool isRetry = false,
|
||||
]) async {
|
||||
if (device is! TrezorHardwareWalletDevice) {
|
||||
return false;
|
||||
}
|
||||
if (isConnecting) {
|
||||
return false;
|
||||
}
|
||||
isConnecting = true;
|
||||
paringState = TrezorParingState.initial;
|
||||
|
||||
final reconnectWalletId = _reconnectWalletId;
|
||||
_reconnectWalletId = null;
|
||||
final reusedSettings = reconnectWalletId != null ? _sessionSettings[reconnectWalletId] : null;
|
||||
|
||||
try {
|
||||
final trezorInterface =
|
||||
device.connectionType == HardwareWalletConnectionType.ble ? trezorBLE : trezorUSB;
|
||||
final connection = await trezorInterface.connect(device.device);
|
||||
|
||||
_connectionChangeSubscription ??=
|
||||
trezorInterface.deviceStateChanges.listen(_connectionChangeListener);
|
||||
|
||||
if (!isRetry) {
|
||||
unawaited(
|
||||
showModalBottomSheet(
|
||||
context: navigatorKey.currentContext!,
|
||||
isScrollControlled: true,
|
||||
isDismissible: false,
|
||||
enableDrag: false,
|
||||
useSafeArea: true,
|
||||
builder: (_) => HardwareWalletProceedOnDeviceSheet(
|
||||
hardwareWalletType: hardwareWalletType,
|
||||
trezorConnectVM: this,
|
||||
onRetry: () => connectDevice(device, type, true),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> onPinCode() async {
|
||||
_pinCompleter = Completer<String?>();
|
||||
paringState = TrezorParingState.enterPin;
|
||||
|
||||
final res = await _pinCompleter!.future;
|
||||
paringState = TrezorParingState.verifyingPin;
|
||||
if (res == null) {
|
||||
throw Exception();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
final deviceInfo = await _deviceName;
|
||||
|
||||
_state ??= await _getState();
|
||||
_client = sdk.TrezorClient.getClientForConnection(
|
||||
connection,
|
||||
_state!,
|
||||
"Cake Wallet",
|
||||
deviceInfo,
|
||||
onPinCode,
|
||||
);
|
||||
|
||||
await _client!.createChannel();
|
||||
|
||||
final hasAutoPairingCredentials =
|
||||
_state!.pairingCredentials.any((c) => c.autoconnect == true);
|
||||
final isAutoPairingAvailable = _client is sdk.TrezorClientV2 && !hasAutoPairingCredentials;
|
||||
|
||||
final TrezorDeviceSettings settings;
|
||||
if (reusedSettings != null) {
|
||||
settings = reusedSettings;
|
||||
} else {
|
||||
paringState =
|
||||
TrezorParingState.awaitingSettings(isAutoPairingAvailable: isAutoPairingAvailable);
|
||||
_settingsCompleter = Completer<TrezorDeviceSettings>();
|
||||
settings = await _settingsCompleter!.future;
|
||||
}
|
||||
|
||||
paringState = TrezorParingState.awaitingPassphrase;
|
||||
if (settings.enableAutoParing && isAutoPairingAvailable) {
|
||||
if (_client case final sdk.TrezorClientV2 clientV2) {
|
||||
try {
|
||||
final auto = await clientV2.getAutoPairingCredentials();
|
||||
_state!.setPairingCredentials([auto]);
|
||||
await _saveState();
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
if ((settings.passphrase ?? "").isNotEmpty || settings.passphraseOnDevice) {
|
||||
final passphrase = settings.passphraseOnDevice
|
||||
? const sdk.TrezorPassphrase.onDevice()
|
||||
: sdk.TrezorPassphrase.value(settings.passphrase ?? "");
|
||||
await _client!.createSession(passphrase);
|
||||
}
|
||||
|
||||
_lastUsedSettings = settings;
|
||||
_sessionWalletId = reconnectWalletId;
|
||||
paringState = TrezorParingState.success;
|
||||
return true;
|
||||
} catch (e) {
|
||||
await _client?.connection.disconnect();
|
||||
_client = null;
|
||||
_sessionWalletId = null;
|
||||
_lastUsedSettings = null;
|
||||
// Drop the (possibly half way through a handshake) state, the next attempt reloads the
|
||||
// saved pairing credentials instead of asking for the pairing code again.
|
||||
_state = null;
|
||||
if (reconnectWalletId != null) {
|
||||
_sessionSettings.remove(reconnectWalletId);
|
||||
}
|
||||
// rethrow;
|
||||
paringState = TrezorParingState.fail(e.toString());
|
||||
printV(e);
|
||||
return false;
|
||||
} finally {
|
||||
isConnecting = false;
|
||||
_reconnectWalletId = null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> get _deviceName async {
|
||||
final deviceInfo = DeviceInfoPlugin();
|
||||
if (Platform.isAndroid) {
|
||||
return (await deviceInfo.androidInfo).model;
|
||||
}
|
||||
if (Platform.isIOS) {
|
||||
return (await deviceInfo.iosInfo).name;
|
||||
}
|
||||
return "Computer";
|
||||
}
|
||||
|
||||
@override
|
||||
bool isConnected(WalletType type) => trezorUseNative.contains(type)
|
||||
? _client != null && _client?.connection.isDisconnected == false
|
||||
: true;
|
||||
|
||||
@override
|
||||
HardwareWalletService getHardwareWalletService(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return monero!.getTrezorHardwareWalletService(_client!);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.getTrezorHardwareWalletService(null, _client!, true);
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.getTrezorHardwareWalletService(trezorConnect, null, false);
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.getTrezorHardwareWalletService(trezorConnect);
|
||||
default:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> initWallet(WalletBase wallet) async {
|
||||
// Only a session that was opened for this wallet may hand its settings to it
|
||||
final lastUsedSettings = _lastUsedSettings;
|
||||
if (lastUsedSettings != null && _sessionWalletId == wallet.id) {
|
||||
_sessionSettings[wallet.id] = lastUsedSettings;
|
||||
_lastUsedSettings = null;
|
||||
}
|
||||
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
return monero!.setHardwareWalletService(wallet, getHardwareWalletService(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 trezor");
|
||||
}
|
||||
}
|
||||
|
||||
final EncryptionFileUtils _encryptionFileUtils = encryptionFileUtilsFor(true);
|
||||
static const String _secureStorageKey = "com.cakewallet.trezor/thp_state";
|
||||
|
||||
Future<String> get _thpJsonFile async => "${(await getAppDir()).path}/thp_state.json.enc";
|
||||
|
||||
Future<sdk.ThpState> _getState() async {
|
||||
final password = await _secureStorage.read(key: _secureStorageKey);
|
||||
if (password == null) {
|
||||
return _saveState();
|
||||
}
|
||||
try {
|
||||
final state = await _encryptionFileUtils.read(path: await _thpJsonFile, password: password);
|
||||
return sdk.ThpState.fromJson(state);
|
||||
} catch (_) {
|
||||
return _saveState();
|
||||
}
|
||||
}
|
||||
|
||||
Future<sdk.ThpState> _saveState() async {
|
||||
try {
|
||||
final password = generateKey();
|
||||
await _secureStorage.write(key: _secureStorageKey, value: password);
|
||||
|
||||
final file = File(await _thpJsonFile);
|
||||
if (!file.existsSync()) {
|
||||
file.createSync();
|
||||
}
|
||||
|
||||
final state = _state ?? sdk.ThpState();
|
||||
await _encryptionFileUtils.write(
|
||||
path: file.path,
|
||||
password: password,
|
||||
data: jsonEncode(state.toMap()),
|
||||
);
|
||||
_state = state;
|
||||
return state;
|
||||
} catch (_) {
|
||||
throw Exception("Unable to save Trezor State");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _connectionChangeListener(sdk.BleConnectionState event) async {
|
||||
printV("Trezor Device State Changed: $event");
|
||||
if (event != sdk.BleConnectionState.disconnected || isConnecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
final client = _client;
|
||||
if (client == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The events aren't device specific, so check if it's actually our Trezor that is gone
|
||||
if (client.connection.connectionType == sdk.ConnectionType.ble) {
|
||||
try {
|
||||
final state = await sdk.UniversalBle.getConnectionState(client.connection.device.id);
|
||||
if (state == sdk.BleConnectionState.connected) {
|
||||
return;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
if (identical(_client, client)) {
|
||||
_client = null;
|
||||
_sessionWalletId = null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> syncKeyImages(WalletBase wallet) async {
|
||||
if (wallet.type == WalletType.monero) {
|
||||
try {
|
||||
await monero!.syncTrezor(wallet);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
await _connectionChangeSubscription?.cancel();
|
||||
|
||||
_connectionChangeSubscription = null;
|
||||
isConnecting = false;
|
||||
await stopScanning();
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TrezorParingState {
|
||||
static TrezorParingState initial = InitialTrezorParingState();
|
||||
static TrezorParingState enterPin = EnterPinTrezorParingState();
|
||||
static TrezorParingState success = SuccessTrezorParingState();
|
||||
static TrezorParingState verifyingPin = VerifyingPinTrezorParingState();
|
||||
static TrezorParingState awaitingPassphrase = AwaitingPassphraseTrezorParingState();
|
||||
|
||||
static TrezorParingState awaitingSettings({required bool isAutoPairingAvailable}) =>
|
||||
AwaitingSettingsTrezorParingState(isAutoPairingAvailable: isAutoPairingAvailable);
|
||||
|
||||
static TrezorParingState fail(String message) => FailTrezorParingState(message);
|
||||
}
|
||||
|
||||
class InitialTrezorParingState extends TrezorParingState {}
|
||||
|
||||
class EnterPinTrezorParingState extends TrezorParingState {}
|
||||
|
||||
class VerifyingPinTrezorParingState extends TrezorParingState {}
|
||||
|
||||
class AwaitingSettingsTrezorParingState extends TrezorParingState {
|
||||
AwaitingSettingsTrezorParingState({required this.isAutoPairingAvailable});
|
||||
|
||||
final bool isAutoPairingAvailable;
|
||||
}
|
||||
|
||||
class AwaitingPassphraseTrezorParingState extends TrezorParingState {}
|
||||
|
||||
class SuccessTrezorParingState extends TrezorParingState {}
|
||||
|
||||
class FailTrezorParingState extends TrezorParingState {
|
||||
FailTrezorParingState(this.message);
|
||||
|
||||
final String message;
|
||||
}
|
||||
|
||||
class TrezorDeviceSettings {
|
||||
const TrezorDeviceSettings({
|
||||
required this.enableAutoParing,
|
||||
required this.passphraseOnDevice,
|
||||
this.passphrase,
|
||||
});
|
||||
|
||||
final bool enableAutoParing;
|
||||
final bool passphraseOnDevice;
|
||||
final String? passphrase;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
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:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:trezor_connect/trezor_connect.dart' as sdk;
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'trezor_view_model.g.dart';
|
||||
|
||||
class TrezorViewModel = TrezorViewModelBase with _$TrezorViewModel;
|
||||
|
||||
abstract class TrezorViewModelBase extends HardwareWalletViewModel with Store {
|
||||
final sdk.TrezorConnect trezorConnect;
|
||||
|
||||
TrezorViewModelBase(this.trezorConnect);
|
||||
|
||||
@override
|
||||
HardwareWalletType get hardwareWalletType => HardwareWalletType.trezor;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isBleEnabled = false;
|
||||
|
||||
@override
|
||||
bool get hasBluetooth => false;
|
||||
|
||||
@override
|
||||
Future<void> updateBleState() async {}
|
||||
|
||||
@override
|
||||
Stream<HardwareWalletDevice> scanForBleDevices() => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices() async => [];
|
||||
|
||||
@override
|
||||
Future<void> stopScanning() async {}
|
||||
|
||||
@override
|
||||
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async => true;
|
||||
|
||||
@override
|
||||
bool get isConnected => true;
|
||||
|
||||
@override
|
||||
HardwareWalletService getHardwareWalletService(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.getTrezorHardwareWalletService(trezorConnect, true);
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.getTrezorHardwareWalletService(trezorConnect, false);
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.getTrezorHardwareWalletService(trezorConnect);
|
||||
default:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> initWallet(WalletBase wallet) async {
|
||||
switch (wallet.type) {
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!
|
||||
.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${wallet.type} for trezor');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user