cake_wallet/lib/view_model/hardware_wallet/ledger_view_model.dart
Konstantin Ullrich ae758756d8
Ledger monero fix (#1834)
* Fix sending for monero ledger

* Ignore no tx keys found error

* re-add Monero to Ledger enabled wallets

* Fix No Element Exception on requireHardwareWalletConnection check

* Fix Showing connection screen again

* Maybe fix Race condition

* fix namespace

* Maybe fix Race condition and add missing pop

* Minor fixes

* Minor fixes

* Fix minor localization

* Fix minor localization

* Add Text prompt if device is not showing after 10 seconds.

---------

Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
2024-12-14 01:32:36 +02:00

178 lines
5.5 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/main.dart';
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/utils/device_info.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/hardware/device_connection_type.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: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';
class LedgerViewModel = LedgerViewModelBase with _$LedgerViewModel;
abstract class LedgerViewModelBase with Store {
// late final Ledger ledger;
late final sdk.LedgerInterface ledgerPlusBLE;
late final sdk.LedgerInterface ledgerPlusUSB;
bool get _doesSupportHardwareWallets {
if (!DeviceInfo.instance.isMobile) {
return false;
}
if (isMoneroOnly) {
return DeviceConnectionType.supportedConnectionTypes(
WalletType.monero, Platform.isIOS)
.isNotEmpty;
}
return true;
}
LedgerViewModelBase() {
if (_doesSupportHardwareWallets) {
reaction((_) => bleIsEnabled, (_) {
if (bleIsEnabled) _initBLE();
});
updateBleState();
if (!Platform.isIOS) {
ledgerPlusUSB = sdk.LedgerInterface.usb();
}
}
}
@observable
bool bleIsEnabled = false;
bool _bleIsInitialized = false;
Future<void> _initBLE() async {
if (bleIsEnabled && !_bleIsInitialized) {
ledgerPlusBLE = sdk.LedgerInterface.ble(
onPermissionRequest: (_) async {
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.bluetoothAdvertise,
].request();
return statuses.values.where((status) => status.isDenied).isEmpty;
},
bleOptions:
sdk.BluetoothOptions(maxScanDuration: Duration(minutes: 5)));
_bleIsInitialized = true;
}
}
Future<void> updateBleState() async {
final bleState = await sdk.UniversalBle.getBluetoothAvailabilityState();
final newState = bleState == sdk.AvailabilityState.poweredOn;
if (newState != bleIsEnabled) bleIsEnabled = newState;
}
Stream<sdk.LedgerDevice> scanForBleDevices() => ledgerPlusBLE.scan();
Stream<sdk.LedgerDevice> scanForUsbDevices() => ledgerPlusUSB.scan();
Future<void> stopScanning() async {
await ledgerPlusBLE.stopScanning();
if (!Platform.isIOS) {
await ledgerPlusUSB.stopScanning();
}
}
Future<void> connectLedger(sdk.LedgerDevice device, WalletType type) async {
if (isConnected) {
try {
await _connectionChangeListener?.cancel();
_connectionChangeListener = null;
await _connection!.disconnect().catchError((_) {});
} catch (_) {}
}
final ledger = device.connectionType == sdk.ConnectionType.ble
? ledgerPlusBLE
: ledgerPlusUSB;
if (_connectionChangeListener == null) {
_connectionChangeListener = ledger.deviceStateChanges.listen((event) {
printV('Ledger Device State Changed: $event');
if (event == sdk.BleConnectionState.disconnected) {
_connection = null;
if (type == WalletType.monero) {
monero!.resetLedgerConnection();
Navigator.of( navigatorKey.currentContext!).pushNamed(
Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: WalletType.monero,
allowChangeWallet: true,
isReconnect: true,
onConnectDevice: (context, ledgerVM) async {
Navigator.of(context).pop();
},
),
);
}
}
});
}
_connection = await ledger.connect(device);
}
StreamSubscription<sdk.BleConnectionState>? _connectionChangeListener;
sdk.LedgerConnection? _connection;
bool get isConnected => _connection != null && !(_connection!.isDisconnected);
sdk.LedgerConnection get connection => _connection!;
void setLedger(WalletBase wallet) {
switch (wallet.type) {
case WalletType.monero:
return monero!.setLedgerConnection(wallet, connection);
case WalletType.bitcoin:
case WalletType.litecoin:
return bitcoin!.setLedgerConnection(wallet, connection);
case WalletType.ethereum:
return ethereum!.setLedgerConnection(wallet, connection);
case WalletType.polygon:
return polygon!.setLedgerConnection(wallet, connection);
default:
throw Exception('Unexpected wallet type: ${wallet.type}');
}
}
String? interpretErrorCode(String errorCode) {
switch (errorCode) {
case "6985":
return S.current.ledger_error_tx_rejected_by_user;
case "5515":
return S.current.ledger_error_device_locked;
case "6d02": // UNKNOWN_APDU
case "6511":
case "6e00":
return S.current.ledger_error_wrong_app;
default:
return null;
}
}
}