diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml
index 36fb28a25..99a489bb1 100644
--- a/.github/workflows/pr_test_build_linux.yml
+++ b/.github/workflows/pr_test_build_linux.yml
@@ -118,7 +118,7 @@ jobs:
pushd scripts/monero_c/release
mkdir -p $(git describe --tags)
pushd $(git describe --tags)
- wget https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
+ wget https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC2/release-bundle.zip
unzip release-bundle.zip
rm release-bundle.zip
popd
diff --git a/.gitignore b/.gitignore
index 9fce14fcf..268167bc4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -225,3 +225,5 @@ scripts/zcash_lib
scripts/android/yttrium
scripts/bitbox_flutter
scripts/reown_flutter
+
+*.salive
\ No newline at end of file
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 6c1540222..26f5544c9 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -60,7 +60,7 @@ android {
defaultConfig {
applicationId appProperties['id']
- minSdkVersion 24
+ minSdkVersion 26
targetSdkVersion 36
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml
index a7364d670..0f15c75bc 100644
--- a/android/app/src/main/AndroidManifestBase.xml
+++ b/android/app/src/main/AndroidManifestBase.xml
@@ -128,6 +128,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/images/hardware_wallet/device_ledger_nano_gen_5.svg b/assets/images/hardware_wallet/device_ledger_nano_gen_5.svg
deleted file mode 100644
index 9d2fc8bbd..000000000
--- a/assets/images/hardware_wallet/device_ledger_nano_gen_5.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/assets/images/hardware_wallet/device_trezor_safe_5.svg b/assets/images/hardware_wallet/device_trezor_safe_5.svg
deleted file mode 100644
index 14c0329be..000000000
--- a/assets/images/hardware_wallet/device_trezor_safe_5.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/assets/images/hardware_wallet/ledger_flex.png b/assets/images/hardware_wallet/ledger_flex.png
deleted file mode 100644
index fa39f241f..000000000
Binary files a/assets/images/hardware_wallet/ledger_flex.png and /dev/null differ
diff --git a/assets/images/hardware_wallet/ledger_nano_s.png b/assets/images/hardware_wallet/ledger_nano_s.png
deleted file mode 100644
index 02777aeb6..000000000
Binary files a/assets/images/hardware_wallet/ledger_nano_s.png and /dev/null differ
diff --git a/assets/images/hardware_wallet/ledger_stax.png b/assets/images/hardware_wallet/ledger_stax.png
deleted file mode 100644
index 06c9c848e..000000000
Binary files a/assets/images/hardware_wallet/ledger_stax.png and /dev/null differ
diff --git a/cw_core/lib/hardware/device_connection_type.dart b/cw_core/lib/hardware/device_connection_type.dart
index 39e0400c9..3b1dd9349 100644
--- a/cw_core/lib/hardware/device_connection_type.dart
+++ b/cw_core/lib/hardware/device_connection_type.dart
@@ -29,7 +29,7 @@ enum DeviceConnectionType {
break;
case HardwareWalletType.trezor:
isSupported = [
- // WalletType.monero,
+ WalletType.monero,
WalletType.bitcoin,
WalletType.litecoin,
WalletType.ethereum,
diff --git a/cw_core/lib/hardware/hardware_wallet_service.dart b/cw_core/lib/hardware/hardware_wallet_service.dart
index 19125ba84..94cbdc443 100644
--- a/cw_core/lib/hardware/hardware_wallet_service.dart
+++ b/cw_core/lib/hardware/hardware_wallet_service.dart
@@ -4,7 +4,8 @@ import 'dart:typed_data';
import 'package:cw_core/hardware/hardware_account_data.dart';
abstract class HardwareWalletService {
- Future> getAvailableAccounts({int index = 0, int limit = 5});
+ Future> getAvailableAccounts({int index = 0, int limit = 5}) =>
+ throw UnimplementedError();
Future signTransaction({required String transaction}) => throw UnimplementedError();
diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart
index 1ce397267..5877ad2da 100644
--- a/cw_core/lib/wallet_info.dart
+++ b/cw_core/lib/wallet_info.dart
@@ -44,7 +44,7 @@ enum HardwareWalletType {
coldcard,
seedsigner,
keystone,
- trezor,
+ trezor;
}
enum WalletInfoAddressType {
diff --git a/cw_monero/lib/ledger.dart b/cw_monero/lib/ledger.dart
index 7707acd48..7c1742ada 100644
--- a/cw_monero/lib/ledger.dart
+++ b/cw_monero/lib/ledger.dart
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:ffi';
import 'dart:typed_data';
+import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
@@ -118,3 +119,10 @@ void _logLedgerCommand(Uint8List command, [bool isResponse = true]) {
"> ${_ledgerMoneroCommands[command[1]]} ${toHexString(command.sublist(2))}");
}
}
+
+class MoneroLedgerService extends HardwareWalletService {
+ final LedgerConnection connection;
+
+ MoneroLedgerService(this.connection);
+}
+
diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart
index e315f24fa..30c550721 100644
--- a/cw_monero/lib/monero_wallet.dart
+++ b/cw_monero/lib/monero_wallet.dart
@@ -37,6 +37,7 @@ import 'package:cw_monero/monero_unspent.dart';
import 'package:cw_monero/monero_wallet_addresses.dart';
import 'package:cw_monero/monero_wallet_service.dart';
import 'package:cw_monero/pending_monero_transaction.dart';
+import 'package:cw_monero/trezor.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
@@ -385,8 +386,42 @@ abstract class MoneroWalletBase extends WalletBase syncTrezor() async {
+ if (trezorService == null) throw Exception("Trezor not connected");
+
+ final ptr = Pointer.fromAddress(currentWallet!.ffiAddress());
+ final tdis = monero.Wallet_exportTrezorTdis(ptr);
+
+ final response = await Trezor(trezorService!).keyImageSync(tdis);
+
+ final success = monero.Wallet_importTrezorEncryptedKeyImagesJson(ptr, response);
+
+ if (!success) throw Exception(monero.Wallet_errorString(ptr));
+ }
+
+ Future signTrezorTransaction(String json) async {
+ if (trezorService == null) throw Exception("Trezor not connected");
+ return Trezor(trezorService!).signTransaction(json);
+ }
+
@override
Future createTransaction(Object credentials) async {
+ if (hardwareWalletType == HardwareWalletType.trezor) {
+ for (int i = 0; i < 2; i++) {
+ try {
+ return await _createTransaction(credentials);
+ } catch (e) {
+ printV(e);
+ }
+ await save();
+ await Future.delayed(Duration(seconds: i));
+ }
+ }
+ return await _createTransaction(credentials);
+ }
+
+ Future _createTransaction(Object credentials) async {
final _credentials = credentials as MoneroTransactionCreationCredentials;
final inputs = [];
final outputs = _credentials.outputs;
diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart
index 2a153c5e1..1b0e9e347 100644
--- a/cw_monero/lib/monero_wallet_service.dart
+++ b/cw_monero/lib/monero_wallet_service.dart
@@ -3,8 +3,8 @@ import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
-import 'package:collection/collection.dart';
import 'package:cw_core/get_height_by_date.dart';
+import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/unspent_coins_info.dart';
@@ -20,8 +20,8 @@ import 'package:cw_monero/api/wallet_manager.dart';
import 'package:cw_monero/bip39_seed.dart';
import 'package:cw_monero/ledger.dart';
import 'package:cw_monero/monero_wallet.dart';
+import 'package:cw_monero/trezor.dart';
import 'package:hive/hive.dart';
-import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:monero/monero.dart' as monero;
import 'package:polyseed/polyseed.dart';
@@ -46,11 +46,11 @@ class MoneroNewWalletCredentials extends WalletCredentials {
class MoneroRestoreWalletFromHardwareCredentials extends WalletCredentials {
MoneroRestoreWalletFromHardwareCredentials(
{required String name,
- required this.ledgerConnection,
+ required this.hardwareWalletService,
int height = 0,
String? password})
: super(name: name, password: password, height: height);
- LedgerConnection ledgerConnection;
+ HardwareWalletService hardwareWalletService;
}
class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
@@ -193,7 +193,7 @@ class MoneroWalletService extends WalletService<
unspentCoinsInfo: unspentCoinsInfoSource,
password: password);
- if (wallet.isHardwareWallet) {
+ if (wallet.hardwareWalletType == HardwareWalletType.ledger) {
wallet.setLedgerConnection(gLedger!);
gLedger = null;
}
@@ -301,15 +301,29 @@ class MoneroWalletService extends WalletService<
try {
final path = await pathForWallet(name: credentials.name, type: getType());
final password = credentials.password;
- final height = credentials.height;
- enableLedgerExchange(credentials.ledgerConnection);
+ if (credentials.hardwareWalletService case MoneroLedgerService service) {
+ enableLedgerExchange(service.connection);
- await monero_wallet_manager.restoreWalletFromHardwareWallet(
+ await monero_wallet_manager.restoreWalletFromHardwareWallet(
path: path,
password: password!,
- restoreHeight: height!,
- deviceName: 'Ledger');
+ restoreHeight: credentials.height!,
+ deviceName: 'Ledger',
+ );
+ } else if (credentials.hardwareWalletService case MoneroTrezorService service) {
+ final watchCredentials = await Trezor(service).getWatchCredentials();
+
+ monero_wallet_manager.restoreWalletFromKeys(
+ path: path,
+ password: credentials.password!,
+ language: "English",
+ restoreHeight: credentials.height!,
+ address: watchCredentials.address,
+ viewKey: watchCredentials.watchKey,
+ spendKey: "",
+ );
+ }
final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!,
@@ -567,7 +581,7 @@ class MoneroWalletService extends WalletService<
if (walletInfo == null) {
return false;
}
- return walletInfo.isHardwareWallet;
+ return walletInfo.hardwareWalletType == HardwareWalletType.ledger;
}
}
@@ -585,4 +599,4 @@ Future closeWalletAwaitIfShould(int wmaddr, int waddr) async {
monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
}));
}
-}
\ No newline at end of file
+}
diff --git a/cw_monero/lib/pending_monero_transaction.dart b/cw_monero/lib/pending_monero_transaction.dart
index e1d191eac..c32a2cc9c 100644
--- a/cw_monero/lib/pending_monero_transaction.dart
+++ b/cw_monero/lib/pending_monero_transaction.dart
@@ -1,5 +1,8 @@
import 'dart:async';
+import 'dart:ffi';
+import 'package:cw_core/utils/print_verbose.dart';
+import 'package:cw_core/wallet_info.dart';
import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/transaction_history.dart'
@@ -10,6 +13,7 @@ import 'package:cw_core/amount_converter.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/monero_wallet.dart';
+import 'package:monero/monero.dart' as monero;
class DoubleSpendException implements Exception {
DoubleSpendException();
@@ -43,22 +47,39 @@ class PendingMoneroTransaction with PendingTransaction {
CryptoCurrency.xmr, pendingTransactionDescription.fee);
@override
- bool shouldCommitUR() => isViewOnly;
+ bool shouldCommitUR() => isViewOnly && wallet.hardwareWalletType != HardwareWalletType.trezor;
@override
Future commit() async {
- try {
- await monero_transaction_history.commitTransactionFromPointerAddress(
- address: pendingTransactionDescription.pointerAddress,
- useUR: false);
- } catch (e) {
- final message = e.toString();
+ if (wallet.hardwareWalletType == HardwareWalletType.trezor) {
+ final ptr = Pointer.fromAddress(pendingTransactionDescription.pointerAddress);
+ final ret = await monero.PendingTransaction_commitTrezor(ptr, 0);
- if (message.contains('Reason: double spend')) {
- throw DoubleSpendException();
+ final json = await wallet.signTrezorTransaction(ret);
+
+ final wptr = Pointer.fromAddress(currentWallet!.ffiAddress());
+
+ final suc = await monero.Wallet_submitTransactionHex(wptr, json);
+
+ if (!suc) {
+ final err = monero.UnsignedTransaction_errorString(ptr);
+ printV(err);
+ throw err;
}
+ } else {
+ try {
+ await monero_transaction_history.commitTransactionFromPointerAddress(
+ address: pendingTransactionDescription.pointerAddress,
+ useUR: false);
+ } catch (e) {
+ final message = e.toString();
- rethrow;
+ if (message.contains('Reason: double spend')) {
+ throw DoubleSpendException();
+ }
+
+ rethrow;
+ }
}
storeSync(force: true);
unawaited(() async {
diff --git a/cw_monero/lib/trezor.dart b/cw_monero/lib/trezor.dart
new file mode 100644
index 000000000..f2651d062
--- /dev/null
+++ b/cw_monero/lib/trezor.dart
@@ -0,0 +1,53 @@
+import 'dart:convert';
+
+import 'package:cw_core/hardware/hardware_wallet_service.dart';
+import 'package:trezor_flutter/trezor_flutter.dart';
+
+class MoneroTrezorService extends HardwareWalletService {
+ final TrezorClient client;
+
+ MoneroTrezorService(this.client);
+}
+
+class MoneroTrezorWatchCredentials {
+ final String watchKey;
+ final String address;
+
+ const MoneroTrezorWatchCredentials(this.watchKey, this.address);
+}
+
+class Trezor {
+ final MoneroTrezorService service;
+
+ Trezor(this.service);
+
+ Future getWatchCredentials() async {
+ final credentials = await TrezorMonero(service.client).getWatchCredentials();
+
+ return MoneroTrezorWatchCredentials(credentials.$1, credentials.$2);
+ }
+
+ Future keyImageSync(String tdis) async {
+ final tdisMap = jsonDecode(tdis) as Map;
+ final tdisList = tdisMap["tdis"] as List;
+
+ final txIds = [];
+ for (final tdi in tdisList) {
+ txIds.add(MoneroKeyImageTxData(
+ outKey: tdi["out_key"],
+ txPubKey: tdi["tx_pub_key"],
+ internalOutputIndex: tdi["internal_output_index"],
+ subAddrMajor: tdi["sub_addr_major"],
+ subAddrMinor: tdi["sub_addr_minor"],
+ additionalTxPubKeys: (tdi["additional_tx_pub_keys"] as List?)?.map((e) => e as String).toList() ?? []
+ ));
+ }
+ final keyImages = await TrezorMonero(service.client).syncKeyImages(txIds);
+
+ return jsonEncode(keyImages.toMap());
+ }
+
+ Future signTransaction(String json) async {
+ return TrezorMonero(service.client).signTransaction(jsonDecode(json));
+ }
+}
diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock
index 6f6174d08..d00f2149e 100644
--- a/cw_monero/pubspec.lock
+++ b/cw_monero/pubspec.lock
@@ -296,6 +296,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.3"
+ fast_base58:
+ dependency: transitive
+ description:
+ name: fast_base58
+ sha256: "611f65633b734f27a850b51371b3eba993a5165650e12e8e7b02959f3768ba06"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.1"
ffi:
dependency: "direct main"
description:
@@ -342,10 +350,10 @@ packages:
dependency: transitive
description:
name: flutter_web_bluetooth
- sha256: "1363831def5eed1e1064d1eca04e8ccb35446e8f758579c3c519e156b77926da"
+ sha256: ad26a1b3fef95b86ea5f63793b9a0cdc1a33490f35d754e4e711046cae3ebbf8
url: "https://pub.dev"
source: hosted
- version: "1.0.0"
+ version: "1.1.0"
frontend_server_client:
dependency: transitive
description:
@@ -494,18 +502,18 @@ packages:
dependency: "direct main"
description:
name: ledger_flutter_plus
- sha256: "1c03f3c4a9754b5f0170a9eb9552ec54fa86e985f8ee71a255ee2c5629b53d31"
+ sha256: "59c5a6062505c721c2ca909fa085c52cffe93de995174b8418b54a6acf52eb70"
url: "https://pub.dev"
source: hosted
- version: "1.5.1"
+ version: "1.5.6"
ledger_usb_plus:
dependency: transitive
description:
name: ledger_usb_plus
- sha256: "21cc5d976cf7edb3518bd2a0c4164139cbb0817d2e4f2054707fc4edfdf9ce87"
+ sha256: "911df2d1d55cdae612ff2c9db140cf71a9b28a67ba16adfdc4983cd9f02c531e"
url: "https://pub.dev"
source: hosted
- version: "1.0.4"
+ version: "1.0.8"
logging:
dependency: transitive
description:
@@ -582,9 +590,9 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
- ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
- resolved-ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
- url: "https://github.com/mrcyjanek/monero_c"
+ ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
+ resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
+ url: "http://github.com/mrcyjanek/monero_c.git"
source: git
version: "0.0.0"
mutex:
@@ -724,6 +732,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
+ protobuf:
+ dependency: transitive
+ description:
+ name: protobuf
+ sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.0"
provider:
dependency: transitive
description:
@@ -962,6 +978,24 @@ packages:
relative: true
source: path
version: "0.0.1"
+ trezor_flutter:
+ dependency: "direct main"
+ description:
+ path: trezor-flutter
+ ref: "4bf54339a1caefd4c53a4a8873a9686ec13a9107"
+ resolved-ref: "4bf54339a1caefd4c53a4a8873a9686ec13a9107"
+ url: "https://github.com/cake-tech/trezor-flutter.git"
+ source: git
+ version: "0.1.0"
+ trezor_usb_transport:
+ dependency: transitive
+ description:
+ path: trezor_usb_transport
+ ref: "4bf54339a1caefd4c53a4a8873a9686ec13a9107"
+ resolved-ref: "4bf54339a1caefd4c53a4a8873a9686ec13a9107"
+ url: "https://github.com/cake-tech/trezor-flutter.git"
+ source: git
+ version: "0.0.1"
tuple:
dependency: transitive
description:
@@ -982,10 +1016,10 @@ packages:
dependency: transitive
description:
name: universal_ble
- sha256: "1fad089150a29db82b3b7d60327e18c5ad6b3a5bb509defc1c690b0a76b9c098"
+ sha256: "8325ca9f5cce2fba2b4efef74a4fcfe7144f303b409d6fc6ed16197b00a1a241"
url: "https://pub.dev"
source: hosted
- version: "0.15.0"
+ version: "1.2.0"
universal_platform:
dependency: transitive
description:
diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml
index 7f38c0ea0..4c10e94d7 100644
--- a/cw_monero/pubspec.yaml
+++ b/cw_monero/pubspec.yaml
@@ -25,11 +25,16 @@ dependencies:
path: ../cw_core
monero:
git:
- url: https://github.com/mrcyjanek/monero_c
- ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
+ url: https://github.com/mrcyjanek/monero_c.git
+ ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
path: impls/monero.dart
mutex: ^3.1.0
ledger_flutter_plus: ^1.4.1
+ trezor_flutter:
+ git:
+ url: https://github.com/cake-tech/trezor-flutter.git
+ ref: 4bf54339a1caefd4c53a4a8873a9686ec13a9107
+ path: trezor-flutter
dev_dependencies:
flutter_test:
diff --git a/cw_wownero/pubspec.lock b/cw_wownero/pubspec.lock
index 734af9338..55cfb8fdf 100644
--- a/cw_wownero/pubspec.lock
+++ b/cw_wownero/pubspec.lock
@@ -502,9 +502,9 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
- ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
- resolved-ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
- url: "https://github.com/mrcyjanek/monero_c"
+ ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
+ resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
+ url: "http://github.com/mrcyjanek/monero_c.git"
source: git
version: "0.0.0"
mutex:
diff --git a/cw_wownero/pubspec.yaml b/cw_wownero/pubspec.yaml
index a23fc3955..ca9c70bea 100644
--- a/cw_wownero/pubspec.yaml
+++ b/cw_wownero/pubspec.yaml
@@ -23,8 +23,8 @@ dependencies:
path: ../cw_core
monero:
git:
- url: https://github.com/mrcyjanek/monero_c
- ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2 # monero_c hash
+ url: https://github.com/mrcyjanek/monero_c.git
+ ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
path: impls/monero.dart
mutex: ^3.1.0
diff --git a/cw_zano/pubspec.lock b/cw_zano/pubspec.lock
index 662ccded6..6e2916d71 100644
--- a/cw_zano/pubspec.lock
+++ b/cw_zano/pubspec.lock
@@ -507,9 +507,9 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
- ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
- resolved-ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
- url: "https://github.com/mrcyjanek/monero_c"
+ ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
+ resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
+ url: "http://github.com/mrcyjanek/monero_c.git"
source: git
version: "0.0.0"
nested:
diff --git a/cw_zano/pubspec.yaml b/cw_zano/pubspec.yaml
index 8eb5d3e14..16585b715 100644
--- a/cw_zano/pubspec.yaml
+++ b/cw_zano/pubspec.yaml
@@ -24,8 +24,8 @@ dependencies:
fluttertoast: ^8.2.12
monero:
git:
- url: https://github.com/mrcyjanek/monero_c
- ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2 # monero_c hash
+ url: https://github.com/mrcyjanek/monero_c.git
+ ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
path: impls/monero.dart
dev_dependencies:
flutter_test:
diff --git a/integration_test/components/common_test_flows.dart b/integration_test/components/common_test_flows.dart
index d885e3e14..888c2ce03 100644
--- a/integration_test/components/common_test_flows.dart
+++ b/integration_test/components/common_test_flows.dart
@@ -231,12 +231,9 @@ class CommonTestFlows {
await _newWalletTypePageRobot.findParticularWalletTypeInScrollableList(type);
+ // Tapping the wallet type navigates directly (no separate Next button).
_tester.printToConsole('Tapping wallet type: ${type.name}');
await _newWalletTypePageRobot.selectWalletType(type);
- await _tester.pumpAndSettle(Duration(milliseconds: 500));
-
- _tester.printToConsole('Pressing next button');
- await _newWalletTypePageRobot.onNextButtonPressed();
await _tester.pumpAndSettle(Duration(milliseconds: 2000));
diff --git a/integration_test/robots/new_wallet_type_page_robot.dart b/integration_test/robots/new_wallet_type_page_robot.dart
index fc3e94f59..11785b67d 100644
--- a/integration_test/robots/new_wallet_type_page_robot.dart
+++ b/integration_test/robots/new_wallet_type_page_robot.dart
@@ -77,11 +77,9 @@ class NewWalletTypePageRobot {
tester.printToConsole('Wallet type ${type.name} is now visible and tappable');
}
+ // Tapping a wallet type now navigates directly (the page no longer has a
+ // separate "Next" button — selection happens on tap).
Future selectWalletType(WalletType type) async {
await commonTestCases.tapItemByKey('new_wallet_type_${type.name}_button_key');
}
-
- Future onNextButtonPressed() async {
- await commonTestCases.tapItemByKey('new_wallet_type_next_button_key');
- }
}
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index e661632c8..2d32cb833 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -295,6 +295,7 @@ SPEC CHECKSUMS:
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e
+ warp_api: 997d8fb3e777669bc6260d0c12e56cd40b9db1d1
YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966
PODFILE CHECKSUM: 5296465b1c6d14d506230356756826012f65d97a
diff --git a/lib/buy/dfx/dfx_buy_provider.dart b/lib/buy/dfx/dfx_buy_provider.dart
index f927b702d..582cccd5d 100644
--- a/lib/buy/dfx/dfx_buy_provider.dart
+++ b/lib/buy/dfx/dfx_buy_provider.dart
@@ -336,7 +336,7 @@ class DFXBuyProvider extends BuyProvider {
required String cryptoCurrencyAddress,
String? countryCode}) async {
if (wallet.isHardwareWallet) {
- if (!hardwareWalletVM!.isConnected) {
+ if (!hardwareWalletVM!.isConnected(wallet.walletInfo.type)) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: wallet.walletInfo.type,
diff --git a/lib/buy/robinhood/robinhood_buy_provider.dart b/lib/buy/robinhood/robinhood_buy_provider.dart
index 7cd992812..2c26fa4ca 100644
--- a/lib/buy/robinhood/robinhood_buy_provider.dart
+++ b/lib/buy/robinhood/robinhood_buy_provider.dart
@@ -173,7 +173,7 @@ class RobinhoodBuyProvider extends BuyProvider {
required String cryptoCurrencyAddress,
String? countryCode}) async {
if (wallet.isHardwareWallet) {
- if (!hardwareWalletVM!.isConnected) {
+ if (!hardwareWalletVM!.isConnected(wallet.walletInfo.type)) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: wallet.walletInfo.type,
diff --git a/lib/di.dart b/lib/di.dart
index 991e2f7ee..cd7ee62d1 100644
--- a/lib/di.dart
+++ b/lib/di.dart
@@ -240,7 +240,7 @@ import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/bitbox_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/hardware_wallet/trezor_view_model.dart';
+import 'package:cake_wallet/view_model/hardware_wallet/trezor_connect_view_model.dart';
import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
@@ -414,7 +414,7 @@ Future setup({
switch(type) {
case HardwareWalletType.bitbox: return getIt();
case HardwareWalletType.ledger: return getIt();
- case HardwareWalletType.trezor: return getIt();
+ case HardwareWalletType.trezor: return getIt();
case HardwareWalletType.cupcake:
case HardwareWalletType.coldcard:
case HardwareWalletType.seedsigner:
@@ -429,7 +429,7 @@ Future setup({
getIt.registerLazySingleton(() => TrezorConnect("cakewallet://trezor_connect",
appName: "Cake Wallet"));
- getIt.registerLazySingleton(() => TrezorViewModel(getIt()));
+ getIt.registerLazySingleton(() => TrezorConnectViewModel(getIt()));
getIt.registerFactory(() => KeyService(getIt.get()));
diff --git a/lib/entities/hardware_wallet/hardware_wallet_device.dart b/lib/entities/hardware_wallet/hardware_wallet_device.dart
index 646f5a2c2..757e68099 100644
--- a/lib/entities/hardware_wallet/hardware_wallet_device.dart
+++ b/lib/entities/hardware_wallet/hardware_wallet_device.dart
@@ -1,5 +1,6 @@
import 'package:bitbox_flutter/usb/bitbox_device.dart' as bitbox;
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
+import 'package:trezor_flutter/trezor_flutter.dart' as trezor;
abstract class HardwareWalletDevice {
String get name;
@@ -41,6 +42,21 @@ class BitboxHardwareWalletDevice extends HardwareWalletDevice {
HardwareWalletConnectionType get connectionType => HardwareWalletConnectionType.usb;
}
+class TrezorHardwareWalletDevice extends HardwareWalletDevice {
+ final trezor.TrezorDevice device;
+
+ TrezorHardwareWalletDevice(this.device);
+
+ @override
+ String get name => device.name;
+
+ @override
+ HardwareWalletDeviceType get type => device.deviceInfo.toGeneric();
+
+ @override
+ HardwareWalletConnectionType get connectionType => device.connectionType.toGeneric();
+}
+
enum HardwareWalletDeviceType {
ledgerBlue,
ledgerNanoS,
@@ -50,7 +66,12 @@ enum HardwareWalletDeviceType {
ledgerStax,
ledgerFlex,
BitBox02,
- BitBox02Nova;
+ BitBox02Nova,
+ trezorModelOne,
+ trezorModelT,
+ trezorSafe3,
+ trezorSafe5,
+ trezorSafe7;
}
enum HardwareWalletConnectionType {
@@ -60,7 +81,7 @@ enum HardwareWalletConnectionType {
qr;
}
-extension ToGenericHardwareWalletDeviceType on ledger.LedgerDeviceType {
+extension LedgerToGenericHardwareWalletDeviceType on ledger.LedgerDeviceType {
HardwareWalletDeviceType toGeneric() {
switch (this) {
case ledger.LedgerDeviceType.blue:
@@ -81,7 +102,24 @@ extension ToGenericHardwareWalletDeviceType on ledger.LedgerDeviceType {
}
}
-extension ToGenericHardwareWalletConnectionType on ledger.ConnectionType {
+extension TrezorToGenericHardwareWalletDeviceType on trezor.TrezorDeviceType {
+ HardwareWalletDeviceType toGeneric() {
+ switch (this) {
+ case trezor.TrezorDeviceType.modelOne:
+ return HardwareWalletDeviceType.trezorModelOne;
+ case trezor.TrezorDeviceType.modelT:
+ return HardwareWalletDeviceType.trezorModelT;
+ case trezor.TrezorDeviceType.safe3:
+ return HardwareWalletDeviceType.trezorSafe3;
+ case trezor.TrezorDeviceType.safe5:
+ return HardwareWalletDeviceType.trezorSafe5;
+ case trezor.TrezorDeviceType.safe7:
+ return HardwareWalletDeviceType.trezorSafe7;
+ }
+ }
+}
+
+extension LedgerToGenericHardwareWalletConnectionType on ledger.ConnectionType {
HardwareWalletConnectionType toGeneric() {
switch (this) {
case ledger.ConnectionType.usb:
@@ -91,3 +129,14 @@ extension ToGenericHardwareWalletConnectionType on ledger.ConnectionType {
}
}
}
+
+extension TrezorToGenericHardwareWalletConnectionType on trezor.ConnectionType {
+ HardwareWalletConnectionType toGeneric() {
+ switch (this) {
+ case trezor.ConnectionType.usb:
+ return HardwareWalletConnectionType.usb;
+ case trezor.ConnectionType.ble:
+ return HardwareWalletConnectionType.ble;
+ }
+ }
+}
diff --git a/lib/monero/cw_monero.dart b/lib/monero/cw_monero.dart
index 08143b3b3..0a378e219 100644
--- a/lib/monero/cw_monero.dart
+++ b/lib/monero/cw_monero.dart
@@ -236,13 +236,14 @@ class CWMonero extends Monero {
required String name,
required String password,
required int height,
- required ledger.LedgerConnection ledgerConnection,
+ required HardwareWalletService hardwareWalletService,
}) =>
MoneroRestoreWalletFromHardwareCredentials(
- name: name,
- password: password,
- height: height,
- ledgerConnection: ledgerConnection);
+ name: name,
+ password: password,
+ height: height,
+ hardwareWalletService: hardwareWalletService,
+ );
@override
WalletCredentials createMoneroRestoreWalletFromSeedCredentials(
@@ -416,9 +417,7 @@ class CWMonero extends Monero {
}
@override
- void monerocCheck() {
- checkIfMoneroCIsFine();
- }
+ void monerocCheck() => checkIfMoneroCIsFine();
@override
Future setLedgerConnection(Object wallet, ledger.LedgerConnection connection) async {
@@ -427,29 +426,41 @@ class CWMonero extends Monero {
}
@override
- void resetLedgerConnection() {
- disableLedgerExchange();
- }
+ void resetLedgerConnection() => disableLedgerExchange();
@override
- void setGlobalLedgerConnection(ledger.LedgerConnection connection) {
- gLedger = connection;
- }
+ void setGlobalLedgerConnection(ledger.LedgerConnection connection) => gLedger = connection;
@override
String? getLastLedgerCommand() => latestLedgerCommand;
- bool isViewOnly() {
- return isViewOnlyBySpendKey(null);
+ @override
+ void setHardwareWalletService(Object wallet, HardwareWalletService service) {
+ final moneroWallet = wallet as MoneroWallet;
+ moneroWallet.trezorService = service as MoneroTrezorService;
}
- @override
- Map> debugCallLength() {
- return monero_wallet_api.debugCallLength();
- }
@override
- Map getWalletCacheDebug() {
- return monero_wallet_api.getWalletCacheDebug();
+ HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection) =>
+ MoneroLedgerService(connection);
+
+ @override
+ HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorClient client) =>
+ MoneroTrezorService(client);
+
+ @override
+ Future syncTrezor(Object wallet) async {
+ final moneroWallet = wallet as MoneroWallet;
+
+ return moneroWallet.syncTrezor();
}
+
+ bool isViewOnly() => isViewOnlyBySpendKey(null);
+
+ @override
+ Map> debugCallLength() => monero_wallet_api.debugCallLength();
+
+ @override
+ Map getWalletCacheDebug() => monero_wallet_api.getWalletCacheDebug();
}
diff --git a/lib/new-ui/pages/send_page.dart b/lib/new-ui/pages/send_page.dart
index c3f5da957..9292d1af2 100644
--- a/lib/new-ui/pages/send_page.dart
+++ b/lib/new-ui/pages/send_page.dart
@@ -726,12 +726,13 @@ class _NewSendPageState extends State {
}
if (widget.sendViewModel.wallet.isHardwareWallet) {
- if (!widget.sendViewModel.hardwareWalletViewModel!.isConnected) {
+ if (!widget.sendViewModel.hardwareWalletViewModel!
+ .isConnected(widget.sendViewModel.walletType)) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: widget.sendViewModel.walletType,
hardwareWalletType: widget.sendViewModel.wallet.walletInfo.hardwareWalletType!,
- onConnectDevice: (BuildContext context, _) {
+ onConnectDevice: (_, __) {
widget.sendViewModel.hardwareWalletViewModel!
.initWallet(widget.sendViewModel.wallet);
Navigator.of(context).pop();
@@ -748,8 +749,12 @@ class _NewSendPageState extends State {
amount += item.formattedCryptoAmount;
}
if (monero!.needExportOutputs(widget.sendViewModel.wallet, amount)) {
- await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage,
- arguments: monero!.exportOutputsUR(widget.sendViewModel.wallet));
+ if (widget.sendViewModel.wallet.hardwareWalletType == HardwareWalletType.trezor) {
+ await monero!.syncTrezor(widget.sendViewModel.wallet);
+ } else {
+ await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage,
+ arguments: monero!.exportOutputsUR(widget.sendViewModel.wallet));
+ }
await Future.delayed(Duration(seconds: 1)); // wait for monero to refresh the state
}
if (monero!.needExportOutputs(widget.sendViewModel.wallet, amount)) {
diff --git a/lib/new-ui/widgets/coins_page/wallet_info.dart b/lib/new-ui/widgets/coins_page/wallet_info.dart
index 21c83db44..4963513fc 100644
--- a/lib/new-ui/widgets/coins_page/wallet_info.dart
+++ b/lib/new-ui/widgets/coins_page/wallet_info.dart
@@ -86,11 +86,11 @@ class WalletInfoBar extends StatelessWidget {
case null:
return null;
case HardwareWalletType.bitbox:
- return "assets/images/hardware_wallet/device_bitbox.svg";
+ return "assets/new-ui/hardware_wallets/device_bitbox.svg";
case HardwareWalletType.ledger:
- return "assets/images/hardware_wallet/device_ledger_nano_x.svg";
+ return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
case HardwareWalletType.trezor:
- return "assets/images/hardware_wallet/device_trezor_safe_5.svg";
+ return "assets/new-ui/hardware_wallets/device_trezor_safe_5.svg";
case HardwareWalletType.cupcake:
return "assets/images/cupcake.svg";
case HardwareWalletType.coldcard:
diff --git a/lib/new-ui/widgets/digit_input.dart b/lib/new-ui/widgets/digit_input.dart
new file mode 100644
index 000000000..d6889db67
--- /dev/null
+++ b/lib/new-ui/widgets/digit_input.dart
@@ -0,0 +1,182 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+class DigitInputController {
+ String _text = "";
+
+ String get text => _text;
+
+ set text(String newText) {
+ _text = newText;
+ for (final listener in _listeners) {
+ listener.call();
+ }
+ }
+
+ final List _listeners = [];
+
+ void addListener(void Function() listener) => _listeners.add(listener);
+}
+
+class DigitInputPill extends StatelessWidget {
+ const DigitInputPill({super.key, this.digit, required this.highlighted});
+
+ final String? digit;
+ final bool highlighted;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: 51,
+ height: 84,
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surfaceContainerHigh,
+ borderRadius: BorderRadius.circular(16),
+ border:
+ highlighted ? Border.all(color: Theme.of(context).colorScheme.primary, width: 2) : null,
+ ),
+ child: Center(
+ child: Padding(
+ padding: EdgeInsets.symmetric(vertical: 16),
+ child: Text(digit ?? " ", style: TextStyle(fontSize: 40)),
+ ),
+ ),
+ );
+ }
+}
+
+class DigitInput extends StatefulWidget {
+ const DigitInput({
+ super.key,
+ required this.controller,
+ required this.desiredLength,
+ this.breakAt = 3,
+ });
+
+ final DigitInputController controller;
+ final int desiredLength;
+ final int breakAt;
+
+ @override
+ State createState() => _DigitInputState();
+}
+
+class _DigitInputState extends State implements TextInputClient {
+ TextInputConnection? _textInputConnection;
+
+ @override
+ void initState() {
+ super.initState();
+ _openVirtualKeyboard();
+ }
+
+ void _openVirtualKeyboard() {
+ if (_textInputConnection == null || !_textInputConnection!.attached) {
+ _textInputConnection = TextInput.attach(
+ this,
+ const TextInputConfiguration(
+ inputType: TextInputType.numberWithOptions(
+ signed: false,
+ decimal: false,
+ ),
+ inputAction: TextInputAction.none,
+ ),
+ );
+ }
+
+ _textInputConnection!.setEditingState(
+ TextEditingValue(
+ text: widget.controller.text,
+ selection: TextSelection.collapsed(offset: widget.controller.text.length),
+ ),
+ );
+ _textInputConnection!.show();
+ }
+
+ void _closeVirtualKeyboard() => _textInputConnection?.close();
+
+ @override
+ void dispose() {
+ _closeVirtualKeyboard();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: _openVirtualKeyboard,
+ child: SizedBox(
+ height: 84,
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ shrinkWrap: true,
+ physics: NeverScrollableScrollPhysics(),
+ itemBuilder: (context, index) => DigitInputPill(
+ digit: widget.controller.text.length > index ? widget.controller.text[index] : null,
+ highlighted: widget.controller.text.length == index,
+ ),
+ separatorBuilder: (context, index) =>
+ SizedBox(width: (index + 1) % widget.breakAt == 0 ? 16 : 4),
+ itemCount: widget.desiredLength,
+ ),
+ ),
+ );
+ }
+
+ @override
+ void updateEditingValue(TextEditingValue value) {
+ final RegExp numberRegExp = RegExp(r'^\d{0,6}$');
+
+ if (numberRegExp.hasMatch(value.text)) {
+ setState(() => widget.controller.text = value.text);
+ } else {
+ // flutter keeps the text editing state regardless of whether you actually used it or not.
+ // so, if validation fails, we gotta set it back to what it was explicitly
+ _textInputConnection?.setEditingState(
+ TextEditingValue(
+ text: widget.controller.text,
+ selection: TextSelection.collapsed(offset: widget.controller.text.length),
+ ),
+ );
+ }
+ }
+
+ @override
+ void performAction(TextInputAction action) {}
+
+ @override
+ TextEditingValue? get currentTextEditingValue => TextEditingValue(text: widget.controller.text);
+
+ @override
+ void connectionClosed() => _textInputConnection = null;
+
+ @override
+ AutofillScope? get currentAutofillScope => null;
+
+ @override
+ void performPrivateCommand(String action, Map data) {}
+
+ @override
+ void showAutocorrectionPromptRect(int start, int end) {}
+
+ @override
+ void updateFloatingCursor(RawFloatingCursorPoint point) {}
+
+ @override
+ void insertTextPlaceholder(Size size) {}
+
+ @override
+ void removeTextPlaceholder() {}
+
+ @override
+ void showToolbar() {}
+
+ @override
+ void performSelector(String selectorName) {}
+
+ @override
+ void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) {}
+
+ @override
+ void insertContent(KeyboardInsertedContent content) {}
+}
diff --git a/lib/new-ui/widgets/hardware_wallet/proceed_on_device_message.dart b/lib/new-ui/widgets/hardware_wallet/proceed_on_device_message.dart
new file mode 100644
index 000000000..100325dbe
--- /dev/null
+++ b/lib/new-ui/widgets/hardware_wallet/proceed_on_device_message.dart
@@ -0,0 +1,60 @@
+import 'package:cake_wallet/generated/i18n.dart';
+import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
+import 'package:cw_core/wallet_info.dart';
+import 'package:flutter/material.dart';
+
+class HardwareWalletProceedOnDeviceMessage extends StatelessWidget {
+ const HardwareWalletProceedOnDeviceMessage({super.key, required this.hardwareWalletType});
+
+ final HardwareWalletType hardwareWalletType;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: double.infinity,
+ decoration: BoxDecoration(
+ border: Border.all(
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ borderRadius: BorderRadius.circular(16),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(12.0),
+ child: Column(spacing: 8, children: [
+ if (hardwareWalletIcon != null)
+ CakeImageWidget(
+ imageUrl: hardwareWalletIcon!,
+ width: 36,
+ height: 36,
+ colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
+ ),
+ Text(
+ S.of(context).proceed_on_device,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
+ )
+ ]),
+ ),
+ );
+ }
+
+ String? get hardwareWalletIcon {
+ switch (hardwareWalletType) {
+ case HardwareWalletType.bitbox:
+ return "assets/new-ui/hardware_wallets/device_bitbox.svg";
+ case HardwareWalletType.ledger:
+ return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
+ case HardwareWalletType.trezor:
+ return "assets/new-ui/hardware_wallets/device_trezor_safe_7.svg";
+ case HardwareWalletType.cupcake:
+ return "assets/images/cupcake.svg";
+ case HardwareWalletType.coldcard:
+ case HardwareWalletType.seedsigner:
+ case HardwareWalletType.keystone:
+ return "assets/images/hardware_wallet/device_qr.svg";
+ }
+ }
+}
diff --git a/lib/new-ui/widgets/hardware_wallet/proceed_on_device_sheet.dart b/lib/new-ui/widgets/hardware_wallet/proceed_on_device_sheet.dart
new file mode 100644
index 000000000..2a06f37e6
--- /dev/null
+++ b/lib/new-ui/widgets/hardware_wallet/proceed_on_device_sheet.dart
@@ -0,0 +1,338 @@
+import 'package:cake_wallet/generated/i18n.dart';
+import 'package:cake_wallet/new-ui/widgets/digit_input.dart';
+import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
+import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
+import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
+import 'package:cake_wallet/new-ui/widgets/send_page/directional_switcher.dart';
+import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
+import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
+import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
+import 'package:cake_wallet/utils/show_pop_up.dart';
+import 'package:cake_wallet/view_model/hardware_wallet/trezor_connect_view_model.dart';
+import 'package:cw_core/wallet_info.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:mobx/mobx.dart';
+
+class HardwareWalletProceedOnDeviceSheet extends StatefulWidget {
+ const HardwareWalletProceedOnDeviceSheet({
+ super.key,
+ required this.hardwareWalletType,
+ required this.trezorConnectVM,
+ required this.onRetry,
+ });
+
+ final HardwareWalletType hardwareWalletType;
+ final TrezorConnectViewModelBase trezorConnectVM;
+ final void Function() onRetry;
+
+ @override
+ State createState() =>
+ _HardwareWalletProceedOnDeviceSheetState();
+}
+
+class _HardwareWalletProceedOnDeviceSheetState extends State {
+ TrezorParingState _paringState = TrezorParingState.initial;
+
+ final DigitInputController _controller = DigitInputController();
+
+ static const pinOpenDuration = Duration(milliseconds: 300);
+
+ // you should probably check if there can be other pin lengths
+ static const pinLength = 6;
+
+ ReactionDisposer? paringStateReaction;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller.addListener(() => setState(() {}));
+
+ _paringState = widget.trezorConnectVM.paringState;
+ paringStateReaction = reaction((_) => widget.trezorConnectVM.paringState, (paringState) {
+ if (paringState is SuccessTrezorParingState) {
+ if (mounted) Navigator.of(context).pop();
+ return;
+ }
+ setState(() => _paringState = paringState);
+ });
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ paringStateReaction?.reaction.dispose();
+ }
+
+ bool get _isAwaitingPin => _paringState == TrezorParingState.enterPin;
+
+ @override
+ Widget build(BuildContext context) {
+ return PopScope(
+ canPop: false,
+ child: SafeArea(
+ bottom: false,
+ child: Container(
+ width: MediaQuery.of(context).size.width,
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surface,
+ borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
+ ),
+ child: SafeArea(
+ child: Padding(
+ padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
+ child: Column(
+ children: [
+ ModalTopBar(
+ title: "",
+ leadingWidget: AnimatedSwitcher(
+ layoutBuilder: (currentChild, previousChildren) {
+ return Stack(
+ alignment: Alignment.centerLeft,
+ children: [
+ ...previousChildren,
+ if (currentChild != null) currentChild,
+ ],
+ );
+ },
+ duration: pinOpenDuration,
+ child: Text(
+ key: ValueKey(pageTitle),
+ style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
+ pageTitle,
+ ),
+ ),
+ trailingIcon: Icon(
+ Icons.close,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ onTrailingPressed: () => showPopUp(
+ context: context,
+ builder: (context) => AlertWithTwoActions(
+ alertTitle: S.of(context).are_you_sure_exit,
+ alertContent: S.of(context).hww_exit_desc,
+ leftButtonText: S.of(context).cancel,
+ rightButtonText: S.of(context).yes_exit,
+ rightAlertButtonStyle: AlertButtonStyle.error(context),
+ actionLeftButton: Navigator.of(context).pop,
+ actionRightButton: () {
+ Navigator.of(context).pop();
+ Navigator.of(context).pop();
+ },
+ ),
+ ),
+ ),
+ Expanded(
+ child: DirectionalAnimatedSwitcher(
+ duration: Duration(milliseconds: 400),
+ child: content,
+ ),
+ ),
+ Padding(
+ padding: EdgeInsets.only(bottom: 24, right: 24),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ SizedBox(),
+ AnimatedOpacity(
+ curve: Curves.easeOutQuad,
+ opacity: hasFullPin ? 1 : 0,
+ duration: pinOpenDuration,
+ child: ModernButton(
+ backgroundColor: Theme.of(context).colorScheme.primary,
+ iconColor: Theme.of(context).colorScheme.onPrimary,
+ size: 36,
+ icon: Icon(Icons.arrow_forward),
+ onPressed: () =>
+ widget.trezorConnectVM.setParingPin(_controller.text.trim()),
+ ),
+ ),
+ ],
+ ),
+ )
+ ],
+ ),
+ )),
+ ),
+ ),
+ );
+ }
+
+ Widget get content {
+ if (_paringState is InitialTrezorParingState || _paringState is EnterPinTrezorParingState) {
+ return _enterPinCode();
+ }
+
+ if (_paringState is VerifyingPinTrezorParingState) return _verifyingCode();
+
+ if (_paringState is FailTrezorParingState)
+ return _errorBox((_paringState as FailTrezorParingState).message);
+
+ return SizedBox.shrink(key: ValueKey(3));
+ }
+
+ void retry() {
+ _controller.text = "";
+ widget.onRetry();
+ }
+
+ String get pageTitle {
+ if (_paringState is InitialTrezorParingState) return S.of(context).device_confirmation;
+ if (_isAwaitingPin) return S.of(context).pairing_code;
+ return "";
+ }
+
+ bool get hasFullPin => _controller.text.length == pinLength && _isAwaitingPin;
+
+ String? get hardwareWalletIcon {
+ switch (widget.hardwareWalletType) {
+ case HardwareWalletType.bitbox:
+ return "assets/new-ui/hardware_wallets/device_bitbox.svg";
+ case HardwareWalletType.ledger:
+ return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
+ case HardwareWalletType.trezor:
+ return "assets/new-ui/hardware_wallets/device_trezor_safe_7.svg";
+ case HardwareWalletType.cupcake:
+ return "assets/images/cupcake.svg";
+ case HardwareWalletType.coldcard:
+ case HardwareWalletType.seedsigner:
+ case HardwareWalletType.keystone:
+ return "assets/images/hardware_wallet/device_qr.svg";
+ }
+ }
+
+ Widget _verifyingCode() => Container(
+ key: ValueKey(1),
+ width: MediaQuery.of(context).size.width,
+ child: Column(
+ spacing: 12,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const CupertinoActivityIndicator(radius: 36),
+ const SizedBox(),
+ Text(
+ "${S.of(context).verifying_code}...",
+ style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
+ ),
+ Text(
+ S.of(context).this_can_take_few_seconds,
+ style: TextStyle(
+ fontWeight: FontWeight.w400,
+ fontSize: 16,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ )
+ ],
+ ),
+ );
+
+ Widget _errorBox(String errorText) => Container(
+ key: ValueKey(2),
+ width: MediaQuery.of(context).size.width,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 18),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ spacing: 12,
+ children: [
+ const Spacer(),
+ Icon(
+ Icons.error_outline,
+ size: 80,
+ color: Theme.of(context).colorScheme.error,
+ ),
+ Text(
+ S.of(context).pairing_error,
+ style: TextStyle(
+ fontSize: 20,
+ fontWeight: FontWeight.w500,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
+ textAlign: TextAlign.center,
+ ),
+ Text(
+ errorText,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ textAlign: TextAlign.center,
+ ),
+ const Spacer(),
+ NewPrimaryButton(
+ onPressed: retry,
+ text: S.of(context).try_again,
+ color: Theme.of(context).colorScheme.primary,
+ textColor: Theme.of(context).colorScheme.onPrimary,
+ )
+ ],
+ ),
+ ),
+ );
+
+ Widget _enterPinCode() => Column(
+ key: ValueKey(0),
+ spacing: 12,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ AnimatedContainer(
+ duration: pinOpenDuration,
+ curve: Curves.easeOutCubic,
+ width: _isAwaitingPin ? 40 : 100,
+ height: _isAwaitingPin ? 40 : 100,
+ child: CakeImageWidget(
+ imageUrl: hardwareWalletIcon,
+ colorFilter: ColorFilter.mode(
+ Theme.of(context).colorScheme.onSurfaceVariant,
+ BlendMode.srcIn,
+ ),
+ ),
+ ),
+ AnimatedSwitcher(
+ duration: pinOpenDuration,
+ switchInCurve: Curves.easeInCubic,
+ switchOutCurve: Curves.easeOutCubic,
+ child: _isAwaitingPin
+ ? Column(
+ spacing: 24,
+ key: ValueKey(1),
+ children: [
+ Text(
+ textAlign: TextAlign.center,
+ "${S.of(context).pairing_code_desc_1}\n${S.of(context).pairing_code_desc_2}",
+ ),
+ DigitInput(
+ controller: _controller,
+ desiredLength: pinLength,
+ )
+ ],
+ )
+ : Column(
+ key: ValueKey(0),
+ spacing: 12,
+ children: [
+ Text(
+ S.of(context).proceed_on_device,
+ style: TextStyle(
+ fontWeight: FontWeight.w500,
+ fontSize: 20,
+ ),
+ ),
+ Padding(
+ padding: EdgeInsets.symmetric(horizontal: 20),
+ child: Text(
+ S.of(context).proceed_on_device_description,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 16,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ ),
+ )
+ ],
+ ),
+ )
+ ],
+ );
+}
diff --git a/lib/new-ui/widgets/hardware_wallet/trezor_paring_sheet.dart b/lib/new-ui/widgets/hardware_wallet/trezor_paring_sheet.dart
new file mode 100644
index 000000000..9b429edc4
--- /dev/null
+++ b/lib/new-ui/widgets/hardware_wallet/trezor_paring_sheet.dart
@@ -0,0 +1,106 @@
+import 'package:cake_wallet/generated/i18n.dart';
+import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
+import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
+import 'package:flutter/material.dart';
+
+class HardwareWalletTrezorParingSheet extends StatefulWidget {
+ const HardwareWalletTrezorParingSheet({super.key});
+
+ @override
+ State createState() => _HardwareWalletTrezorParingSheetState();
+}
+
+class _HardwareWalletTrezorParingSheetState extends State {
+ final TextEditingController _paringCodeController = TextEditingController();
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
+ child: SafeArea(
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.vertical(top: Radius.circular(18)),
+ color: Theme.of(context).colorScheme.surface,
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ ModalTopBar(
+ title: "Paring code",
+ onLeadingPressed: Navigator.of(context).pop,
+ onTrailingPressed: () {},
+ leadingIcon: Icon(Icons.close),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(18.0),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ spacing: 12,
+ children: [
+ SizedBox(),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Expanded(
+ flex: 75,
+ child: AnimatedContainer(
+ duration: Duration(milliseconds: 300),
+ height: 60,
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surfaceContainer,
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ bottomLeft: Radius.circular(16),
+ ),
+ border: Border.all(
+ color: Theme.of(context).colorScheme.surfaceContainer,
+ width: 2,
+ ),
+ ),
+ child: TextField(
+ textAlign: TextAlign.left,
+ textAlignVertical: TextAlignVertical.center,
+ controller: _paringCodeController,
+ keyboardType:
+ TextInputType.numberWithOptions(signed: false, decimal: false),
+ decoration: InputDecoration(
+ hint: Text(
+ "000 000",
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 16,
+ color: Theme.of(
+ context,
+ ).colorScheme.onSurface.withValues(alpha: 0.5),
+ ),
+ ),
+ border: InputBorder.none,
+ filled: true,
+ fillColor: Colors.transparent,
+ ),
+ style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
+ ),
+ ),
+ ),
+ ],
+ ),
+ SizedBox(),
+ NewPrimaryButton(
+ text: S.of(context).continue_text,
+ onPressed: () => Navigator.of(context).pop(_paringCodeController.text),
+ color: Theme.of(context).colorScheme.primary,
+ textColor: Theme.of(context).colorScheme.onPrimary,
+ )
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/new-ui/widgets/send_page/send_confirm_bottom_widget.dart b/lib/new-ui/widgets/send_page/send_confirm_bottom_widget.dart
index 68f8c6889..671579914 100644
--- a/lib/new-ui/widgets/send_page/send_confirm_bottom_widget.dart
+++ b/lib/new-ui/widgets/send_page/send_confirm_bottom_widget.dart
@@ -1,16 +1,14 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/confirm_swiper.dart';
+import 'package:cake_wallet/new-ui/widgets/hardware_wallet/proceed_on_device_message.dart';
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
-import 'package:cw_core/wallet_info.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
-import 'package:flutter_svg/svg.dart';
-import 'package:vector_graphics/vector_graphics.dart';
class SendConfirmBottomWidget extends StatelessWidget {
const SendConfirmBottomWidget({super.key, required this.sendViewModel});
@@ -61,7 +59,7 @@ class SendConfirmBottomWidget extends StatelessWidget {
text: "${S.of(context).signing_transaction}...",
);
case IsAwaitingDeviceResponseState:
- return HardwareWalletConfirmationMessage(
+ return HardwareWalletProceedOnDeviceMessage(
hardwareWalletType: sendViewModel.wallet.hardwareWalletType!);
case TransactionCommitting:
return LoadingBottomWidget(
@@ -173,58 +171,3 @@ class TransactionErrorActions extends StatelessWidget {
);
}
}
-
-class HardwareWalletConfirmationMessage extends StatelessWidget {
- const HardwareWalletConfirmationMessage({super.key, required this.hardwareWalletType});
-
- final HardwareWalletType hardwareWalletType;
-
- @override
- Widget build(BuildContext context) {
- return Container(
- width: double.infinity,
- decoration: BoxDecoration(
- border: Border.all(
- color: Theme.of(context).colorScheme.primary,
- ),
- borderRadius: BorderRadius.circular(16),
- ),
- child: Padding(
- padding: const EdgeInsets.all(12.0),
- child: Column(spacing: 8, children: [
- if (hardwareWalletIcon != null)
- CakeImageWidget(imageUrl:
- hardwareWalletIcon!,
- width: 36,
- height: 36,
- colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
- ),
- Text(
- S.of(context).proceed_on_device,
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w400,
- color: Theme.of(context).colorScheme.onSurface),
- )
- ]),
- ),
- );
- }
-
- String? get hardwareWalletIcon {
- switch (hardwareWalletType) {
- case HardwareWalletType.bitbox:
- return "assets/images/hardware_wallet/device_bitbox.svg";
- case HardwareWalletType.ledger:
- return "assets/images/hardware_wallet/device_ledger_nano_x.svg";
- case HardwareWalletType.trezor:
- return "assets/images/hardware_wallet/device_trezor_safe_5.svg";
- case HardwareWalletType.cupcake:
- return "assets/images/cupcake.svg";
- case HardwareWalletType.coldcard:
- case HardwareWalletType.seedsigner:
- case HardwareWalletType.keystone:
- return "assets/images/hardware_wallet/device_qr.svg";
- }
- }
-}
diff --git a/lib/new-ui/widgets/swap_page/swap_confirm_sheet.dart b/lib/new-ui/widgets/swap_page/swap_confirm_sheet.dart
index 9ccfb1453..3676cd6e4 100644
--- a/lib/new-ui/widgets/swap_page/swap_confirm_sheet.dart
+++ b/lib/new-ui/widgets/swap_page/swap_confirm_sheet.dart
@@ -41,7 +41,7 @@ class _SwapConfirmSheetState extends State {
final sendVM = widget.exchangeTradeViewModel.sendViewModel;
if (sendVM.wallet.isHardwareWallet) {
- if (!sendVM.hardwareWalletViewModel!.isConnected) {
+ if (!sendVM.hardwareWalletViewModel!.isConnected(sendVM.walletType)) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: sendVM.walletType,
diff --git a/lib/router.dart b/lib/router.dart
index a5af558eb..89365171a 100644
--- a/lib/router.dart
+++ b/lib/router.dart
@@ -371,7 +371,7 @@ Route createRoute(RouteSettings settings) {
(_) => getIt.get(
param1: NewWalletTypeArguments(
onTypeSelected: (BuildContext context, WalletType type) {
- if (hardwareWalletType == HardwareWalletType.trezor) {
+ if (hardwareWalletType == HardwareWalletType.trezor && type != WalletType.monero) {
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
arguments: [type, hardwareWalletType]);
return;
diff --git a/lib/src/screens/connect_device/connect_device_page.dart b/lib/src/screens/connect_device/connect_device_page.dart
index 2122db44a..22e466cf3 100644
--- a/lib/src/screens/connect_device/connect_device_page.dart
+++ b/lib/src/screens/connect_device/connect_device_page.dart
@@ -3,10 +3,13 @@ import 'dart:io';
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
import 'package:cake_wallet/generated/i18n.dart';
+import 'package:cake_wallet/main.dart';
+import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/connect_device/widgets/device_tile.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart';
+import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/themes/core/material_base_theme.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
@@ -91,6 +94,8 @@ class ConnectDevicePageBodyState extends State {
var bleDevices = [];
var usbDevices = [];
+ List get allDevices => [...bleDevices, ...usbDevices];
+
late Timer? _usbRefreshTimer = null;
late Timer? _bleRefreshTimer = null;
late Timer? _bleStateTimer = null;
@@ -158,24 +163,38 @@ class ConnectDevicePageBodyState extends State {
Future _connectToDevice(HardwareWalletDevice device) async {
final isConnected = await widget.hardwareWalletVM.connectDevice(device, widget.walletType);
- if (isConnected) widget.onConnectDevice(context, widget.hardwareWalletVM);
+
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (isConnected) widget.onConnectDevice(navigatorKey.currentContext!, widget.hardwareWalletVM);
+ });
}
String _getDeviceTileLeading(HardwareWalletDeviceType deviceType) {
switch (deviceType) {
case HardwareWalletDeviceType.ledgerNanoX:
- return 'assets/images/hardware_wallet/ledger_nano_x.png';
+ return 'assets/new-ui/hardware_wallets/ledger_nano_x.svg';
case HardwareWalletDeviceType.ledgerNanoGen5:
- return 'assets/images/hardware_wallet/device_ledger_nano_gen_5.svg';
+ return 'assets/new-ui/hardware_wallets/device_ledger_nano_gen_5.svg';
case HardwareWalletDeviceType.ledgerStax:
- return 'assets/images/hardware_wallet/ledger_stax.png';
+ return 'assets/new-ui/hardware_wallets/ledger_stax.svg';
case HardwareWalletDeviceType.ledgerFlex:
- return 'assets/images/hardware_wallet/ledger_flex.png';
+ return 'assets/new-ui/hardware_wallets/ledger_flex.svg';
case HardwareWalletDeviceType.BitBox02:
- return 'assets/images/hardware_wallet/device_bitbox.svg';
+ case HardwareWalletDeviceType.BitBox02Nova:
+ return 'assets/new-ui/hardware_wallets/device_bitbox.svg';
+ case HardwareWalletDeviceType.trezorModelOne:
+ return 'assets/new-ui/hardware_wallets/device_trezor_model_one.svg';
+ case HardwareWalletDeviceType.trezorModelT:
+ return 'assets/new-ui/hardware_wallets/device_trezor_model_t.svg';
+ case HardwareWalletDeviceType.trezorSafe3:
+ return 'assets/new-ui/hardware_wallets/device_trezor_safe_3.svg';
+ case HardwareWalletDeviceType.trezorSafe5:
+ return 'assets/new-ui/hardware_wallets/device_trezor_safe_5.svg';
+ case HardwareWalletDeviceType.trezorSafe7:
+ return 'assets/new-ui/hardware_wallets/device_trezor_safe_7.svg';
default:
- return 'assets/images/hardware_wallet/ledger_nano_x.png';
+ return 'assets/new-ui/hardware_wallets/ledger_nano_x.svg';
}
}
@@ -197,25 +216,15 @@ class ConnectDevicePageBodyState extends State {
Expanded(
child: SingleChildScrollView(
child: Column(
+ spacing: 24,
children: [
- Padding(
- padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
- child: Text(
- description,
- style: Theme.of(context).textTheme.titleMedium,
- textAlign: TextAlign.center,
- ),
+ CakeImageWidget(
+ imageUrl: "assets/new-ui/hardware_wallet.svg",
),
- Offstage(
- offstage: !longWait,
- child: Padding(
- padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
- child: Text(
- S.of(context).if_you_dont_see_your_device,
- style: Theme.of(context).textTheme.titleMedium,
- textAlign: TextAlign.center,
- ),
- ),
+ Text(
+ description,
+ style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
+ textAlign: TextAlign.center,
),
Observer(
builder: (_) => Offstage(
@@ -231,56 +240,69 @@ class ConnectDevicePageBodyState extends State {
),
),
),
- if (bleDevices.length > 0) ...[
- Padding(
- padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
- child: Container(
- width: double.infinity,
- child: Text(
- S.of(context).bluetooth,
- style: Theme.of(context).textTheme.bodyMedium,
- ),
- ),
- ),
- ...bleDevices
- .map(
- (device) => Padding(
- padding: EdgeInsets.only(bottom: 20),
- child: DeviceTile(
- onPressed: () => _connectToDevice(device),
- title: device.name,
- leading: _getDeviceTileLeading(device.type),
- connectionType: device.connectionType,
+ Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surfaceContainer,
+ borderRadius: BorderRadius.circular(18)),
+ child: ListView.separated(
+ shrinkWrap: true,
+ itemBuilder: (context, index) {
+ final item = allDevices[index];
+ return GestureDetector(
+ onTap: () => _connectToDevice(item),
+ child: SizedBox(
+ height: 48,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ spacing: 12,
+ children: [
+ CakeImageWidget(
+ imageUrl: _getDeviceTileLeading(item.type),
+ width: 24,
+ height: 24,
+ colorFilter: ColorFilter.mode(
+ Theme.of(context).colorScheme.onSurfaceVariant,
+ BlendMode.srcIn),
+ ),
+ Text(item.name)
+ ],
+ ),
+ Row(
+ spacing: 12,
+ children: [
+ if (item.connectionType ==
+ HardwareWalletConnectionType.ble)
+ CakeImageWidget(
+ imageUrl: "assets/new-ui/bluetooth.svg",
+ width: 24,
+ height: 24,
+ colorFilter: ColorFilter.mode(
+ Theme.of(context).colorScheme.primary,
+ BlendMode.srcIn),
+ ),
+ CakeImageWidget(
+ imageUrl: "assets/new-ui/arrow_forward.svg",
+ height: 16,
+ colorFilter: ColorFilter.mode(
+ Theme.of(context).colorScheme.onSurfaceVariant,
+ BlendMode.srcIn))
+ ],
+ )
+ ],
+ ),
+ ),
),
- ),
- )
- .toList()
- ],
- if (usbDevices.length > 0) ...[
- Padding(
- padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
- child: Container(
- width: double.infinity,
- child: Text(
- S.of(context).usb,
- style: Theme.of(context).textTheme.bodyMedium,
- ),
- ),
- ),
- ...usbDevices
- .map(
- (device) => Padding(
- padding: EdgeInsets.only(bottom: 20),
- child: DeviceTile(
- onPressed: () => _connectToDevice(device),
- title: device.name,
- leading: _getDeviceTileLeading(device.type),
- connectionType: device.connectionType,
- ),
- ),
- )
- .toList(),
- ],
+ );
+ },
+ separatorBuilder: (context, index) => Container(
+ height: 1,
+ color: Theme.of(context).colorScheme.surfaceContainerHigh),
+ itemCount: allDevices.length),
+ ),
if (widget.allowChangeWallet) ...[
PrimaryButton(
text: S.of(context).wallets,
@@ -293,10 +315,10 @@ class ConnectDevicePageBodyState extends State {
),
),
),
- PrimaryButton(
+ NewPrimaryButton(
text: S.of(context).how_to_connect,
color: Theme.of(context).colorScheme.surfaceContainer,
- textColor: Theme.of(context).colorScheme.onSecondaryContainer,
+ textColor: Theme.of(context).colorScheme.primary,
onPressed: () => _onHowToConnect(context),
)
],
diff --git a/lib/src/screens/connect_device/widgets/device_tile.dart b/lib/src/screens/connect_device/widgets/device_tile.dart
index 803a648bb..4014e5040 100644
--- a/lib/src/screens/connect_device/widgets/device_tile.dart
+++ b/lib/src/screens/connect_device/widgets/device_tile.dart
@@ -1,4 +1,5 @@
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
+import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -43,14 +44,12 @@ class DeviceTile extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
- if (leading != null && !leading!.endsWith(".svg"))
- Image.asset(
- leading!,
- height: 30,
- color: Theme.of(context).colorScheme.onSurface,
- ),
- if (leading != null && leading!.endsWith(".svg"))
- SvgPicture.asset(leading!, height: 30),
+ CakeImageWidget(
+ height: 30,
+ imageUrl: leading,
+ colorFilter:
+ ColorFilter.mode(Theme.of(context).colorScheme.onSurface, BlendMode.srcIn),
+ ),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 16),
diff --git a/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart b/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart
index a6ea7b340..de05bccf4 100644
--- a/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart
+++ b/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart
@@ -34,11 +34,11 @@ class CryptoBalanceWidget extends StatelessWidget {
case null:
return null;
case HardwareWalletType.bitbox:
- return "assets/images/hardware_wallet/device_bitbox.svg";
+ return "assets/new-ui/hardware_wallets/device_bitbox.svg";
case HardwareWalletType.ledger:
- return "assets/images/hardware_wallet/device_ledger_nano_x.svg";
+ return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
case HardwareWalletType.trezor:
- return "assets/images/hardware_wallet/device_trezor_safe_5.svg";
+ return "assets/new-ui/hardware_wallets/device_trezor_safe_5.svg";
case HardwareWalletType.cupcake:
return "assets/images/cupcake.svg";
case HardwareWalletType.coldcard:
diff --git a/lib/src/screens/exchange_trade/exchange_trade_page.dart b/lib/src/screens/exchange_trade/exchange_trade_page.dart
index d7fd3196e..6ebd2526f 100644
--- a/lib/src/screens/exchange_trade/exchange_trade_page.dart
+++ b/lib/src/screens/exchange_trade/exchange_trade_page.dart
@@ -213,7 +213,7 @@ class ExchangeTradeState extends State {
final sendVM = widget.exchangeTradeViewModel.sendViewModel;
if (sendVM.wallet.isHardwareWallet) {
- if (!sendVM.hardwareWalletViewModel!.isConnected) {
+ if (!sendVM.hardwareWalletViewModel!.isConnected(sendVM.walletType)) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: sendVM.walletType,
diff --git a/lib/src/screens/integrations/deuro/savings_page.dart b/lib/src/screens/integrations/deuro/savings_page.dart
index 4a5d62a02..a08437822 100644
--- a/lib/src/screens/integrations/deuro/savings_page.dart
+++ b/lib/src/screens/integrations/deuro/savings_page.dart
@@ -160,7 +160,7 @@ class DEuroSavingsPage extends BasePage {
Future _requireHardwareWallet(BuildContext context) async {
if (_dEuroViewModel.wallet.isHardwareWallet) {
- if (!_dEuroViewModel.hardwareWalletViewModel!.isConnected) {
+ if (!_dEuroViewModel.hardwareWalletViewModel!.isConnected(_dEuroViewModel.wallet.type)) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: _dEuroViewModel.wallet.type,
diff --git a/lib/src/screens/new_wallet/new_wallet_type_page.dart b/lib/src/screens/new_wallet/new_wallet_type_page.dart
index fff206836..4b170dc03 100644
--- a/lib/src/screens/new_wallet/new_wallet_type_page.dart
+++ b/lib/src/screens/new_wallet/new_wallet_type_page.dart
@@ -6,14 +6,8 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_utils.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
-import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
-import 'package:cake_wallet/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
-import 'package:cake_wallet/src/widgets/primary_button.dart';
-import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
-import 'package:cake_wallet/src/widgets/search_bar_widget.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
-import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/hardware/device_connection_type.dart';
@@ -21,10 +15,38 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
+class NewWalletTypePageHeader extends StatelessWidget {
+ const NewWalletTypePageHeader({super.key});
+
+ @override
+ Widget build(BuildContext context) => const SizedBox(
+ width: 138,
+ height: 75,
+ child: Stack(
+ children: [
+ Positioned(
+ left: 0,
+ child: CakeImageWidget(
+ imageUrl: "assets/new-ui/hardware_wallet.svg",
+ width: 75,
+ height: 75,
+ ),
+ ),
+ Positioned(
+ left: 63,
+ child: CakeImageWidget(
+ imageUrl: "assets/new-ui/cake_coin.svg",
+ width: 75,
+ height: 75,
+ ),
+ ),
+ ],
+ ),
+ );
+}
+
class NewWalletTypePage extends BasePage {
- NewWalletTypePage({
- required this.newWalletTypeArguments,
- });
+ NewWalletTypePage({required this.newWalletTypeArguments});
final NewWalletTypeArguments newWalletTypeArguments;
@@ -38,23 +60,21 @@ class NewWalletTypePage extends BasePage {
@override
Function(BuildContext)? get pushToNextWidget => (context) {
- FocusScopeNode currentFocus = FocusScope.of(context);
- if (!currentFocus.hasPrimaryFocus) {
- currentFocus.focusedChild?.unfocus();
- }
- };
+ final currentFocus = FocusScope.of(context);
+ if (!currentFocus.hasPrimaryFocus) currentFocus.focusedChild?.unfocus();
+ };
@override
Widget body(BuildContext context) => WalletTypeForm(
- walletImage: currentTheme.isDark ? walletTypeImage : walletTypeLightImage,
- isCreate: newWalletTypeArguments.isCreate,
- onTypeSelected: newWalletTypeArguments.onTypeSelected,
- hardwareWalletType: newWalletTypeArguments.hardwareWalletType,
- );
+ walletImage: currentTheme.isDark ? walletTypeImage : walletTypeLightImage,
+ isCreate: newWalletTypeArguments.isCreate,
+ onTypeSelected: newWalletTypeArguments.onTypeSelected,
+ hardwareWalletType: newWalletTypeArguments.hardwareWalletType,
+ );
}
class WalletTypeForm extends StatefulWidget {
- WalletTypeForm({
+ const WalletTypeForm({
required this.walletImage,
required this.isCreate,
this.onTypeSelected,
@@ -77,9 +97,6 @@ class WalletTypeFormState extends State {
static const aspectRatioImage = 1.22;
- final TextEditingController searchController = TextEditingController();
-
- WalletType? selected;
List types;
List filteredTypes = [];
@@ -87,21 +104,12 @@ class WalletTypeFormState extends State {
void initState() {
types = filteredTypes = availableWalletTypes
.where((element) =>
- !widget.isHardwareWallet ||
- DeviceConnectionType.supportedConnectionTypes(
- element, widget.hardwareWalletType!, Platform.isIOS)
- .isNotEmpty)
+ !widget.isHardwareWallet ||
+ DeviceConnectionType.supportedConnectionTypes(
+ element, widget.hardwareWalletType!, Platform.isIOS)
+ .isNotEmpty)
.toList();
super.initState();
-
- searchController.addListener(() {
- setState(() {
- filteredTypes = List.from(types.where((type) => walletTypeToDisplayName(type)
- .toLowerCase()
- .contains(searchController.text.toLowerCase())));
- return;
- });
- });
}
@override
@@ -110,96 +118,102 @@ class WalletTypeFormState extends State {
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
child: Column(
+ spacing: 24,
children: [
- Padding(
- padding: EdgeInsets.only(top: 48),
- child: Text(
- S.of(context).choose_wallet_currency,
- textAlign: TextAlign.center,
- style: Theme.of(context).textTheme.titleMedium?.copyWith(
- color: Theme.of(context).colorScheme.onSurfaceVariant,
- ),
- ),
+ const NewWalletTypePageHeader(),
+ Text(
+ S.of(context).choose_wallet_currency,
+ textAlign: TextAlign.center,
),
- Padding(
- padding: EdgeInsets.fromLTRB(24, 24, 24, 12),
- child: SearchBarWidget(searchController: searchController),
- ),
- Expanded(
- child: ScrollableWithBottomSection(
- contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
- scrollableKey: ValueKey('new_wallet_type_scrollable_key'),
- content: Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- ...filteredTypes.map(
- (type) => Padding(
- padding: EdgeInsets.only(top: 12),
- child: SelectButton(
- key: ValueKey('new_wallet_type_${type.name}_button_key'),
- image: CakeImageWidget(
- imageUrl: getCryptoCurrencyIconForWalletListItem(type),
- height: 24,
- width: 24,
+ Flexible(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 18),
+ child: Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surfaceContainer,
+ borderRadius: BorderRadius.circular(18),
+ ),
+ child: ListView.separated(
+ key: const ValueKey('new_wallet_type_scrollable_key'),
+ itemCount: filteredTypes.length,
+ shrinkWrap: true,
+ itemBuilder: (context, index) {
+ final item = filteredTypes[index];
+ final curr = getCryptoCurrencyForWalletListItem(item);
+
+ return GestureDetector(
+ key: ValueKey('new_wallet_type_${item.name}_button_key'),
+ behavior: HitTestBehavior.opaque,
+ onTap: () {
+ onTypeSelected(item);
+ },
+ child: SizedBox(
+ height: 48,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ CakeImageWidget(
+ height: 24,
+ width: 24,
+ imageUrl: curr.iconPath,
+ ),
+ const SizedBox(width: 12),
+ Text(curr.fullName ?? curr.name),
+ const SizedBox(width: 4),
+ Text(
+ curr.title,
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ )
+ ],
+ ),
+ CakeImageWidget(
+ imageUrl: "assets/new-ui/arrow_forward.svg",
+ height: 16,
+ colorFilter: ColorFilter.mode(
+ Theme.of(context).colorScheme.onSurfaceVariant,
+ BlendMode.srcIn,
+ ),
+ )
+ ],
+ ),
),
- text: walletTypeToDisplayName(type),
- showTrailingIcon: false,
- height: 54,
- isSelected: selected == type,
- onTap: () => setState(() => selected = type),
- deviceConnectionTypes: widget.isHardwareWallet
- ? DeviceConnectionType.supportedConnectionTypes(
- type, widget.hardwareWalletType!, Platform.isIOS)
- : [],
),
- ),
+ );
+ },
+ separatorBuilder: (context, index) => Container(
+ height: 1,
+ color: Theme.of(context).colorScheme.surfaceContainerHigh,
),
- ],
- ),
- bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
- bottomSection: PrimaryButton(
- key: ValueKey('new_wallet_type_next_button_key'),
- onPressed: () => onTypeSelected(),
- text: S.of(context).seed_language_next,
- color: Theme.of(context).colorScheme.primary,
- textColor: Theme.of(context).colorScheme.onPrimary,
- isDisabled: selected == null,
+ ),
),
),
- ),
+ )
],
),
),
);
}
- Future onTypeSelected() async {
- if (selected == null) throw Exception('Wallet Type is not selected yet.');
-
- if (selected == WalletType.haven && widget.isCreate) {
- return await showPopUp(
- context: context,
- builder: (BuildContext context) => PopUpCancellableAlertDialog(
- contentText: S.of(context).pause_wallet_creation,
- actionButtonText: S.of(context).ok,
- buttonAction: () => Navigator.of(context).pop(),
- ),
- );
- }
-
+ Future onTypeSelected(WalletType selected) async {
// If it's a restore flow, trigger the external callback
// If it's not a BIP39 Wallet or if there are no other wallets, route to the newWallet page
// Any other scenario, route to pre-existing seed page
final walletList = await WalletInfo.getAll();
if (!widget.isCreate) {
- widget.onTypeSelected!(context, selected!);
- } else if (!isBIP39Wallet(selected!) || walletList.isEmpty) {
+ widget.onTypeSelected!(context, selected);
+ } else if (!isBIP39Wallet(selected) || walletList.isEmpty) {
Navigator.of(context).pushNamed(
Routes.newWallet,
- arguments: NewWalletArguments(type: selected!),
+ arguments: NewWalletArguments(type: selected),
);
} else {
- Navigator.of(context).pushNamed(Routes.walletGroupDescription, arguments: selected!);
+ Navigator.of(context).pushNamed(Routes.walletGroupDescription, arguments: selected);
}
}
-}
\ No newline at end of file
+}
diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart
index 7e44e413d..db0d5e883 100644
--- a/lib/src/screens/send/send_page.dart
+++ b/lib/src/screens/send/send_page.dart
@@ -426,7 +426,7 @@ class SendPage extends BasePage {
}
if (sendViewModel.wallet.isHardwareWallet) {
- if (!sendViewModel.hardwareWalletViewModel!.isConnected) {
+ if (!sendViewModel.hardwareWalletViewModel!.isConnected(sendViewModel.walletType)) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: sendViewModel.walletType,
diff --git a/lib/view_model/hardware_wallet/bitbox_view_model.dart b/lib/view_model/hardware_wallet/bitbox_view_model.dart
index 07a412cc2..b51815451 100644
--- a/lib/view_model/hardware_wallet/bitbox_view_model.dart
+++ b/lib/view_model/hardware_wallet/bitbox_view_model.dart
@@ -78,7 +78,7 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
bool _isConnecting = false;
@override
- bool get isConnected => false;
+ bool isConnected(WalletType type) => false;
@override
HardwareWalletService getHardwareWalletService(WalletType type) {
diff --git a/lib/view_model/hardware_wallet/hardware_wallet_view_model.dart b/lib/view_model/hardware_wallet/hardware_wallet_view_model.dart
index 4c79eda71..de64079b5 100644
--- a/lib/view_model/hardware_wallet/hardware_wallet_view_model.dart
+++ b/lib/view_model/hardware_wallet/hardware_wallet_view_model.dart
@@ -6,10 +6,11 @@ import 'package:cw_core/wallet_type.dart';
abstract class HardwareWalletViewModel {
HardwareWalletType get hardwareWalletType;
- bool get isConnected;
bool get isBleEnabled;
bool get hasBluetooth;
+ bool isConnected(WalletType type);
+
Future updateBleState();
Stream scanForBleDevices();
diff --git a/lib/view_model/hardware_wallet/ledger_view_model.dart b/lib/view_model/hardware_wallet/ledger_view_model.dart
index 793f0df84..c0746223a 100644
--- a/lib/view_model/hardware_wallet/ledger_view_model.dart
+++ b/lib/view_model/hardware_wallet/ledger_view_model.dart
@@ -114,7 +114,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
if (_isConnecting) return false;
_isConnecting = true;
_connectingWalletType = type;
- if (isConnected) {
+ if (isConnected(type)) {
try {
await _connection!.disconnect().catchError((_) {});
} catch (_) {}
@@ -126,7 +126,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
if (_connectionChangeSubscription == null) {
_connectionChangeSubscription = ledger
- .deviceStateChanges
+ .deviceStateChanges(device.device.id)
.listen(_connectionChangeListener);
}
@@ -172,7 +172,7 @@ 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!;
@@ -196,6 +196,8 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
@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:
diff --git a/lib/view_model/hardware_wallet/trezor_connect_view_model.dart b/lib/view_model/hardware_wallet/trezor_connect_view_model.dart
new file mode 100644
index 000000000..3fd4514e6
--- /dev/null
+++ b/lib/view_model/hardware_wallet/trezor_connect_view_model.dart
@@ -0,0 +1,258 @@
+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/evm/evm.dart';
+import 'package:cake_wallet/main.dart';
+import 'package:cake_wallet/monero/monero.dart';
+import 'package:cake_wallet/new-ui/widgets/hardware_wallet/proceed_on_device_sheet.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: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';
+
+class TrezorConnectViewModel = TrezorConnectViewModelBase with _$TrezorConnectViewModel;
+
+abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with Store {
+ final connect_sdk.TrezorConnect trezorConnect;
+
+ late final sdk.TrezorInterface trezorBLE;
+ late final sdk.TrezorInterface trezorUSB;
+
+ sdk.ThpState _state = sdk.ThpState();
+
+ TrezorConnectViewModelBase(this.trezorConnect) {
+ if (_doesSupportHardwareWallets) {
+ reaction((_) => isBleEnabled, (_) {
+ if (isBleEnabled) _initBLE();
+ });
+ updateBleState();
+
+ if (!Platform.isIOS) {
+ trezorUSB = sdk.TrezorInterface.usb();
+ }
+ }
+ }
+
+ bool get _doesSupportHardwareWallets {
+ if (isMoneroOnly) {
+ return DeviceConnectionType.supportedConnectionTypes(
+ WalletType.monero, HardwareWalletType.trezor, Platform.isIOS)
+ .isNotEmpty;
+ }
+
+ return true;
+ }
+
+ bool _bleIsInitialized = false;
+
+ Future _initBLE() async {
+ if (isBleEnabled && !_bleIsInitialized) {
+ trezorBLE = sdk.TrezorInterface.ble(
+ onPermissionRequest: (_) async {
+ if (Platform.isMacOS) return true;
+
+ Map 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;
+ }
+ }
+
+ @override
+ HardwareWalletType get hardwareWalletType => HardwareWalletType.trezor;
+
+ @override
+ @observable
+ bool isBleEnabled = false;
+
+ @override
+ bool get hasBluetooth => true;
+
+ @override
+ Future updateBleState() async {
+ final bleState = await sdk.UniversalBle.getBluetoothAvailabilityState();
+
+ final newState = bleState == sdk.AvailabilityState.poweredOn;
+
+ if (newState != isBleEnabled) isBleEnabled = newState;
+ }
+
+ @override
+ Stream scanForBleDevices() =>
+ trezorBLE.scan().map((d) => TrezorHardwareWalletDevice(d));
+
+ @override
+ Future> getAllUsbDevices() => trezorUSB.devices
+ .then((devices) => devices.map((d) => TrezorHardwareWalletDevice(d)).toList());
+
+ @override
+ Future stopScanning() async {
+ if (_bleIsInitialized) await trezorBLE.stopScanning();
+ if (!Platform.isIOS) await trezorUSB.stopScanning();
+ }
+
+ bool _isConnecting = false;
+ sdk.TrezorClient? _client;
+
+ Completer? _pinCompleter;
+
+ @observable
+ TrezorParingState paringState = TrezorParingState.initial;
+
+ void setParingPin(String pin) => _pinCompleter?.complete(pin);
+
+ @override
+ Future connectDevice(HardwareWalletDevice device, WalletType type,
+ [bool isRetry = false]) async {
+ if (!(device is TrezorHardwareWalletDevice)) return false;
+ if (_isConnecting) return false;
+ _isConnecting = true;
+ paringState = TrezorParingState.initial;
+
+ try {
+ final trezorInterface =
+ device.connectionType == HardwareWalletConnectionType.ble ? trezorBLE : trezorUSB;
+ final connection = await trezorInterface.connect(device.device);
+
+ if (!isRetry) {
+ showModalBottomSheet(
+ context: navigatorKey.currentContext!,
+ isScrollControlled: true,
+ isDismissible: false,
+ enableDrag: false,
+ useSafeArea: true,
+ builder: (context) =>
+ HardwareWalletProceedOnDeviceSheet(
+ hardwareWalletType: hardwareWalletType,
+ trezorConnectVM: this,
+ onRetry: () => connectDevice(device, type, true)
+ ),
+ );
+ }
+
+ Future onPinCode() async {
+ _pinCompleter = Completer();
+ paringState = TrezorParingState.enterPin;
+
+ final res = await _pinCompleter!.future;
+ paringState = TrezorParingState.verifyingPin;
+ if (res == null) throw Exception();
+ return res;
+ }
+
+ final deviceInfo = await _deviceName;
+
+ _client = sdk.TrezorClient.getClientForConnection(
+ connection,
+ _state,
+ "Cake Wallet",
+ deviceInfo,
+ onPinCode,
+ );
+
+ await _client!.createChannel();
+
+ paringState = TrezorParingState.success;
+ return true;
+ } catch (e) {
+ await _client?.connection.disconnect();
+ _client = null;
+ _state = sdk.ThpState();
+ // rethrow;
+ paringState = TrezorParingState.fail(e.toString());
+ printV(e);
+ return false;
+ } finally {
+ _isConnecting = false;
+ }
+ }
+
+ Future 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) => type == WalletType.monero
+ ? _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(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 initWallet(WalletBase wallet) async {
+ 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');
+ }
+ }
+}
+
+
+abstract class TrezorParingState {
+ static TrezorParingState initial = InitialTrezorParingState();
+ static TrezorParingState enterPin = EnterPinTrezorParingState();
+ static TrezorParingState verifyingPin = VerifyingPinTrezorParingState();
+ static TrezorParingState success = SuccessTrezorParingState();
+ static TrezorParingState fail(String message) => FailTrezorParingState(message);
+}
+
+class InitialTrezorParingState extends TrezorParingState {}
+
+class EnterPinTrezorParingState extends TrezorParingState {}
+
+class VerifyingPinTrezorParingState extends TrezorParingState {}
+
+class SuccessTrezorParingState extends TrezorParingState {}
+
+class FailTrezorParingState extends TrezorParingState {
+ final String message;
+
+ FailTrezorParingState(this.message);
+}
diff --git a/lib/view_model/hardware_wallet/trezor_view_model.dart b/lib/view_model/hardware_wallet/trezor_view_model.dart
deleted file mode 100644
index 8a56fb1a4..000000000
--- a/lib/view_model/hardware_wallet/trezor_view_model.dart
+++ /dev/null
@@ -1,79 +0,0 @@
-import 'dart:async';
-
-import 'package:cake_wallet/bitcoin/bitcoin.dart';
-import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
-import 'package:cake_wallet/evm/evm.dart';
-import 'package:cake_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 updateBleState() async {}
-
- @override
- Stream scanForBleDevices() => throw UnimplementedError();
-
- @override
- Future> getAllUsbDevices() async => [];
-
- @override
- Future stopScanning() async {}
-
- @override
- Future 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 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');
- }
- }
-}
diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart
index 35883f502..a51423ec5 100644
--- a/lib/view_model/send/send_view_model.dart
+++ b/lib/view_model/send/send_view_model.dart
@@ -619,7 +619,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
if (!(state is IsExecutingState)) state = IsExecutingState();
if (wallet.isHardwareWallet) {
- state = IsAwaitingDeviceResponseState();
if (walletType == WalletType.monero) {
_ledgerTxStateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
if (monero!.getLastLedgerCommand() == "INS_CLSAG") {
@@ -627,6 +626,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
state = IsDeviceSigningResponseState();
}
});
+ } else {
+ state = IsAwaitingDeviceResponseState();
}
}
@@ -970,8 +971,11 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
throw Exception("Pending transaction doesn't exist. It should not be happened.");
}
+
try {
- state = TransactionCommitting();
+ state = wallet.isHardwareWallet && walletType == WalletType.monero
+ ? IsAwaitingDeviceResponseState()
+ : TransactionCommitting();
if (ocpRequest != null) {
await _handleOcpRequest();
diff --git a/lib/view_model/wallet_hardware_restore_view_model.dart b/lib/view_model/wallet_hardware_restore_view_model.dart
index ac89b2adc..1baabd1b3 100644
--- a/lib/view_model/wallet_hardware_restore_view_model.dart
+++ b/lib/view_model/wallet_hardware_restore_view_model.dart
@@ -89,7 +89,7 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with
credentials = monero!.createMoneroRestoreWalletFromHardwareCredentials(
name: name,
- ledgerConnection: (hardwareWalletVM as LedgerViewModel).connection,
+ hardwareWalletService: hardwareWalletVM.getHardwareWalletService(type),
password: password,
height: _options['height'] as int? ?? 0,
);
diff --git a/pubspec_base.yaml b/pubspec_base.yaml
index a04b093d6..fb35f3582 100644
--- a/pubspec_base.yaml
+++ b/pubspec_base.yaml
@@ -125,7 +125,7 @@ dependencies:
ledger_flutter_plus:
git:
url: https://github.com/vespr-wallet/ledger-flutter-plus
- ref: 60817d4b20144f9da9029f5034790272795b9d38
+ ref: 0ccfbe26298e8974dfd4e891f34719c8348562f9
trezor_connect:
git:
url: https://github.com/cake-tech/trezor_connect
@@ -203,7 +203,7 @@ dependency_overrides:
ledger_flutter_plus:
git:
url: https://github.com/cake-tech/ledger-flutter-plus
- ref: 5237e5b3d5f07696eefd0aaee9c32478ed399d20
+ ref: 7bcb00380783f8611f68f5cd63fec96261fea2c2
ledger_usb_plus:
git:
url: https://github.com/konstantinullrich/ledger-usb-plus
@@ -227,6 +227,10 @@ dependency_overrides:
decimal: ^2.3.3
flutter_rust_bridge: 2.11.1
flutter_secure_storage_windows: 3.1.2
+ universal_ble:
+ git:
+ url: https://github.com/cake-tech/universal_ble.git
+ ref: f7044558ad111a156987614971e08c583c80175c
flutter_icons:
image_path: "assets/images/app_logo.png"
@@ -290,6 +294,7 @@ flutter:
- assets/new-ui/card_icons/outline_icons/
- assets/new-ui/card_icons/symbol_icons/
- assets/new-ui/crypto_full_icons/
+ - assets/new-ui/hardware_wallets/
- assets/new-ui/node_speed_badges/
fonts:
diff --git a/res/pictures/bluetooth.svg b/res/pictures/bluetooth.svg
new file mode 100644
index 000000000..ddee93cf7
--- /dev/null
+++ b/res/pictures/bluetooth.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/pictures/cake_coin.svg b/res/pictures/cake_coin.svg
new file mode 100644
index 000000000..03d468a3c
--- /dev/null
+++ b/res/pictures/cake_coin.svg
@@ -0,0 +1,60 @@
+
diff --git a/res/pictures/hardware_wallet.svg b/res/pictures/hardware_wallet.svg
new file mode 100644
index 000000000..56f7c8998
--- /dev/null
+++ b/res/pictures/hardware_wallet.svg
@@ -0,0 +1,191 @@
+
diff --git a/assets/images/hardware_wallet/device_bitbox.svg b/res/pictures/hardware_wallets/device_bitbox.svg
similarity index 87%
rename from assets/images/hardware_wallet/device_bitbox.svg
rename to res/pictures/hardware_wallets/device_bitbox.svg
index 52368aaec..fe41717d5 100644
--- a/assets/images/hardware_wallet/device_bitbox.svg
+++ b/res/pictures/hardware_wallets/device_bitbox.svg
@@ -1,3 +1,3 @@
diff --git a/res/pictures/hardware_wallets/device_ledger_flex.svg b/res/pictures/hardware_wallets/device_ledger_flex.svg
new file mode 100644
index 000000000..9c8e9f7d4
--- /dev/null
+++ b/res/pictures/hardware_wallets/device_ledger_flex.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/pictures/hardware_wallets/device_ledger_nano_gen_5.svg b/res/pictures/hardware_wallets/device_ledger_nano_gen_5.svg
new file mode 100644
index 000000000..fe1124e33
--- /dev/null
+++ b/res/pictures/hardware_wallets/device_ledger_nano_gen_5.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/pictures/hardware_wallets/device_ledger_nano_s.svg b/res/pictures/hardware_wallets/device_ledger_nano_s.svg
new file mode 100644
index 000000000..a8032746a
--- /dev/null
+++ b/res/pictures/hardware_wallets/device_ledger_nano_s.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/hardware_wallet/device_ledger_nano_x.svg b/res/pictures/hardware_wallets/device_ledger_nano_x.svg
similarity index 94%
rename from assets/images/hardware_wallet/device_ledger_nano_x.svg
rename to res/pictures/hardware_wallets/device_ledger_nano_x.svg
index c605249dc..538bb3fa3 100644
--- a/assets/images/hardware_wallet/device_ledger_nano_x.svg
+++ b/res/pictures/hardware_wallets/device_ledger_nano_x.svg
@@ -1,3 +1,3 @@
diff --git a/res/pictures/hardware_wallets/device_ledger_stax.svg b/res/pictures/hardware_wallets/device_ledger_stax.svg
new file mode 100644
index 000000000..270457001
--- /dev/null
+++ b/res/pictures/hardware_wallets/device_ledger_stax.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/pictures/hardware_wallets/device_trezor_model_one.svg b/res/pictures/hardware_wallets/device_trezor_model_one.svg
new file mode 100644
index 000000000..5d1c779c5
--- /dev/null
+++ b/res/pictures/hardware_wallets/device_trezor_model_one.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/pictures/hardware_wallets/device_trezor_model_t.svg b/res/pictures/hardware_wallets/device_trezor_model_t.svg
new file mode 100644
index 000000000..fb437c9ea
--- /dev/null
+++ b/res/pictures/hardware_wallets/device_trezor_model_t.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/pictures/hardware_wallets/device_trezor_safe_3.svg b/res/pictures/hardware_wallets/device_trezor_safe_3.svg
new file mode 100644
index 000000000..338c7275b
--- /dev/null
+++ b/res/pictures/hardware_wallets/device_trezor_safe_3.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/pictures/hardware_wallets/device_trezor_safe_5.svg b/res/pictures/hardware_wallets/device_trezor_safe_5.svg
new file mode 100644
index 000000000..5ee539f39
--- /dev/null
+++ b/res/pictures/hardware_wallets/device_trezor_safe_5.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/pictures/hardware_wallets/device_trezor_safe_7.svg b/res/pictures/hardware_wallets/device_trezor_safe_7.svg
new file mode 100644
index 000000000..8d442f4b6
--- /dev/null
+++ b/res/pictures/hardware_wallets/device_trezor_safe_7.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb
index f8828872e..f0b425652 100644
--- a/res/values/strings_en.arb
+++ b/res/values/strings_en.arb
@@ -74,6 +74,7 @@
"approve_tokens": "Approve tokens",
"apps": "Apps",
"arbiscan_history": "ArbiScan History",
+ "are_you_sure_exit": "Are you sure you want to exit?",
"arrive_in_this_address": "${currency} ${tag}will arrive in this address",
"ascending": "Ascending",
"ask_each_time": "Ask each time",
@@ -330,6 +331,7 @@
"deuro_savings_welcome_description": "dEURO is a fully decentralized, collateralized stablecoin pegged to the Euro — secure, versatile, and censorship-resistant without having to rely on oracles. 1 dEURO = 1 EURO.",
"deuro_tooltip_no_eth": "In order to enable dEURO, you must have ETH in your wallet.",
"deuro_tx_commited_content": "It might take a couple of seconds for the transaction to confirm and be reflected on screen",
+ "device_confirmation": "Device Confirmation",
"device_is_signing": "Device is signing",
"dfx_option_description": "Buy crypto with EUR & CHF. For retail and corporate customers in Europe",
"did_you_back_up_seeds": "Did you back up all your seeds?",
@@ -529,6 +531,7 @@
"how_to_connect": "How to Connect",
"how_to_use": "How to use",
"how_to_use_card": "How to use this card",
+ "hww_exit_desc": "This will ed the ongoing pairing process with your hardware device",
"i_understand": "I understand",
"icon_style": "Icon style",
"id": "ID: ",
@@ -722,6 +725,10 @@
"overshot": "Overshot",
"overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "Pairing Invalid Event",
+ "pairing_code": "Pairing Code",
+ "pairing_code_desc_1": "Your device will show a pairing code.",
+ "pairing_code_desc_2": "Input here to continue",
+ "pairing_error": "Pairing Error",
"passphrase": "Passphrase (Optional)",
"passphrase_view_keys": "Passphrase",
"passphrases_doesnt_match": "Passphrases do not match, please try again",
@@ -1138,6 +1145,7 @@
"testnet_coins_no_value": "Testnet coins have no value",
"third_intro_content": "Yats live outside of Cake Wallet, too. Any wallet address on earth can be replaced with a Yat!",
"third_intro_title": "Yat plays nicely with others",
+ "this_can_take_few_seconds": "This can take a few seconds",
"this_pair_is_not_supported_warning": "This pair is not supported with the currently selected exchange(s). Please select another exchange.",
"thorchain_contract_address_not_supported": "THORChain does not support sending to a contract address",
"thorchain_taproot_address_not_supported": "The THORChain provider does not support Taproot addresses. Please change the address or select a different provider.",
@@ -1287,6 +1295,7 @@
"value_type": "Value Type",
"variable_pair_not_supported": "This variable pair is not supported with the selected exchanges",
"verification": "Verification",
+ "verifying_code": "Verifying Code",
"verify_message": "Verify Message",
"verify_seed": "Verify Seed",
"verify_with_2fa": "Verify with Cake 2FA",
@@ -1394,6 +1403,7 @@
"yat_popup_title": "Your wallet address can be emojified.",
"yes": "Yes",
"yesterday": "Yesterday",
+ "yes_exit": "Yes. Exit",
"you_now_have_debit_card": "You now have a debit card",
"you_pay": "You Pay",
"you_will_get": "Convert to",
@@ -1408,4 +1418,4 @@
"zcash_card_missing_funds": "Missing funds?",
"zcash_card_scan": "Scan",
"zcash_card_warning": "Do not close the app until the procedure completes, if you do so this process will need to restart from scratch."
-}
\ No newline at end of file
+}
diff --git a/scripts/prepare_moneroc.sh b/scripts/prepare_moneroc.sh
index 2d7b06f0c..0a328f5f3 100755
--- a/scripts/prepare_moneroc.sh
+++ b/scripts/prepare_moneroc.sh
@@ -14,9 +14,9 @@ else
fi
# NOTE: Make sure to update monero_c prebuilds link in workflow files
-# https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
+# https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC2/release-bundle.zip
git fetch -a
-git checkout bc8d1a0b75b97156d71579581b4cdfe58c777ed2
+git checkout 3bfb3856a838f2bf6b729501837bb0295dedf25d
git reset --hard
git submodule update --init --force --recursive
diff --git a/tool/configure.dart b/tool/configure.dart
index 80856320d..6a5385f86 100644
--- a/tool/configure.dart
+++ b/tool/configure.dart
@@ -334,8 +334,10 @@ import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
+import 'package:trezor_flutter/trezor_flutter.dart' as trezor;
import 'package:polyseed/polyseed.dart';""";
const moneroCWHeaders = """
+import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/account.dart' as monero_account;
import 'package:cw_core/get_height_by_date.dart';
import 'package:cw_core/monero_amount_format.dart';
@@ -345,6 +347,7 @@ import 'package:cw_monero/api/wallet.dart' as monero_wallet_api;
import 'package:cw_monero/ledger.dart';
import 'package:cw_monero/monero_unspent.dart';
import 'package:cw_monero/api/account_list.dart';
+import 'package:cw_monero/trezor.dart';
import 'package:cw_monero/monero_wallet_service.dart';
import 'package:cw_monero/monero_wallet.dart';
import 'package:cw_monero/monero_transaction_info.dart';
@@ -463,7 +466,7 @@ abstract class Monero {
HardwareWalletType? hardwareWalletType,
required int height});
WalletCredentials createMoneroRestoreWalletFromSeedCredentials({required String name, required String password, required String passphrase, required int height, required String mnemonic});
- WalletCredentials createMoneroRestoreWalletFromHardwareCredentials({required String name, required String password, required int height, required ledger.LedgerConnection ledgerConnection});
+ WalletCredentials createMoneroRestoreWalletFromHardwareCredentials({required String name, required String password, required int height, required HardwareWalletService hardwareWalletService});
WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required int seedType, required String? passphrase, String? password, String? mnemonic});
Map getKeys(Object wallet);
int? getRestoreHeight(Object wallet);
@@ -484,6 +487,10 @@ WalletCredentials createMoneroNewWalletCredentials({required String name, requir
void resetLedgerConnection();
void setGlobalLedgerConnection(ledger.LedgerConnection connection);
String? getLastLedgerCommand();
+ void setHardwareWalletService(Object wallet, HardwareWalletService service);
+ HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);
+ HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorClient client);
+ Future syncTrezor(Object wallet);
Map> debugCallLength();
Map getWalletCacheDebug();
}