diff --git a/README.md b/README.md
index 4e1d2a13..b212ffe7 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,11 @@
A Flutter Wallet for Real Unit Investors.
## Getting Started
+Before getting started, please make sure you have the latest version of Flutter, golang and gomobile installed.
+```shell
+go install golang.org/x/mobile/cmd/gomobile@latest
+gomobile init
+```
### 1. Generate translations
@@ -26,4 +31,4 @@ flutter pub get
```shell
flutter run
-```
\ No newline at end of file
+```
diff --git a/assets/images/illustrations/bitbox_connect.svg b/assets/images/illustrations/bitbox_connect.svg
new file mode 100644
index 00000000..18842b9d
--- /dev/null
+++ b/assets/images/illustrations/bitbox_connect.svg
@@ -0,0 +1,44 @@
+
diff --git a/assets/images/illustrations/bitbox_connected.svg b/assets/images/illustrations/bitbox_connected.svg
new file mode 100644
index 00000000..427d1d92
--- /dev/null
+++ b/assets/images/illustrations/bitbox_connected.svg
@@ -0,0 +1,42 @@
+
diff --git a/assets/languages/strings_de.arb b/assets/languages/strings_de.arb
index 05f13647..d639558c 100644
--- a/assets/languages/strings_de.arb
+++ b/assets/languages/strings_de.arb
@@ -17,10 +17,16 @@
"buy_payment_not_possible": "Kauf momentan nicht möglich.",
"buy_payment_not_possible_description": "Es scheint ein Berechtigungsproblem vorzuliegen. Kontaktieren Sie bitte den Support für weitere Informationen.",
"buy_realu": "REALU kaufen",
+ "cancel": "Abbrechen",
"close": "Schließen",
"collect_interest": "Auszahlen",
"confirm": "Ausführen",
+ "connect_bitbox_content": "Bitte verbinden Sie Ihre BitBox02 mit Ihrem Smartphone.",
+ "connect_bitbox_content_ios": "Bitte verbinden Sie Ihre BitBox02 mit Ihrem Smartphone und aktivieren Sie zusätzlich Bluetooth.",
+ "connect_bitbox_title": "BitBox02 verbinden",
"connect_with": "Mit ${wallet} verbinden",
+ "connected_bitbox_content": "Bitte folgen Sie nun den Anweisungen auf Ihrer BitBox02.",
+ "connected_bitbox_title": "Verbindung erfolgreich",
"contact_support": "Support kontaktieren",
"copy_seed": "Seed kopieren",
"country": "Land",
diff --git a/assets/languages/strings_en.arb b/assets/languages/strings_en.arb
index f6158ca8..86e05fdd 100644
--- a/assets/languages/strings_en.arb
+++ b/assets/languages/strings_en.arb
@@ -17,10 +17,16 @@
"buy_payment_not_possible": "Purchase currently not possible.",
"buy_payment_not_possible_description": "There appears to be a permission issue. Please contact support for further information.",
"buy_realu": "Buy REALU",
+ "cancel": "Cancel",
"close": "Close",
"collect_interest": "Collect",
"confirm": "Confirm",
+ "connect_bitbox_content": "Please connect your BitBox02 with your Smartphone.",
+ "connect_bitbox_content_ios": "Please connect your BitBox02 with your Smartphone and activate Bluetooth.",
+ "connect_bitbox_title": "Connect BitBox02",
"connect_with": "Connect with ${wallet}",
+ "connected_bitbox_content": "Please follow the steps on your BitBox02.",
+ "connected_bitbox_title": "Connection successful",
"contact_support": "Contact support",
"copy_seed": "Copy seed",
"country": "Country",
diff --git a/lib/di.dart b/lib/di.dart
index 78d50be3..f6c011f3 100644
--- a/lib/di.dart
+++ b/lib/di.dart
@@ -1,6 +1,7 @@
import 'dart:io';
import 'package:get_it/get_it.dart';
+import 'package:realunit_wallet/packages/hardware_wallet/bitbox.dart';
import 'package:realunit_wallet/packages/open_crypto_pay/open_crypto_pay_service.dart';
import 'package:realunit_wallet/packages/repository/asset_repository.dart';
import 'package:realunit_wallet/packages/repository/balance_repository.dart';
@@ -76,19 +77,23 @@ void setupRepositories() {
getIt.registerFactory(() => BalanceRepository(getIt()));
getIt.registerFactory(() => AssetRepository(getIt()));
getIt.registerFactory(() => NodeRepository(getIt()));
- getIt
- .registerFactory(() => TransactionRepository(getIt(), getIt()));
+ getIt.registerFactory(() =>
+ TransactionRepository(getIt(), getIt()));
}
void setupServices() {
- getIt
- .registerFactory(() => WalletService(getIt(), getIt()));
+ getIt.registerSingleton(BalanceService(
+ getIt(), getIt(), getIt()));
- getIt.registerSingleton(
- BalanceService(getIt(), getIt(), getIt()));
+ getIt.registerSingleton(BitboxService());
+ getIt.registerFactory(() => WalletService(
+ getIt(),
+ getIt(),
+ getIt(),
+ ));
- getIt.registerFactory(() => TransactionHistoryService(
- getIt(), getIt(), getIt()));
+ getIt.registerFactory(() => TransactionHistoryService(getIt(),
+ getIt(), getIt()));
getIt.registerFactory(() => OpenCryptoPayService());
getIt.registerFactory(() => DFXPriceService(getIt()));
@@ -97,8 +102,8 @@ void setupServices() {
getIt.registerFactory(() => DfxBrokerbotService(getIt()));
getIt.registerFactory(() => SettingsService(getIt()));
- getIt.registerFactory(
- () => DFXService(getIt(), getIt(), getIt()));
+ getIt.registerFactory(() => DFXService(getIt(),
+ getIt(), getIt()));
}
void setupBlocs() {
@@ -112,4 +117,5 @@ void setupBlocs() {
));
}
-Future _existsDatabaseFile() async => File(await AppDatabase.getDatabasePath()).exists();
+Future _existsDatabaseFile() async =>
+ File(await AppDatabase.getDatabasePath()).exists();
diff --git a/lib/main.dart b/lib/main.dart
index 3b86faeb..58c0aa7f 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -81,7 +81,8 @@ class _WalletAppState extends State {
void _onDetached() => developer.log('detached');
void _onResumed() {
- getIt().updateERC20Balances(getIt().primaryAddress);
+ getIt()
+ .updateERC20Balances(getIt().primaryAddress);
}
void _onInactive() => developer.log('inactive', name: 'AppLifecycleListener');
diff --git a/lib/packages/hardware_wallet/bitbox.dart b/lib/packages/hardware_wallet/bitbox.dart
new file mode 100644
index 00000000..a16ed382
--- /dev/null
+++ b/lib/packages/hardware_wallet/bitbox.dart
@@ -0,0 +1,26 @@
+import 'package:bitbox_flutter/bitbox_flutter.dart' as sdk;
+import 'package:realunit_wallet/packages/hardware_wallet/bitbox_credentials.dart';
+
+class BitboxService {
+ BitboxService() {
+ bitboxManager = sdk.BitboxManager();
+ }
+
+ late sdk.BitboxManager bitboxManager;
+
+ Future> getAllUsbDevices() => bitboxManager.devices;
+
+ BitboxCredentials getCredentials(String address) =>
+ BitboxCredentials(address)..setBitbox(bitboxManager);
+
+ Future connectDevice(sdk.BitboxDevice device) async {
+ await bitboxManager.connect(device);
+ final didInit = await bitboxManager.initBitBox();
+
+ if (!didInit) throw Exception("Failed to init");
+
+ final didVerify = await bitboxManager.channelHashVerify();
+
+ if (!didVerify) throw Exception("Failed to verify");
+ }
+}
diff --git a/lib/packages/hardware_wallet/bitbox_credentials.dart b/lib/packages/hardware_wallet/bitbox_credentials.dart
new file mode 100644
index 00000000..3426cf94
--- /dev/null
+++ b/lib/packages/hardware_wallet/bitbox_credentials.dart
@@ -0,0 +1,78 @@
+import 'dart:async';
+import 'dart:typed_data';
+
+import 'package:bitbox_flutter/bitbox_manager.dart';
+import 'package:web3dart/crypto.dart';
+import 'package:web3dart/web3dart.dart';
+
+class BitboxCredentials extends CredentialsWithKnownAddress {
+ final String _address;
+
+ BitboxManager? bitboxManager;
+ String? derivationPath;
+
+ BitboxCredentials(this._address);
+
+ @override
+ EthereumAddress get address => EthereumAddress.fromHex(_address);
+
+ void setBitbox(BitboxManager connection, [String? derivationPath_]) {
+ bitboxManager = connection;
+ derivationPath = derivationPath_ ?? "m/44'/60'/0'/0/0";
+ }
+
+ @override
+ MsgSignature signToEcSignature(Uint8List payload, {int? chainId, bool isEIP1559 = false}) =>
+ throw UnimplementedError("EvmLedgerCredentials.signToEcSignature");
+
+ @override
+ Future signToSignature(Uint8List payload,
+ {int? chainId, bool isEIP1559 = false}) async {
+ if (bitboxManager == null) {
+ throw Exception("Bitbox not connected");
+ }
+
+ if (isEIP1559) payload = payload.sublist(1);
+ final sig = await bitboxManager!
+ .signETHRLPTransaction(chainId ?? 1, derivationPath!, bytesToHex(payload), isEIP1559);
+
+ final r = bytesToHex(sig.sublist(0, 32));
+ final s = bytesToHex(sig.sublist(32, 32 + 32));
+ final v = sig.last.toInt();
+
+ if (isEIP1559) {
+ return MsgSignature(BigInt.parse(r, radix: 16), BigInt.parse(s, radix: 16), v);
+ }
+
+ var truncChainId = chainId ?? 1;
+ while (truncChainId.bitLength > 32) {
+ truncChainId >>= 8;
+ }
+
+ final truncTarget = truncChainId * 2 + 35;
+
+ int parity = v;
+ if (truncTarget & 0xff == v) {
+ parity = 0;
+ } else if ((truncTarget + 1) & 0xff == v) {
+ parity = 1;
+ }
+
+ // https://github.com/ethereumjs/ethereumjs-util/blob/8ffe697fafb33cefc7b7ec01c11e3a7da787fe0e/src/signature.ts#L26
+ final chainIdV = chainId != null ? (parity + (chainId * 2 + 35)) : parity;
+
+ return MsgSignature(BigInt.parse(r, radix: 16), BigInt.parse(s, radix: 16), chainIdV);
+ }
+
+ @override
+ Future signPersonalMessage(Uint8List payload, {int? chainId}) async {
+ if (isNotConnected) throw Exception("Bitbox not connected");
+ return await bitboxManager!.signETHMessage(chainId ?? 1, derivationPath!, payload);
+ }
+
+ @override
+ Uint8List signPersonalMessageToUint8List(Uint8List payload, {int? chainId}) =>
+ throw UnimplementedError("EvmLedgerCredentials.signPersonalMessageToUint8List");
+
+ bool get isNotConnected => bitboxManager == null;
+}
diff --git a/lib/packages/repository/wallet_repository.dart b/lib/packages/repository/wallet_repository.dart
index 4f2e31bf..4748d25d 100644
--- a/lib/packages/repository/wallet_repository.dart
+++ b/lib/packages/repository/wallet_repository.dart
@@ -1,16 +1,19 @@
import 'package:realunit_wallet/packages/storage/database.dart';
import 'package:realunit_wallet/packages/storage/wallet_storage.dart';
+import 'package:realunit_wallet/packages/wallet/wallet.dart';
class WalletRepository {
final AppDatabase _appDatabase;
const WalletRepository(this._appDatabase);
- Future createWallet(String name, String seed) =>
- _appDatabase.insertWallet(name, seed);
+ Future createWallet(String name, WalletType type, String seed) =>
+ _appDatabase.insertWallet(name, seed, "", type.index);
- Future getWalletById(int id) =>
- _appDatabase.getWalletById(id);
+ Future createViewWallet(String name, WalletType type, String address) =>
+ _appDatabase.insertWallet(name, "", address, type.index);
+
+ Future getWalletById(int id) => _appDatabase.getWalletById(id);
Future deleteWallet(int id) => _appDatabase.deleteWallet(id);
}
diff --git a/lib/packages/service/app_store.dart b/lib/packages/service/app_store.dart
index af72f9b2..99d5a5c2 100644
--- a/lib/packages/service/app_store.dart
+++ b/lib/packages/service/app_store.dart
@@ -22,7 +22,7 @@ class AppStore {
_nodes = await nodeRepository.allNodes;
}
- String get primaryAddress => wallet.currentAccount.primaryAddress.address.hexEip55;
+ String get primaryAddress => wallet.currentAccount.primaryAddress.address.hex;
web3.Web3Client getClient(int chainId) {
final node = _nodes.firstWhere(
diff --git a/lib/packages/service/wallet_service.dart b/lib/packages/service/wallet_service.dart
index f58c4197..8c3223f0 100644
--- a/lib/packages/service/wallet_service.dart
+++ b/lib/packages/service/wallet_service.dart
@@ -1,4 +1,5 @@
import 'package:bip39/bip39.dart' as bip39;
+import 'package:realunit_wallet/packages/hardware_wallet/bitbox.dart';
import 'package:realunit_wallet/packages/repository/settings_repository.dart';
import 'package:realunit_wallet/packages/repository/wallet_repository.dart';
import 'package:realunit_wallet/packages/wallet/wallet.dart';
@@ -6,26 +7,41 @@ import 'package:realunit_wallet/packages/wallet/wallet.dart';
class WalletService {
final WalletRepository _repository;
final SettingsRepository _settingsRepository;
+ final BitboxService _bitboxService;
- const WalletService(this._repository, this._settingsRepository);
+ const WalletService(this._bitboxService, this._repository, this._settingsRepository);
- Future createWallet(String name) {
+ Future createSeedWallet(String name) {
final mnemonic = bip39.generateMnemonic();
return restoreWallet(name, mnemonic);
}
- Future restoreWallet(String name, String seed) async {
- final walletId = await _repository.createWallet(name, seed);
+ Future createBitboxWallet(String name) async {
+ final address = await _bitboxService.bitboxManager.getETHAddress(1, "m/44'/60'/0'/0/0");
+ final walletId = await _repository.createViewWallet(name, WalletType.bitbox, address);
await _settingsRepository.saveCurrentWalletId(walletId);
- return Wallet(walletId, name, seed);
+ return BitboxWallet(walletId, name, address, _bitboxService);
}
- Future getWalletById(int id) async {
+ Future restoreWallet(String name, String seed) async {
+ final walletId = await _repository.createWallet(name, WalletType.software, seed);
+ await _settingsRepository.saveCurrentWalletId(walletId);
+ return SoftwareWallet(walletId, name, seed);
+ }
+
+ Future getWalletById(int id) async {
final result = (await _repository.getWalletById(id))!;
- return Wallet(result.id, result.name, result.seed);
+ final walletType = WalletType.values[result.type];
+ switch (walletType) {
+ case WalletType.software:
+ return SoftwareWallet(result.id, result.name, result.seed);
+ case WalletType.bitbox:
+ return BitboxWallet(
+ result.id, result.name, result.address, _bitboxService);
+ }
}
- Future getCurrentWallet() async {
+ Future getCurrentWallet() async {
final id = _settingsRepository.currentWalletId!;
return getWalletById(id);
}
diff --git a/lib/packages/storage/database.dart b/lib/packages/storage/database.dart
index dca63ad1..e60cba14 100644
--- a/lib/packages/storage/database.dart
+++ b/lib/packages/storage/database.dart
@@ -52,7 +52,7 @@ class AppDatabase extends _$AppDatabase {
: super(_openDatabase(encryptionPassword));
@override
- int get schemaVersion => 2;
+ int get schemaVersion => 1;
@override
MigrationStrategy get migration => MigrationStrategy(
@@ -60,9 +60,7 @@ class AppDatabase extends _$AppDatabase {
await m.createAll();
},
onUpgrade: (Migrator m, int from, int to) async {
- if (from < 2) {
- await m.createTable(keyValueCache);
- }
+
},
);
diff --git a/lib/packages/storage/wallet_storage.dart b/lib/packages/storage/wallet_storage.dart
index 974143bc..5f52b4fb 100644
--- a/lib/packages/storage/wallet_storage.dart
+++ b/lib/packages/storage/wallet_storage.dart
@@ -1,9 +1,11 @@
-import 'package:realunit_wallet/packages/storage/database.dart';
import 'package:drift/drift.dart';
+import 'package:realunit_wallet/packages/storage/database.dart';
extension WalletStorage on AppDatabase {
- Future insertWallet(String name, String seed) => into(walletInfos)
- .insert(WalletInfosCompanion.insert(name: name, seed: seed));
+ Future insertWallet(
+ String name, String seed, String address, int walletType) =>
+ into(walletInfos).insert(WalletInfosCompanion.insert(
+ name: name, seed: seed, address: address, type: walletType));
Future getWalletById(int id) =>
(select(walletInfos)..where((row) => row.id.equals(id)))
@@ -33,6 +35,10 @@ class WalletInfos extends Table {
TextColumn get name => text()();
TextColumn get seed => text()();
+
+ TextColumn get address => text()();
+
+ IntColumn get type => integer()();
}
@DataClassName("WalletAccountInfo")
diff --git a/lib/packages/utils/device_info.dart b/lib/packages/utils/device_info.dart
index c5295b0d..0b15674d 100644
--- a/lib/packages/utils/device_info.dart
+++ b/lib/packages/utils/device_info.dart
@@ -1,12 +1,12 @@
-import 'dart:io';
+import 'package:flutter/foundation.dart';
class DeviceInfo {
DeviceInfo._();
static DeviceInfo get instance => DeviceInfo._();
- bool get isMobile => Platform.isAndroid || Platform.isIOS;
+ bool get isMobile => [TargetPlatform.android, TargetPlatform.iOS].contains(defaultTargetPlatform);
- bool get isDesktop =>
- Platform.isMacOS || Platform.isWindows || Platform.isLinux;
+ bool get isDesktop => [TargetPlatform.macOS, TargetPlatform.windows, TargetPlatform.linux]
+ .contains(defaultTargetPlatform);
}
diff --git a/lib/packages/wallet/wallet.dart b/lib/packages/wallet/wallet.dart
index e998dc92..b35fb5f2 100644
--- a/lib/packages/wallet/wallet.dart
+++ b/lib/packages/wallet/wallet.dart
@@ -1,5 +1,6 @@
import 'package:bip32/bip32.dart';
import 'package:bip39/bip39.dart';
+import 'package:realunit_wallet/packages/hardware_wallet/bitbox.dart';
import 'package:realunit_wallet/packages/wallet/wallet_account.dart';
enum WalletType { software, bitbox }
@@ -16,7 +17,7 @@ abstract class AWallet {
AWallet(this.id, this.name);
}
-class Wallet extends AWallet {
+class SoftwareWallet extends AWallet {
@override
WalletType get walletType => WalletType.software;
@@ -31,7 +32,7 @@ class Wallet extends AWallet {
@override
WalletAccount get currentAccount => _currentAccount;
- Wallet(super.id, super.name, this.seed) {
+ SoftwareWallet(super.id, super.name, this.seed) {
final seedBytes = mnemonicToSeed(seed);
_bip32 = BIP32.fromSeed(seedBytes);
primaryAccount = WalletAccount(_bip32, 0);
@@ -40,3 +41,24 @@ class Wallet extends AWallet {
void selectAccount(int index) => _currentAccount = WalletAccount(_bip32, index);
}
+
+class BitboxWallet extends AWallet {
+ @override
+ WalletType get walletType => WalletType.bitbox;
+
+ final BitboxService _bitboxService;
+
+ @override
+ late final BitboxWalletAccount primaryAccount;
+
+ late BitboxWalletAccount _currentAccount;
+
+ @override
+ BitboxWalletAccount get currentAccount => _currentAccount;
+
+ BitboxWallet(super.id, super.name, String address, this._bitboxService) {
+ primaryAccount = BitboxWalletAccount(0, _bitboxService.getCredentials(
+ address));
+ _currentAccount = primaryAccount;
+ }
+}
diff --git a/lib/packages/wallet/wallet_account.dart b/lib/packages/wallet/wallet_account.dart
index 75ab41cb..ff12dce5 100644
--- a/lib/packages/wallet/wallet_account.dart
+++ b/lib/packages/wallet/wallet_account.dart
@@ -34,3 +34,11 @@ class WalletAccount extends AWalletAccount {
Future signMessage(String message, {int addressIndex = 0}) async =>
"0x${hex.encode(_getPrivateKeyAt(root, addressIndex, addressIndex).signPersonalMessageToUint8List(ascii.encode(message)))}";
}
+
+class BitboxWalletAccount extends AWalletAccount {
+ BitboxWalletAccount(super.accountIndex, super.primaryAddress);
+
+ @override
+ Future signMessage(String message, {int addressIndex = 0}) async =>
+ "0x${hex.encode(await primaryAddress.signPersonalMessage(ascii.encode(message)))}";
+}
diff --git a/lib/screens/create_wallet/bloc/create_wallet_cubit.dart b/lib/screens/create_wallet/bloc/create_wallet_cubit.dart
index ce60c847..1cf86712 100644
--- a/lib/screens/create_wallet/bloc/create_wallet_cubit.dart
+++ b/lib/screens/create_wallet/bloc/create_wallet_cubit.dart
@@ -10,7 +10,7 @@ class CreateWalletCubit extends Cubit {
final WalletService _service;
void createWallet() async {
- final wallet = await _service.createWallet("Obi-Wallet-Kenobi");
+ final wallet = await _service.createSeedWallet("Obi-Wallet-Kenobi");
emit(state.copyWith(wallet: wallet));
}
diff --git a/lib/screens/create_wallet/bloc/create_wallet_state.dart b/lib/screens/create_wallet/bloc/create_wallet_state.dart
index 055230cd..d5d776b0 100644
--- a/lib/screens/create_wallet/bloc/create_wallet_state.dart
+++ b/lib/screens/create_wallet/bloc/create_wallet_state.dart
@@ -4,11 +4,11 @@ final class CreateWalletState {
const CreateWalletState({this.hideSeed = true, this.wallet});
final bool hideSeed;
- final Wallet? wallet;
+ final SoftwareWallet? wallet;
CreateWalletState copyWith({
bool? hideSeed,
- Wallet? wallet,
+ SoftwareWallet? wallet,
}) =>
CreateWalletState(
hideSeed: hideSeed ?? this.hideSeed,
diff --git a/lib/screens/dashboard/widgets/section_balance.dart b/lib/screens/dashboard/widgets/section_balance.dart
index f26d3144..7fd5a9d5 100644
--- a/lib/screens/dashboard/widgets/section_balance.dart
+++ b/lib/screens/dashboard/widgets/section_balance.dart
@@ -1,24 +1,21 @@
import 'dart:developer' as developer;
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:go_router/go_router.dart';
import 'package:realunit_wallet/di.dart';
import 'package:realunit_wallet/generated/i18n.dart';
import 'package:realunit_wallet/packages/open_crypto_pay/exceptions.dart';
import 'package:realunit_wallet/packages/open_crypto_pay/open_crypto_pay_service.dart';
import 'package:realunit_wallet/packages/service/dfx/dfx_service.dart';
import 'package:realunit_wallet/packages/wallet/payment_uri.dart';
-import 'package:realunit_wallet/screens/receive/receive_page.dart';
import 'package:realunit_wallet/screens/send/send_page.dart';
import 'package:realunit_wallet/screens/settings/bloc/settings_bloc.dart';
import 'package:realunit_wallet/styles/colors.dart';
-import 'package:realunit_wallet/styles/styles.dart';
+import 'package:realunit_wallet/styles/icons.dart';
import 'package:realunit_wallet/widgets/action_button.dart';
import 'package:realunit_wallet/widgets/hide_amount_text.dart';
import 'package:realunit_wallet/widgets/qr_scanner.dart';
-import 'package:realunit_wallet/widgets/vertical_icon_button.dart';
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:go_router/go_router.dart';
class SectionBalance extends StatelessWidget {
final BigInt balance;
@@ -37,110 +34,90 @@ class SectionBalance extends StatelessWidget {
});
@override
- Widget build(BuildContext context) => Container(
- width: double.infinity,
- color: RealUnitColors.realUnitBlue,
- child: SafeArea(
- child: Column(
- children: [
- Padding(
- padding: EdgeInsets.only(left: 16, right: 16, top: 10),
- child: Row(
- children: [
- if (isFiatServiceAvailable) ...[
- Padding(
- padding: EdgeInsets.only(right: 10),
- child: ActionButton(
- icon: Icons.credit_card,
- label: S.of(context).deposit,
- onPressed: () =>
- getIt().launchProvider(context, true),
- buttonStyle: kBalanceBarActionButtonStyle,
- ),
- ),
- ActionButton(
- icon: Icons.account_balance,
- label: S.of(context).withdraw,
- onPressed: () =>
- getIt().launchProvider(context, false),
- buttonStyle: kBalanceBarActionButtonStyle,
- ),
- ],
- ],
- ),
- ),
- Padding(
- padding: EdgeInsets.only(top: 12, bottom: 12),
- child: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- S.of(context).balance,
- style: TextStyle(
- fontSize: 14,
- color: Colors.white.withAlpha(153),
+ Widget build(BuildContext context) => Column(children: [
+ Container(
+ width: double.infinity,
+ color: RealUnitColors.realUnitBlue,
+ child: SafeArea(
+ child: Column(
+ children: [
+ Padding(
+ padding: EdgeInsets.only(top: 12, bottom: 12),
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ S.of(context).balance,
+ style: TextStyle(
+ fontSize: 14,
+ color: Colors.white.withAlpha(153),
+ ),
),
- ),
- InkWell(
- onTap: onHideAmountPress,
- enableFeedback: false,
- child: Padding(
- padding: EdgeInsets.only(left: 5),
- child: BlocBuilder(
- builder: (context, state) => Icon(
- state.hideAmounts
- ? Icons.visibility_off
- : Icons.visibility,
- size: 14,
- color: Colors.white.withAlpha(153),
+ InkWell(
+ onTap: onHideAmountPress,
+ enableFeedback: false,
+ child: Padding(
+ padding: EdgeInsets.only(left: 5),
+ child: BlocBuilder(
+ builder: (context, state) => Icon(
+ state.hideAmounts
+ ? Icons.visibility_off
+ : Icons.visibility,
+ size: 14,
+ color: Colors.white.withAlpha(153),
+ ),
),
),
),
- ),
- ],
- ),
- HideAmountText(
- amount: balance,
- style: const TextStyle(
- fontSize: 35,
- color: Colors.white,
- fontFamily: "Satoshi Bold"),
- textAlign: TextAlign.center,
- ),
- ],
- ),
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- VerticalIconButton(
- onPressed: () => showCupertinoSheet(
- context: context, pageBuilder: (_) => ReceivePage()),
- icon: const Icon(Icons.arrow_downward, color: Colors.white),
- label: S.of(context).receive,
+ ],
+ ),
+ HideAmountText(
+ amount: balance,
+ style: const TextStyle(
+ fontSize: 35,
+ color: Colors.white,
+ fontFamily: "Satoshi Bold"),
+ textAlign: TextAlign.center,
+ ),
+ ],
),
- Padding(
- padding: EdgeInsets.only(left: 15, right: 15),
- child: VerticalIconButton.extended(
- onPressed: () => _presentQRReader(context),
- icon: const Icon(Icons.qr_code, color: Colors.white),
- label: S.of(context).pay_scan,
- ),
+ ),
+ const SizedBox(height: 20),
+ ],
+ ),
+ ),
+ ),
+ Padding(
+ padding: EdgeInsets.only(left: 16, right: 16, top: 10),
+ child: Row(
+ children: [
+ if (isFiatServiceAvailable) ...[
+ Padding(
+ padding: EdgeInsets.only(right: 10),
+ child: ActionButton(
+ icon: RealUnitTokenIcon(size: 20),
+ label: S.of(context).deposit,
+ onPressed: () =>
+ getIt().launchProvider(context, true),
),
- VerticalIconButton(
- onPressed: () => context.push("/send"),
- icon: const Icon(Icons.arrow_upward, color: Colors.white),
- label: S.of(context).send,
+ ),
+ ActionButton(
+ icon: Icon(
+ Icons.account_balance,
+ color: Colors.white,
+ size: 20,
),
- ],
- ),
- const SizedBox(height: 20),
+ label: S.of(context).withdraw,
+ onPressed: () =>
+ getIt().launchProvider(context, false),
+ ),
+ ],
],
),
),
- );
+ ]);
Future _presentQRReader(BuildContext context) async {
QRData? result = await presentQRScanner(
diff --git a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart
new file mode 100644
index 00000000..d99df6e8
--- /dev/null
+++ b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart
@@ -0,0 +1,50 @@
+import 'dart:async';
+
+import 'package:bitbox_flutter/bitbox_flutter.dart' as sdk;
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:realunit_wallet/packages/hardware_wallet/bitbox.dart';
+import 'package:realunit_wallet/packages/service/wallet_service.dart';
+import 'package:realunit_wallet/packages/wallet/wallet.dart';
+
+part 'connect_bitbox_state.dart';
+
+class ConnectBitboxCubit extends Cubit {
+ ConnectBitboxCubit(this._service, this._walletService)
+ : super(BitboxNotConnected()) {
+ _checkForTimer =
+ Timer.periodic(Duration(milliseconds: 500), (_) => checkForBitbox());
+ }
+
+ final BitboxService _service;
+ final WalletService _walletService;
+ Timer? _checkForTimer;
+
+ Future checkForBitbox() async {
+ final devices = await _service.getAllUsbDevices();
+ if (devices.isNotEmpty) {
+ emit(BitboxFound(devices.first));
+ _checkForTimer?.cancel();
+ connectToBitbox(devices.first);
+ }
+ }
+
+ Future connectToBitbox(sdk.BitboxDevice device) async {
+ if (state is BitboxConnecting) return;
+ emit(BitboxConnecting(device));
+ try {
+ await _service.connectDevice(device);
+ final wallet = await _walletService.createBitboxWallet("Luke-Skywallet");
+ emit(BitboxConnected(wallet));
+ } catch (_) {
+ emit(BitboxNotConnected());
+ _checkForTimer =
+ Timer.periodic(Duration(milliseconds: 30), (_) => checkForBitbox());
+ }
+ }
+
+ @override
+ Future close() async {
+ _checkForTimer?.cancel();
+ super.close();
+ }
+}
diff --git a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_state.dart b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_state.dart
new file mode 100644
index 00000000..6a79d6e6
--- /dev/null
+++ b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_state.dart
@@ -0,0 +1,21 @@
+part of 'connect_bitbox_cubit.dart';
+
+abstract class BitboxConnectionState {}
+
+class BitboxNotConnected extends BitboxConnectionState {}
+
+class BitboxFound extends BitboxConnectionState {
+ final sdk.BitboxDevice device;
+
+ BitboxFound(this.device);
+}
+
+class BitboxConnecting extends BitboxFound {
+ BitboxConnecting(super.device);
+}
+
+class BitboxConnected extends BitboxConnectionState {
+ final BitboxWallet wallet;
+
+ BitboxConnected(this.wallet);
+}
diff --git a/lib/screens/hardware_connect_bitbox/connect_bitbox_page.dart b/lib/screens/hardware_connect_bitbox/connect_bitbox_page.dart
new file mode 100644
index 00000000..623b903b
--- /dev/null
+++ b/lib/screens/hardware_connect_bitbox/connect_bitbox_page.dart
@@ -0,0 +1,25 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:realunit_wallet/di.dart';
+import 'package:realunit_wallet/packages/hardware_wallet/bitbox.dart';
+import 'package:realunit_wallet/packages/service/wallet_service.dart';
+import 'package:realunit_wallet/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart';
+import 'package:realunit_wallet/screens/hardware_connect_bitbox/connect_bitbox_view.dart';
+import 'package:realunit_wallet/screens/home/bloc/home_bloc.dart';
+
+class ConnectBitboxPage extends StatelessWidget {
+ const ConnectBitboxPage({super.key});
+
+ @override
+ Widget build(BuildContext context) => BlocProvider(
+ create: (_) => ConnectBitboxCubit(getIt(), getIt()),
+ child: BlocListener(
+ listener: (context, state) {
+ if (state is BitboxConnected) {
+ context.read().add(LoadWalletEvent(state.wallet));
+ }
+ },
+ child: ConnectBitboxView(),
+ ),
+ );
+}
diff --git a/lib/screens/hardware_connect_bitbox/connect_bitbox_view.dart b/lib/screens/hardware_connect_bitbox/connect_bitbox_view.dart
new file mode 100644
index 00000000..9f83f13b
--- /dev/null
+++ b/lib/screens/hardware_connect_bitbox/connect_bitbox_view.dart
@@ -0,0 +1,57 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:go_router/go_router.dart';
+import 'package:realunit_wallet/generated/i18n.dart';
+import 'package:realunit_wallet/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart';
+import 'package:realunit_wallet/screens/hardware_connect_bitbox/widgets/connect_content.dart';
+import 'package:realunit_wallet/styles/styles.dart';
+import 'package:realunit_wallet/widgets/handlebars.dart';
+
+class ConnectBitboxView extends StatelessWidget {
+ const ConnectBitboxView({super.key});
+
+ @override
+ Widget build(BuildContext context) => Container(
+ color: Colors.white,
+ child: Column(
+ children: [
+ Handlebars.horizontal(context, margin: EdgeInsets.only(top: 5), width: 36),
+ BlocBuilder(
+ builder: (context, state) => Stack(children: [
+ AnimatedSlide(
+ duration: const Duration(milliseconds: 350),
+ curve: Curves.easeInOut,
+ offset: state is BitboxNotConnected ? Offset.zero : const Offset(-1.2, 0),
+ child: ConnectContent(
+ title: S.of(context).connect_bitbox_title,
+ content: defaultTargetPlatform == TargetPlatform.iOS
+ ? S.of(context).connect_bitbox_content_ios
+ : S.of(context).connect_bitbox_content,
+ imagePath: "assets/images/illustrations/bitbox_connect.svg",
+ ),
+ ),
+ AnimatedSlide(
+ duration: const Duration(milliseconds: 350),
+ curve: Curves.easeInOut,
+ offset: state is BitboxFound ? Offset.zero : const Offset(1.2, 0),
+ child: ConnectContent(
+ title: S.of(context).connected_bitbox_title,
+ content: S.of(context).connected_bitbox_content,
+ imagePath: "assets/images/illustrations/bitbox_connected.svg",
+ ),
+ ),
+ ]),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 28, bottom: 54),
+ child: ElevatedButton(
+ style: kFullwidthGrayButtonStyle,
+ onPressed: context.pop,
+ child: Text(S.of(context).cancel),
+ ),
+ )
+ ],
+ ),
+ );
+}
diff --git a/lib/screens/hardware_connect_bitbox/widgets/connect_content.dart b/lib/screens/hardware_connect_bitbox/widgets/connect_content.dart
new file mode 100644
index 00000000..c690f48a
--- /dev/null
+++ b/lib/screens/hardware_connect_bitbox/widgets/connect_content.dart
@@ -0,0 +1,39 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:realunit_wallet/styles/styles.dart';
+
+class ConnectContent extends StatelessWidget {
+ final String imagePath;
+ final String title;
+ final String content;
+
+ const ConnectContent({
+ super.key,
+ required this.imagePath,
+ required this.title,
+ required this.content,
+ });
+
+ @override
+ Widget build(BuildContext context) => Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 40, bottom: 20),
+ child: SvgPicture.asset(imagePath),
+ ),
+ Text(
+ title,
+ textAlign: TextAlign.center,
+ style: kBottomSheetTitleTextStyle,
+ ),
+ SizedBox(
+ width: 330,
+ child: Text(
+ content,
+ textAlign: TextAlign.center,
+ style: kBottomSheetContentTextStyle,
+ ),
+ ),
+ ],
+ );
+}
diff --git a/lib/screens/restore_wallet/cubit/restore_wallet/restore_wallet_state.dart b/lib/screens/restore_wallet/cubit/restore_wallet/restore_wallet_state.dart
index 9f8674a8..704eab3f 100644
--- a/lib/screens/restore_wallet/cubit/restore_wallet/restore_wallet_state.dart
+++ b/lib/screens/restore_wallet/cubit/restore_wallet/restore_wallet_state.dart
@@ -2,7 +2,7 @@ part of 'restore_wallet_cubit.dart';
class RestoreWalletState extends Equatable {
final bool isLoading;
- final Wallet? wallet;
+ final SoftwareWallet? wallet;
const RestoreWalletState({
this.isLoading = false,
diff --git a/lib/screens/settings/settings_page.dart b/lib/screens/settings/settings_page.dart
index e5791a37..feea6868 100644
--- a/lib/screens/settings/settings_page.dart
+++ b/lib/screens/settings/settings_page.dart
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:realunit_wallet/di.dart';
import 'package:realunit_wallet/generated/i18n.dart';
+import 'package:realunit_wallet/packages/wallet/wallet.dart';
import 'package:realunit_wallet/screens/home/bloc/home_bloc.dart';
import 'package:realunit_wallet/screens/settings/bloc/settings_bloc.dart';
import 'package:realunit_wallet/screens/settings/widgets/settings_section.dart';
@@ -76,12 +77,14 @@ class SettingsPage extends StatelessWidget {
trailing: _forwardIcon,
onTap: null,
),
- SettingOption(
- title: S.of(context).settings_wallet_backup,
- leading: KeySolidIcon(size: 24),
- trailing: _forwardIcon,
- onTap: () => context.push('/settings/seed'),
- ),
+ if (context.read().state.openWallet?.walletType ==
+ WalletType.software)
+ SettingOption(
+ title: S.of(context).settings_wallet_backup,
+ leading: KeySolidIcon(size: 24),
+ trailing: _forwardIcon,
+ onTap: () => context.push('/settings/seed'),
+ ),
],
),
),
diff --git a/lib/screens/settings_seed/settings_seed_page.dart b/lib/screens/settings_seed/settings_seed_page.dart
index 919c3508..50375b47 100644
--- a/lib/screens/settings_seed/settings_seed_page.dart
+++ b/lib/screens/settings_seed/settings_seed_page.dart
@@ -11,8 +11,7 @@ class SettingsSeedPage extends StatelessWidget {
@override
Widget build(BuildContext context) => BlocProvider(
- create: (_) =>
- SettingsSeedCubit((getIt().wallet as Wallet).seed),
+ create: (_) => SettingsSeedCubit((getIt().wallet as SoftwareWallet).seed),
child: SettingsSeedView(),
);
}
diff --git a/lib/screens/welcome/welcome_page.dart b/lib/screens/welcome/welcome_page.dart
index baafaca7..49d84d9a 100644
--- a/lib/screens/welcome/welcome_page.dart
+++ b/lib/screens/welcome/welcome_page.dart
@@ -1,12 +1,14 @@
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:realunit_wallet/generated/i18n.dart';
+import 'package:realunit_wallet/router.dart';
+import 'package:realunit_wallet/screens/hardware_connect_bitbox/connect_bitbox_page.dart';
+import 'package:realunit_wallet/screens/welcome/widgets/welcome_card.dart';
import 'package:realunit_wallet/styles/colors.dart';
import 'package:realunit_wallet/styles/icons.dart';
-import 'widgets/welcome_card.dart';
-
class WelcomePage extends StatefulWidget {
const WelcomePage({super.key});
@@ -23,13 +25,8 @@ class _WelcomePageState extends State {
appBar: AppBar(
leading: showSecondStep
? IconButton(
- onPressed: () => setState(
- () => showSecondStep = false,
- ),
- icon: Icon(
- Icons.arrow_back_rounded,
- size: 24,
- ),
+ onPressed: () => setState(() => showSecondStep = false),
+ icon: Icon(Icons.arrow_back_rounded, size: 24),
)
: null,
),
@@ -90,13 +87,14 @@ class _WelcomePageState extends State {
'assets/images/illustrations/software_wallet.svg',
),
),
- WelcomeCard(
- title: S.of(context).bitbox,
- description: S.of(context).hardware_wallet_subtitle,
- trailing: SvgPicture.asset(
- 'assets/images/illustrations/bitbox.svg',
+ if (defaultTargetPlatform == TargetPlatform.android)
+ WelcomeCard(
+ title: S.of(context).bitbox,
+ description: S.of(context).hardware_wallet_subtitle,
+ trailing: SvgPicture.asset(
+ 'assets/images/illustrations/bitbox.svg',
+ ),
),
- ),
],
),
),
@@ -134,4 +132,15 @@ class _WelcomePageState extends State {
),
),
);
+
+ void onBitboxPressed() {
+ showModalBottomSheet(
+ context: navigatorKey.currentContext!,
+ backgroundColor: Colors.white,
+ builder: (_) => BottomSheet(
+ onClosing: () {},
+ builder: (_) => ConnectBitboxPage(),
+ ),
+ );
+ }
}
diff --git a/lib/styles/colors.dart b/lib/styles/colors.dart
index 9f5a5ba1..e5291deb 100644
--- a/lib/styles/colors.dart
+++ b/lib/styles/colors.dart
@@ -21,6 +21,7 @@ class RealUnitColors {
static const green = Color.fromARGB(255, 76, 172, 54);
static const okker = Color(0xFFE9AD3F);
+ static const neutral900 = Color.fromARGB(255, 15, 23, 42);
static const neutral500 = Color.fromARGB(255, 100, 116, 139);
static const neutral400 = Color.fromARGB(255, 148, 163, 184);
static const neutral300 = Color(0xFFCED5DE);
diff --git a/lib/styles/styles.dart b/lib/styles/styles.dart
index 193b691a..1064c185 100644
--- a/lib/styles/styles.dart
+++ b/lib/styles/styles.dart
@@ -25,14 +25,21 @@ final kFullwidthPrimaryButtonStyle = ElevatedButton.styleFrom(
);
final kFullwidthGrayButtonStyle = ElevatedButton.styleFrom(
- backgroundColor: DEuroColors.neutralGrey,
- fixedSize: Size(double.infinity, 55),
+ backgroundColor: RealUnitColors.neutral100,
+ fixedSize: const Size(double.infinity, 20),
elevation: 0.0,
+ textStyle: kFullwidthGrayButtonTextStyle,
+);
+
+const kFullwidthGrayButtonTextStyle = TextStyle(
+ fontSize: 16,
+ color: RealUnitColors.neutral900,
+ fontWeight: FontWeight.w600,
);
final kFullwidthBlueButtonStyle = FilledButton.styleFrom(
backgroundColor: RealUnitColors.realUnitBlue,
- fixedSize: const Size(double.infinity, 50),
+ fixedSize: const Size(double.infinity, 20),
padding: const EdgeInsets.only(left: 24, right: 24),
);
@@ -60,3 +67,15 @@ const kContainerCardStyle = BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(12)),
);
+
+const kBottomSheetTitleTextStyle = TextStyle(
+ fontSize: 22,
+ fontWeight: FontWeight.w700,
+ color: RealUnitColors.realUnitBlack,
+);
+
+const kBottomSheetContentTextStyle = TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.w400,
+ color: RealUnitColors.neutral500,
+);
diff --git a/lib/widgets/handlebars.dart b/lib/widgets/handlebars.dart
index 3987b64e..f67c4aa8 100644
--- a/lib/widgets/handlebars.dart
+++ b/lib/widgets/handlebars.dart
@@ -1,19 +1,20 @@
-import 'package:realunit_wallet/styles/colors.dart';
import 'package:flutter/material.dart';
+import 'package:realunit_wallet/styles/colors.dart';
class Handlebars {
static Widget horizontal(
BuildContext context, {
EdgeInsetsGeometry margin = const EdgeInsets.only(top: 10),
double? width,
+ double borderRadius = 5,
}) =>
Container(
margin: margin,
height: 5,
- width: width ??= MediaQuery.of(context).size.width * 0.25,
+ width: width ?? MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(
color: RealUnitColors.realUnitBlack,
- borderRadius: BorderRadius.circular(5.0),
+ borderRadius: BorderRadius.circular(borderRadius),
),
);
}
diff --git a/pubspec.lock b/pubspec.lock
index c265ffd2..73641e28 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -81,6 +81,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.6"
+ bitbox_flutter:
+ dependency: "direct main"
+ description:
+ path: "."
+ ref: e33e3888a5f384d960b11ad406b906b5770aede7
+ resolved-ref: e33e3888a5f384d960b11ad406b906b5770aede7
+ url: "https://github.com/konstantinullrich/bitbox_flutter"
+ source: git
+ version: "0.0.1"
bloc:
dependency: transitive
description:
@@ -1486,11 +1495,12 @@ packages:
web3dart:
dependency: "direct main"
description:
- name: web3dart
- sha256: "885e5e8f0cc3c87c09f160a7fce6279226ca41316806f7ece2001959c62ecced"
- url: "https://pub.dev"
- source: hosted
- version: "2.7.3"
+ path: "."
+ ref: cake
+ resolved-ref: aa3f932dbf54eda651b7bd01ad00204acf998bd0
+ url: "https://github.com/cake-tech/web3dart.git"
+ source: git
+ version: "2.7.2"
web_socket:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index b9d3eed7..bf3e0e0b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
-version: 0.0.1+1
+version: 0.0.1+2
environment:
sdk: '>=3.3.0 <4.0.0'
@@ -70,6 +70,10 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
+ bitbox_flutter:
+ git:
+ url: https://github.com/konstantinullrich/bitbox_flutter
+ ref: e33e3888a5f384d960b11ad406b906b5770aede7
@@ -90,6 +94,10 @@ dev_dependencies:
dependency_overrides:
collection: 1.19.0
+ web3dart:
+ git:
+ url: https://github.com/cake-tech/web3dart.git
+ ref: cake
flutter:
@@ -151,4 +159,4 @@ flutter_native_splash:
fullscreen: false
android_12:
color: "#ffffff"
- image: assets/images/splash/splash_logo.png
\ No newline at end of file
+ image: assets/images/splash/splash_logo.png
diff --git a/test/screens/restore_wallet/restore_wallet_page_test.dart b/test/screens/restore_wallet/restore_wallet_page_test.dart
index 476db0fc..f0066089 100644
--- a/test/screens/restore_wallet/restore_wallet_page_test.dart
+++ b/test/screens/restore_wallet/restore_wallet_page_test.dart
@@ -27,7 +27,7 @@ class MockHomeBloc extends MockBloc implements HomeBloc {}
class MockWalletService extends Mock implements WalletService {}
-class MockWallet extends Mock implements Wallet {}
+class MockWallet extends Mock implements SoftwareWallet {}
void main() {
late RestoreWalletCubit restoreWalletCubit;