Cw 604 integrate bitcoin ledger (#1407)

* CW-503 Prepare Ledger integration

* CW-503 Revert Tor ignore

* CW-503 Add Connect Device Page

* CW-503 Add createWalletFromDevice for monero

* CW-503 Add Connect Device Page

* CW-503 Add Connect Device Page

* CW-503 Add Debug Options

* CW-503 Add proper hardware wallet selection screen

* CW-503 Minor design changes on connect_device_page

* CW-503 Add Create Wallet from Ledger

* CW-503 Spent Ledger ETH Funds

* CW-503 Minor fixes

* CW-503 Fix Merge conflicts

* CW-503 Fix Merge conflicts

* CW-503 Fix Merge conflicts

* CW-503 Fix minor conflicts

* CW-503 Improve Ledger BLE Communication

* CW-503 Improve Ledger BLE Communication

* CW-503 Rollback Monero Test code

* CW-503 Fix Execution failed for task :app:checkReleaseDuplicateClasses

* CW-503 Better Error-Exceptions

* CW-503 Add SetPinScreen before restore from hardware-wallet

* CW-503 override web3dart to use cake's git hosted version

* CW-503 Implement ledger sign messages

* CW-503 Implement ledger sign messages and send erc20 tokens

* CW-503 Fix merge conflicts

* CW-503 Fix merge conflicts

* CW-503 Use dep override for ledger_flutter

* CW-503 Ledger ERC20 finalisation

* CW-503 More graceful error handling

* CW-503 Even more graceful error handling & remove debug code

* CW-503 Minor Changes for Vik

* CW-503 Fix USB connection

* CW-503 Maybe this overrides web3dart finally

* Indicate Loading on the first 5 Wallet Accounts

* Prepare Ledger Bitcoin

* Fix conflicts with main

* Add Bluetooth permission to iOS [skip ci]

* add privacyinfo for ios [skip ci]

* update PrivacyInfo.xcprivacy [scip ci]

* ios shit [skip ci]

* Improve bitcoin xpub

* Resolve open Todos regarding iOS Support

* Minor debug

* Remove erc20 dependency to have more granular control over the tx UX

* Create Bitcoin Wallets using xpub

* Create Bitcoin Wallets using xpub

* Better error handling

* Improve Ledger account handling

* Add Bitcoin Support for Ledger

* Add Bitcoin Support for Ledger

* Add Bitcoin Support for Ledger

* Implement requested Changes

* Implement requested Changes

* Minor fix

* Implement requested Changes

* Implement requested Changes

* Add comment to remind me

* Enable RBF for Bitcoin Ledger

* Fix merge conflicts

* Update wallet_info.dart

* Cw 503 ledger support (#1310)

* CW-503 Prepare Ledger integration

* CW-503 Revert Tor ignore

* CW-503 Add Connect Device Page

* CW-503 Add createWalletFromDevice for monero

* CW-503 Add Connect Device Page

* CW-503 Add Connect Device Page

* CW-503 Add Debug Options

* CW-503 Add proper hardware wallet selection screen

* CW-503 Minor design changes on connect_device_page

* CW-503 Add Create Wallet from Ledger

* CW-503 Spent Ledger ETH Funds

* CW-503 Minor fixes

* CW-503 Fix Merge conflicts

* CW-503 Fix Merge conflicts

* CW-503 Fix Merge conflicts

* CW-503 Fix minor conflicts

* CW-503 Improve Ledger BLE Communication

* CW-503 Improve Ledger BLE Communication

* CW-503 Rollback Monero Test code

* CW-503 Fix Execution failed for task :app:checkReleaseDuplicateClasses

* CW-503 Better Error-Exceptions

* CW-503 Add SetPinScreen before restore from hardware-wallet

* CW-503 override web3dart to use cake's git hosted version

* CW-503 Implement ledger sign messages

* CW-503 Implement ledger sign messages and send erc20 tokens

* CW-503 Fix merge conflicts

* CW-503 Fix merge conflicts

* CW-503 Use dep override for ledger_flutter

* CW-503 Ledger ERC20 finalisation

* CW-503 More graceful error handling

* CW-503 Even more graceful error handling & remove debug code

* CW-503 Minor Changes for Vik

* CW-503 Fix USB connection

* CW-503 Maybe this overrides web3dart finally

* Indicate Loading on the first 5 Wallet Accounts

* Fix conflicts with main

* Add Bluetooth permission to iOS [skip ci]

* add privacyinfo for ios [skip ci]

* update PrivacyInfo.xcprivacy [scip ci]

* ios shit [skip ci]

* Resolve open Todos regarding iOS Support

* Remove erc20 dependency to have more granular control over the tx UX

* Better error handling

* Improve Ledger account handling

* Implement requested Changes

* Implement requested Changes

* Implement requested Changes

* Implement requested Changes

* Fix merge conflicts

* Update wallet_info.dart

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* Fix merge conflicts

* Fix merge conflicts

* Minor Fix to derivations

* Update cw_bitcoin/lib/bitcoin_wallet.dart [skip ci]

* Update cw_bitcoin/lib/bitcoin_wallet.dart [skip ci]

* Update cw_bitcoin/lib/electrum_wallet.dart [skip ci]

* Fix backward compatibility issues

* Merge Tron

* Fix Tron with HW changes

* fix ble on iOS
fix tron address validation

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Konstantin Ullrich
2024-05-05 04:44:50 +03:00
committed by GitHub
co-authored by Omar Hatem
parent 043d7d7c8b
commit 5eabdcdca1
118 changed files with 3363 additions and 579 deletions
+26 -4
View File
@@ -32,6 +32,14 @@ class CWBitcoin extends Bitcoin {
{required String name, WalletInfo? walletInfo}) =>
BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo);
@override
WalletCredentials createBitcoinHardwareWalletCredentials(
{required String name,
required HardwareAccountData accountData,
WalletInfo? walletInfo}) =>
BitcoinRestoreWalletFromHardware(
name: name, hwAccountData: accountData, walletInfo: walletInfo);
@override
TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
@@ -292,7 +300,7 @@ class CWBitcoin extends Bitcoin {
return [
DerivationInfo(
derivationType: DerivationType.electrum,
derivationPath: "m/0'/0",
derivationPath: "m/0'",
description: "Electrum",
scriptType: "p2wpkh",
)
@@ -344,9 +352,6 @@ class CWBitcoin extends Bitcoin {
if (derivationDepth == 3) {
// we add "/0/0" so that we generate account 0, index 0 and correctly get balance
derivationPath += "/0/0";
// we don't support sub-ACCOUNTS in bitcoin like we do monero, and so the path dInfoCopy
// expects should be ACCOUNT 0, index unspecified:
dInfoCopy.derivationPath = dInfoCopy.derivationPath! + "/0";
}
// var hd = bip32.BIP32.fromSeed(seedBytes).derivePath(derivationPath);
@@ -440,4 +445,21 @@ class CWBitcoin extends Bitcoin {
final bitcoinWallet = wallet as ElectrumWallet;
return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 1.1).round();
}
@override
void setLedger(WalletBase wallet, Ledger ledger, LedgerDevice device) {
(wallet as BitcoinWallet).setLedger(ledger, device);
}
@override
Future<List<HardwareAccountData>> getHardwareWalletAccounts(LedgerViewModel ledgerVM,
{int index = 0, int limit = 5}) async {
final hardwareWalletService = BitcoinHardwareWalletService(ledgerVM.ledger, ledgerVM.device);
try {
return hardwareWalletService.getAvailableAccounts(index: index, limit: limit);
} on LedgerException catch (err) {
print(err.message);
throw err;
}
}
}
+3
View File
@@ -1,5 +1,6 @@
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
@@ -7,10 +8,12 @@ abstract class BuyProvider {
BuyProvider({
required this.wallet,
required this.isTestEnvironment,
required this.ledgerVM,
});
final WalletBase wallet;
final bool isTestEnvironment;
final LedgerViewModel? ledgerVM;
String get title;
+20 -4
View File
@@ -3,9 +3,11 @@ import 'dart:convert';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
@@ -13,8 +15,8 @@ import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class DFXBuyProvider extends BuyProvider {
DFXBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
DFXBuyProvider({required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
static const _baseUrl = 'api.dfx.swiss';
// static const _signMessagePath = '/v1/auth/signMessage';
@@ -91,7 +93,7 @@ class DFXBuyProvider extends BuyProvider {
// }
Future<String> auth() async {
final signMessage = getSignature(await getSignMessage());
final signMessage = await getSignature(await getSignMessage());
final requestBody = jsonEncode({
'wallet': walletName,
@@ -118,7 +120,7 @@ class DFXBuyProvider extends BuyProvider {
}
}
String getSignature(String message) {
Future<String> getSignature(String message) async {
switch (wallet.type) {
case WalletType.ethereum:
case WalletType.polygon:
@@ -135,6 +137,20 @@ class DFXBuyProvider extends BuyProvider {
@override
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
if (wallet.isHardwareWallet) {
if (!ledgerVM!.isConnected) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: wallet.walletInfo.type,
onConnectDevice: (BuildContext context, LedgerViewModel ledgerVM) {
ledgerVM.setLedger(wallet);
Navigator.of(context).pop();
}));
} else {
ledgerVM!.setLedger(wallet);
}
}
try {
final assetOut = this.assetOut;
final blockchain = this.blockchain;
+1 -2
View File
@@ -14,7 +14,6 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:crypto/crypto.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
@@ -30,7 +29,7 @@ class MoonPayProvider extends BuyProvider {
}) : baseSellUrl = isTestEnvironment ? _baseSellTestUrl : _baseSellProductUrl,
baseBuyUrl = isTestEnvironment ? _baseBuyTestUrl : _baseBuyProductUrl,
this._settingsStore = settingsStore,
super(wallet: wallet, isTestEnvironment: isTestEnvironment);
super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
final SettingsStore _settingsStore;
+1 -1
View File
@@ -13,7 +13,7 @@ import 'package:url_launcher/url_launcher.dart';
class OnRamperBuyProvider extends BuyProvider {
OnRamperBuyProvider(this._settingsStore,
{required WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
static const _baseUrl = 'buy.onramper.com';
+21 -4
View File
@@ -3,8 +3,11 @@ import 'dart:convert';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
@@ -12,8 +15,8 @@ import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class RobinhoodBuyProvider extends BuyProvider {
RobinhoodBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
RobinhoodBuyProvider({required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
static const _baseUrl = 'applink.robinhood.com';
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
@@ -34,7 +37,7 @@ class RobinhoodBuyProvider extends BuyProvider {
String get _apiSecret => secrets.exchangeHelperApiKey;
String getSignature(String message) {
Future<String> getSignature(String message) {
switch (wallet.type) {
case WalletType.ethereum:
case WalletType.polygon:
@@ -53,7 +56,7 @@ class RobinhoodBuyProvider extends BuyProvider {
final valid_until = (DateTime.now().millisecondsSinceEpoch / 1000).round() + 10;
final message = "$_apiSecret:${valid_until}";
final signature = getSignature(message);
final signature = await getSignature(message);
final uri = Uri.https(_cIdBaseUrl, "/api/robinhood");
@@ -84,6 +87,20 @@ class RobinhoodBuyProvider extends BuyProvider {
}
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
if (wallet.isHardwareWallet) {
if (!ledgerVM!.isConnected) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: wallet.walletInfo.type,
onConnectDevice: (BuildContext context, LedgerViewModel ledgerVM) {
ledgerVM.setLedger(wallet);
Navigator.of(context).pop();
}));
} else {
ledgerVM!.setLedger(wallet);
}
}
try {
final uri = await requestProviderUrl();
await launchUrl(uri, mode: LaunchMode.externalApplication);
+1 -1
View File
@@ -14,7 +14,7 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
class WyreBuyProvider extends BuyProvider {
WyreBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
: baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl,
super(wallet: wallet, isTestEnvironment: isTestEnvironment);
super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
static const _baseTestApiUrl = 'https://api.testwyre.com';
static const _baseProductApiUrl = 'https://api.sendwyre.com';
+4 -1
View File
@@ -295,7 +295,7 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.sol:
return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)';
case CryptoCurrency.trx:
return '^(T|t)[1-9A-HJ-NP-Za-km-z]{33}\$';
return '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
default:
if (type.tag == CryptoCurrency.eth.title) {
return '0x[0-9a-zA-Z]{42}';
@@ -306,6 +306,9 @@ class AddressValidator extends TextValidator {
if (type.tag == CryptoCurrency.sol.title) {
return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)';
}
if (type.tag == CryptoCurrency.trx.title) {
return '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
}
return null;
}
+15
View File
@@ -101,4 +101,19 @@ class WalletCreationService {
return wallet;
}
Future<WalletBase> restoreFromHardwareWallet(WalletCredentials credentials) async {
checkIfExists(credentials.name);
final password = generateWalletPassword();
credentials.password = password;
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
final wallet = await _service!.restoreFromHardwareWallet(credentials);
if (wallet.type == WalletType.monero) {
await sharedPreferences.setBool(
PreferencesKey.moneroWalletUpdateV1Key(wallet.name), _isNewMoneroWalletPasswordUpdated);
}
return wallet;
}
}
+170 -155
View File
@@ -1,237 +1,240 @@
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/anonpay/anonpay_api.dart';
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/anypay/anypay_api.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/backup_service.dart';
import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart';
import 'package:cake_wallet/core/yat_service.dart';
import 'package:cake_wallet/entities/background_tasks.dart';
import 'package:cake_wallet/entities/biometric_auth.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cake_wallet/entities/qr_view_data.dart';
import 'package:cake_wallet/entities/template.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/exchange/exchange_template.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cake_wallet/ionia/ionia_anypay.dart';
import 'package:cake_wallet/ionia/ionia_api.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:cake_wallet/ionia/ionia_tip.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart';
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
import 'package:cake_wallet/src/screens/send/send_page.dart';
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart';
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_redeem_view_model.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:cake_wallet/ionia/ionia_api.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart';
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/nano_account.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cake_wallet/core/backup_service.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cake_wallet/entities/biometric_auth.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cw_core/node.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/authentication_store.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/dashboard/orders_store.dart';
import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
import 'package:cake_wallet/store/node_list_store.dart';
import 'package:cake_wallet/store/secret_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/key_service.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
import 'package:cake_wallet/src/screens/send/send_page.dart';
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/store/templates/send_template_store.dart';
import 'package:cake_wallet/store/wallet_list_store.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
import 'package:cake_wallet/view_model/auth_view_model.dart';
import 'package:cake_wallet/view_model/backup_view_model.dart';
import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_redeem_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
import 'package:cake_wallet/view_model/order_details_view_model.dart';
import 'package:cake_wallet/view_model/rescan_view_model.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
import 'package:cake_wallet/view_model/support_view_model.dart';
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/auth_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/nano_account.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get_it/get_it.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:cake_wallet/store/authentication_store.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/templates/send_template_store.dart';
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/entities/template.dart';
import 'package:cake_wallet/exchange/exchange_template.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
import 'package:cake_wallet/anypay/anypay_api.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/entities/qr_view_data.dart';
import 'buy/dfx/dfx_buy_provider.dart';
import 'core/totp_request_details.dart';
@@ -308,6 +311,7 @@ Future<void> setup({
getIt.registerFactory<Box<Node>>(() => _powNodeSource, instanceName: Node.boxName + "pow");
getIt.registerSingleton(AuthenticationStore());
getIt.registerSingleton(LedgerViewModel());
getIt.registerSingleton<WalletListStore>(WalletListStore());
getIt.registerSingleton(NodeListStoreBase.instance);
getIt.registerSingleton<SettingsStore>(settingsStore);
@@ -367,6 +371,11 @@ Future<void> setup({
getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type);
});
getIt.registerFactoryParam<WalletHardwareRestoreViewModel, WalletType, void>((type, _) =>
WalletHardwareRestoreViewModel(getIt.get<LedgerViewModel>(), getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
type: type));
getIt.registerFactory<WalletAddressListViewModel>(() => WalletAddressListViewModel(
appStore: getIt.get<AppStore>(),
yatStore: getIt.get<YatStore>(),
@@ -490,12 +499,8 @@ Future<void> setup({
getIt.registerLazySingleton<WalletConnectKeyService>(() => KeyServiceImpl());
getIt.registerLazySingleton<Web3WalletService>(() {
final Web3WalletService web3WalletService = Web3WalletService(
getIt.get<BottomSheetService>(),
getIt.get<WalletConnectKeyService>(),
appStore,
getIt.get<SharedPreferences>()
);
final Web3WalletService web3WalletService = Web3WalletService(getIt.get<BottomSheetService>(),
getIt.get<WalletConnectKeyService>(), appStore, getIt.get<SharedPreferences>());
web3WalletService.create();
return web3WalletService;
});
@@ -599,6 +604,7 @@ Future<void> setup({
getIt.get<BalanceViewModel>(),
getIt.get<ContactListViewModel>(),
_transactionDescriptionBox,
getIt.get<LedgerViewModel>(),
),
);
@@ -804,11 +810,11 @@ Future<void> setup({
editingNode: editingNode,
isSelected: isSelected));
getIt.registerFactory<RobinhoodBuyProvider>(
() => RobinhoodBuyProvider(wallet: getIt.get<AppStore>().wallet!));
getIt.registerFactory<RobinhoodBuyProvider>(() => RobinhoodBuyProvider(
wallet: getIt.get<AppStore>().wallet!, ledgerVM: getIt.get<LedgerViewModel>()));
getIt
.registerFactory<DFXBuyProvider>(() => DFXBuyProvider(wallet: getIt.get<AppStore>().wallet!));
getIt.registerFactory<DFXBuyProvider>(() => DFXBuyProvider(
wallet: getIt.get<AppStore>().wallet!, ledgerVM: getIt.get<LedgerViewModel>()));
getIt.registerFactory<MoonPayProvider>(() => MoonPayProvider(
settingsStore: getIt.get<AppStore>().settingsStore,
@@ -928,8 +934,17 @@ Future<void> setup({
transactionDetailsViewModel:
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
getIt.registerFactoryParam<NewWalletTypePage, void Function(BuildContext, WalletType), bool?>(
(param1, isCreate) => NewWalletTypePage(onTypeSelected: param1, isCreate: isCreate ?? true));
getIt.registerFactoryParam<NewWalletTypePage, void Function(BuildContext, WalletType),
List<bool>?>((param1, additionalParams) {
final isCreate = additionalParams?[0] ?? true;
final isHardwareWallet = additionalParams?[1] ?? false;
return NewWalletTypePage(
onTypeSelected: param1,
isCreate: isCreate,
isHardwareWallet: isHardwareWallet,
);
});
getIt.registerFactoryParam<PreSeedPage, int, void>(
(seedPhraseLength, _) => PreSeedPage(seedPhraseLength));
@@ -1144,9 +1159,9 @@ Future<void> setup({
getIt.registerFactory(() => IoniaAccountCardsPage(getIt.get<IoniaAccountViewModel>()));
getIt.registerFactoryParam<RBFDetailsPage, TransactionInfo, void>(
(TransactionInfo transactionInfo, _) => RBFDetailsPage(
(TransactionInfo transactionInfo, _) => RBFDetailsPage(
transactionDetailsViewModel:
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
getIt.registerFactory(() => AnonPayApi(
useTorOnly: getIt.get<SettingsStore>().exchangeStatus == ExchangeApiMode.torOnly,
+31 -2
View File
@@ -30,14 +30,23 @@ class CWEthereum extends Ethereum {
}) =>
EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
@override
WalletCredentials createEthereumHardwareWalletCredentials({
required String name,
required HardwareAccountData hwAccountData,
WalletInfo? walletInfo,
}) =>
EVMChainRestoreWalletFromHardware(
name: name, hwAccountData: hwAccountData, walletInfo: walletInfo);
@override
String getAddress(WalletBase wallet) => (wallet as EthereumWallet).walletAddresses.address;
@override
String getPrivateKey(WalletBase wallet) {
final privateKeyHolder = (wallet as EthereumWallet).evmChainPrivateKey;
String stringKey = bytesToHex(privateKeyHolder.privateKey);
return stringKey;
if (privateKeyHolder is EthPrivateKey) return bytesToHex(privateKeyHolder.privateKey);
return "";
}
@override
@@ -159,4 +168,24 @@ class CWEthereum extends Ethereum {
}
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
@override
void setLedger(WalletBase wallet, Ledger ledger, LedgerDevice device) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedger(
ledger,
device.connectionType == ConnectionType.usb ? device : null,
wallet.walletInfo.derivationInfo?.derivationPath);
}
@override
Future<List<HardwareAccountData>> getHardwareWalletAccounts(LedgerViewModel ledgerVM,
{int index = 0, int limit = 5}) async {
final hardwareWalletService = EVMChainHardwareWalletService(ledgerVM.ledger, ledgerVM.device);
try {
return await hardwareWalletService.getAvailableAccounts(index: index, limit: limit);
} on LedgerException catch (err) {
print(err.message);
throw err;
}
}
}
+5 -1
View File
@@ -105,7 +105,11 @@ Future<void> initializeAppConfigs() async {
if (!CakeHive.isAdapterRegistered(DERIVATION_INFO_TYPE_ID)) {
CakeHive.registerAdapter(DerivationInfoAdapter());
}
if (!CakeHive.isAdapterRegistered(HARDWARE_WALLET_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(HardwareWalletTypeAdapter());
}
if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(WalletTypeAdapter());
}
+45 -17
View File
@@ -30,14 +30,23 @@ class CWPolygon extends Polygon {
}) =>
EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
@override
WalletCredentials createPolygonHardwareWalletCredentials({
required String name,
required HardwareAccountData hwAccountData,
WalletInfo? walletInfo,
}) =>
EVMChainRestoreWalletFromHardware(
name: name, hwAccountData: hwAccountData, walletInfo: walletInfo);
@override
String getAddress(WalletBase wallet) => (wallet as PolygonWallet).walletAddresses.address;
@override
String getPrivateKey(WalletBase wallet) {
final privateKeyHolder = (wallet as PolygonWallet).evmChainPrivateKey;
String stringKey = bytesToHex(privateKeyHolder.privateKey);
return stringKey;
if (privateKeyHolder is EthPrivateKey) return bytesToHex(privateKeyHolder.privateKey);
return "";
}
@override
@@ -67,21 +76,21 @@ class CWPolygon extends Polygon {
int? feeRate,
}) =>
EVMChainTransactionCredentials(
outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as EVMChainTransactionPriority,
currency: currency,
feeRate: feeRate,
);
outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as EVMChainTransactionPriority,
currency: currency,
feeRate: feeRate,
);
Object createPolygonTransactionCredentialsRaw(
List<OutputInfo> outputs, {
@@ -157,4 +166,23 @@ class CWPolygon extends Polygon {
}
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
@override
void setLedger(WalletBase wallet, Ledger ledger, LedgerDevice device) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedger(
ledger,
device.connectionType == ConnectionType.usb ? device : null,
wallet.walletInfo.derivationInfo?.derivationPath);
}
@override
Future<List<HardwareAccountData>> getHardwareWalletAccounts(LedgerViewModel ledgerVM,
{int index = 0, int limit = 5}) async {
final hardwareWalletService = EVMChainHardwareWalletService(ledgerVM.ledger, ledgerVM.device);
try {
return await hardwareWalletService.getAvailableAccounts(index: index, limit: limit);
} on LedgerException catch (err) {
throw err;
}
}
}
+126 -72
View File
@@ -1,29 +1,74 @@
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/core/totp_request_details.dart';
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/entities/qr_view_data.dart';
import 'package:cake_wallet/entities/wallet_nft_response.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/screens/connect_device/select_hardware_wallet_account_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
import 'package:cake_wallet/src/screens/send/send_page.dart';
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart';
@@ -31,90 +76,49 @@ import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/nano_account.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
import 'package:cake_wallet/src/screens/send/send_page.dart';
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/node.dart';
import 'src/screens/dashboard/pages/nft_import_page.dart';
@@ -151,7 +155,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
final walletNewVM = getIt.get<WalletNewVM>(param1: type);
final seedTypeViewModel = getIt.get<SeedTypeViewModel>();
return CupertinoPageRoute<void>(builder: (_) => NewWalletPage(walletNewVM, seedTypeViewModel));
return CupertinoPageRoute<void>(
builder: (_) => NewWalletPage(walletNewVM, seedTypeViewModel));
case Routes.chooseHardwareWalletAccount:
final arguments = settings.arguments as List<dynamic>;
final type = arguments[0] as WalletType;
final walletVM = getIt.get<WalletHardwareRestoreViewModel>(param1: type);
return CupertinoPageRoute<void>(builder: (_) => SelectHardwareWalletAccountPage(walletVM));
case Routes.setupPin:
Function(PinCodeState<PinCodeWidget>, String)? callback;
@@ -168,7 +180,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) => getIt.get<NewWalletTypePage>(
param1: (BuildContext context, WalletType type) =>
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
param2: false));
param2: [false, false]));
case Routes.restoreOptions:
final isNewInstall = settings.arguments as bool;
@@ -199,7 +211,46 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) => getIt.get<NewWalletTypePage>(
param1: (BuildContext context, WalletType type) =>
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
param2: false));
param2: [false, false]));
}
case Routes.restoreWalletFromHardwareWallet:
final isNewInstall = settings.arguments as bool;
if (isNewInstall) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>(
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
Navigator.of(context.context).pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false),
),
fullscreenDialog: true,
);
}
if (isSingleCoin) {
return MaterialPageRoute<void>(
builder: (_) => ConnectDevicePage(
ConnectDevicePageParams(
walletType: availableWalletTypes.first,
onConnectDevice: (BuildContext context, _) =>
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
arguments: [availableWalletTypes.first]),
),
getIt.get<LedgerViewModel>(),
));
} else {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<NewWalletTypePage>(
param1: (BuildContext context, WalletType type) {
final arguments = ConnectDevicePageParams(
walletType: type,
onConnectDevice: (BuildContext context, _) =>
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
arguments: [type]),
);
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
},
param2: [false, true]));
}
case Routes.restoreWalletTypeFromQR:
@@ -400,8 +451,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.buySellPage:
final args = settings.arguments as bool;
return MaterialPageRoute<void>(
builder: (_) => getIt.get<BuySellOptionsPage>(param1: args));
return MaterialPageRoute<void>(builder: (_) => getIt.get<BuySellOptionsPage>(param1: args));
case Routes.buyWebView:
final args = settings.arguments as List;
@@ -424,8 +474,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.preSeedPage:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<PreSeedPage>(
param1: settings.arguments as int));
builder: (_) => getIt.get<PreSeedPage>(param1: settings.arguments as int));
case Routes.backup:
return CupertinoPageRoute<void>(
@@ -647,6 +696,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.torPage:
return MaterialPageRoute<void>(builder: (_) => getIt.get<TorPage>());
case Routes.connectDevices:
final params = settings.arguments as ConnectDevicePageParams;
return MaterialPageRoute<void>(
builder: (_) => ConnectDevicePage(params, getIt.get<LedgerViewModel>()));
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(
+3
View File
@@ -6,9 +6,11 @@ class Routes {
static const seed = '/seed';
static const restoreOptions = '/restore_options';
static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys';
static const restoreWalletFromHardwareWallet = '/restore/hardware_wallet';
static const restoreWalletTypeFromQR = '/restore_wallet_from_qr_code';
static const restoreWalletChooseDerivation =
'/restore_wallet_choose_derivation';
static const chooseHardwareWalletAccount = '/restore/hardware_wallet/accounts';
static const dashboard = '/dashboard';
static const send = '/send';
static const transactionDetails = '/transaction_info';
@@ -107,4 +109,5 @@ class Routes {
static const nftDetailsPage = '/nft_details_page';
static const importNFTPage = '/import_nft_page';
static const torPage = '/tor_page';
static const connectDevices = '/device/connect';
}
@@ -0,0 +1,221 @@
import 'dart:async';
import 'dart:io';
import 'package:cake_wallet/generated/i18n.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/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:ledger_flutter/ledger_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
typedef OnConnectDevice = void Function(BuildContext, LedgerViewModel);
class ConnectDevicePageParams {
final WalletType walletType;
final OnConnectDevice onConnectDevice;
ConnectDevicePageParams({required this.walletType, required this.onConnectDevice});
}
class ConnectDevicePage extends BasePage {
final WalletType walletType;
final OnConnectDevice onConnectDevice;
final LedgerViewModel ledgerVM;
ConnectDevicePage(ConnectDevicePageParams params, this.ledgerVM)
: walletType = params.walletType,
onConnectDevice = params.onConnectDevice;
@override
String get title => S.current.restore_title_from_hardware_wallet;
@override
Widget body(BuildContext context) => ConnectDevicePageBody(walletType, onConnectDevice, ledgerVM);
}
class ConnectDevicePageBody extends StatefulWidget {
final WalletType walletType;
final OnConnectDevice onConnectDevice;
final LedgerViewModel ledgerVM;
const ConnectDevicePageBody(this.walletType, this.onConnectDevice, this.ledgerVM);
@override
ConnectDevicePageBodyState createState() => ConnectDevicePageBodyState();
}
class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
final imageLedger = 'assets/images/ledger_nano.png';
final ledger = Ledger(
options: LedgerOptions(
scanMode: ScanMode.balanced,
maxScanDuration: const Duration(minutes: 5),
),
onPermissionRequest: (_) async {
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.bluetoothAdvertise,
].request();
return statuses.values.where((status) => status.isDenied).isEmpty;
},
);
var bleIsEnabled = true;
var bleDevices = <LedgerDevice>[];
var usbDevices = <LedgerDevice>[];
late Timer? _usbRefreshTimer = null;
late Timer? _bleRefreshTimer = null;
late StreamSubscription<LedgerDevice>? _bleRefresh = null;
@override
void initState() {
super.initState();
Future.delayed(
Duration(seconds: 1),
() => _bleRefresh = ledger.scan().listen((device) => setState(() => bleDevices.add(device))),
);
// _bleRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshBleDevices());
if (Platform.isAndroid) {
_usbRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshUsbDevices());
}
}
@override
void dispose() {
_bleRefreshTimer?.cancel();
_usbRefreshTimer?.cancel();
_bleRefresh?.cancel();
super.dispose();
}
Future<void> _refreshUsbDevices() async {
final dev = await ledger.listUsbDevices();
if (usbDevices.length != dev.length) setState(() => usbDevices = dev);
}
Future<void> _refreshBleDevices() async {
final isBleEnabled = await Permission.bluetooth.serviceStatus.isEnabled;
setState(() => bleIsEnabled = isBleEnabled);
if (isBleEnabled) {
_bleRefresh = ledger.scan().listen((device) => setState(() => bleDevices.add(device)));
_bleRefreshTimer?.cancel();
_bleRefreshTimer = null;
}
}
Future<void> _connectToDevice(LedgerDevice device) async {
await widget.ledgerVM.connectLedger(device);
widget.onConnectDevice(context, widget.ledgerVM);
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
height: double.infinity,
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 24),
child: SingleChildScrollView(
child: Column(
children: [
Padding(
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
child: Text(
Platform.isIOS
? S.of(context).connect_your_hardware_wallet_ios
: S.of(context).connect_your_hardware_wallet,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
textAlign: TextAlign.center,
),
),
if (!bleIsEnabled)
Padding(
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
child: Text(
S.of(context).ledger_please_enable_bluetooth,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
textAlign: TextAlign.center,
),
),
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: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
),
),
...bleDevices
.map(
(device) => Padding(
padding: EdgeInsets.only(bottom: 20),
child: DeviceTile(
onPressed: () => _connectToDevice(device),
title: device.name,
leading: imageLedger,
connectionType: device.connectionType,
),
),
)
.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: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
),
),
...usbDevices
.map(
(device) => Padding(
padding: EdgeInsets.only(bottom: 20),
child: DeviceTile(
onPressed: () => _connectToDevice(device),
title: device.name,
leading: imageLedger,
connectionType: device.connectionType,
),
),
)
.toList(),
]
],
),
),
),
);
}
}
@@ -0,0 +1,212 @@
// import 'dart:convert';
//
// 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/primary_button.dart';
// import 'package:cake_wallet/utils/responsive_layout_util.dart';
// import 'package:convert/convert.dart';
// import 'package:flutter/material.dart';
// import 'package:ledger_bitcoin/ledger_bitcoin.dart';
// import 'package:ledger_flutter/ledger_flutter.dart';
// import 'package:permission_handler/permission_handler.dart';
// import 'package:polyseed/polyseed.dart';
//
// class DebugDevicePage extends BasePage {
// @override
// String get title => "Connect Ledger";
//
// @override
// Widget body(BuildContext context) => DebugDevicePageBody();
// }
//
// class DebugDevicePageBody extends StatefulWidget {
// @override
// DebugDevicePageBodyState createState() => DebugDevicePageBodyState();
// }
//
// class DebugDevicePageBodyState extends State<DebugDevicePageBody> {
// final imageLedger = Image.asset(
// 'assets/images/ledger_icon_black.png',
// width: 40,
// );
// final ledger = Ledger(
// options: LedgerOptions(
// scanMode: ScanMode.balanced,
// maxScanDuration: const Duration(milliseconds: 5000),
// ),
// onPermissionRequest: (status) async {
// Map<Permission, PermissionStatus> statuses = await [
// // Permission.location,
// Permission.bluetoothScan,
// Permission.bluetoothConnect,
// Permission.bluetoothAdvertise,
// ].request();
//
// if (status != BleStatus.ready) {
// return false;
// }
//
// return statuses.values.where((status) => status.isDenied).isEmpty;
// },
// );
//
// late BitcoinLedgerApp btc;
// var devices = <LedgerDevice>[];
// var status = "";
// var counter = 0;
// LedgerDevice? selectedDevice = null;
//
// @override
// void initState() {
// super.initState();
// btc = BitcoinLedgerApp(ledger);
// }
//
// @override
// void dispose() {
// super.dispose();
// ledger.close(ConnectionType.ble);
// ledger.close(ConnectionType.usb);
// }
//
// Future<void> reconnectCurrentDevice() async {
// // await ledger.disconnect(selectedDevice!);
// // await ledger.connect(selectedDevice!);
// }
//
// Future<void> disconnectCurrentDevice() async {
// await ledger.disconnect(selectedDevice!);
// setState(() => selectedDevice = null);
// }
//
// @override
// Widget build(BuildContext context) {
// final imageLedger = 'assets/images/ledger_nano.png';
//
// return Center(
// child: Container(
// width: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
// height: double.infinity,
// padding: EdgeInsets.symmetric(vertical: 24, horizontal: 24),
// child: SingleChildScrollView(
// child: Column(
// children: [
// Padding(
// padding: EdgeInsets.only(top: 20),
// child: Text(status),
// ),
// if (selectedDevice != null) ...[
// DebugButton(
// title: "Get Version",
// method: "Version",
// func: () async => await btc.getVersion(selectedDevice!),
// ),
// DebugButton(
// title: "Get Master Fingerprint",
// method: "Master Fingerprint",
// func: () async => hex.encode(await btc.getMasterFingerprint(selectedDevice!)),
// ),
// DebugButton(
// title: "Get XPub",
// method: "XPub",
// func: () async => await btc.getXPubKey(selectedDevice!, derivationPath: "m/84'/0'/$counter'"),
// ),
// DebugButton(
// title: "Get Wallet Address",
// method: "Wallet Address",
// func: () async {
// setState(() => counter++);
// final derivationPath = "m/84'/0'/$counter'/0/0";
// return await btc.getAccounts(selectedDevice!, accountsDerivationPath: derivationPath);
// // return await ethereum!.getHardwareWalletAccounts(selectedDevice!);
// },
// ),
// DebugButton(
// title: "Send Money",
// method: "Sig",
// func: () async {
// final psbt = PsbtV2();
// final psbtBuf = base64.decode(
// "cHNidP8BAgQCAAAAAQQBAQEFAQIAAQ4gTW6k/cwKKu1u7m9oKr5ob7VcAC0IPkfaDitRi/FkD7sBDwQAAAAAARAE/////wEA/ekBAQAAAAABA9AYVQLI722H0osKMa/4dvMucrnKV1Myxtlp0l0BoOBDAQAAAAD/////ku6r2ABaHt9N26f/P4eMljX8t1f4lBcFfEwuNm/uXYoBAAAAAP////+YeAl8arEGKOcyrWJAYwSboyCstkhHN8zn7/vy7pkYTAEAAAAA/////wHlHgAAAAAAABYAFKdq0umSucBGVkl2MpT6Hgo/0a/xAkcwRAIgMkiJmNFbEi2I3CQYOwyV/JepCnFQRvj4xghkySpFcJMCIGAypkkWltfj+ucvqUIu27tusDAIAAB+rBhX/GV7hPlEASEDyLmWyTLjLfC9kn8pnW42jW5N6EJo5fObjWWEyfLDu9UCSDBFAiEAg9crVtwBPF+sWk+Th6pLwzDjJGItwsUCvoBPtmMTEb4CIDGuM7WOguV0TP21oidF3bSUZlEAjUHWfWzxLKw+3LofASEDfN16xKb70UZSeQyX5Tlh8iRq7np5Nlz9GYdcSU50sKwCSDBFAiEAvotOblaEiBptRWkvb6bj2MGyRjTphKLBLiHYmrRMTCgCIEKJH+z65uPSSz1NIb0d/u3bU9l0xcWk0idEsXjB+BIiASEDrAEiEtrSNKxbh6F/KPaCTafF2LVjCzb75WB+x4xSuoQAAAAAAQEf5R4AAAAAAAAWABSnatLpkrnARlZJdjKU+h4KP9Gv8SIGA3xMuxmPsBAm9aMEUBs3N46DB+Kdts3bZR/Wxt+uM0H4GKtN6bpUAACAAAAAgAAAAIAAAAAAAAAAAAABBBTk7bEOxYcdXDi1eeWraYDufm6eJgEDCOgDAAAAAAAAAAEEFDX3g/pnDXIfsRw8shK42NZn+SdpAQMIiBMAAAAAAAAiAgN8TLsZj7AQJvWjBFAbNzeOgwfinbbN22Uf1sbfrjNB+BirTem6VAAAgAAAAIAAAACAAAAAAAAAAAAA"
// );
// psbt.deserialize(psbtBuf);
// final result = await btc.signPsbt(selectedDevice!, psbt: psbt);
// return result.toHexString();
// },
// ),
// Padding(
// padding: EdgeInsets.only(top: 20),
// child: PrimaryButton(
// text: "Disconnect",
// onPressed: () => disconnectCurrentDevice(),
// color: Theme.of(context).primaryColor,
// textColor: Colors.white),
// ),
// ],
// if (selectedDevice == null) ...[
// ...devices
// .map(
// (device) => Padding(
// padding: EdgeInsets.only(bottom: 20),
// child: DeviceTile(
// onPressed: () {
// setState(() => selectedDevice = device);
// ledger.connect(device);
// },
// title: device.name,
// leading: imageLedger,
// connectionType: device.connectionType,
// ),
// ),
// )
// .toList(),
// PrimaryButton(
// text: "Refresh BLE",
// onPressed: () async {
// setState(() => devices = []);
// ledger.scan().listen((device) => setState(() {
// devices.add(device);
// }));
// },
// color: Theme.of(context).primaryColor,
// textColor: Colors.white),
// Padding(
// padding: EdgeInsets.only(top: 20),
// child: PrimaryButton(
// text: "Use USB",
// onPressed: () async {
// final dev = await ledger.listUsbDevices();
// setState(() => devices = dev);
// },
// color: Theme.of(context).primaryColor,
// textColor: Colors.white),
// ),
// ],
// ],
// ),
// )),
// );
// }
//
// Widget DebugButton(
// {required String title, required String method, required Future<dynamic> Function() func}) {
// return Padding(
// padding: EdgeInsets.only(top: 20),
// child: PrimaryButton(
// text: title,
// onPressed: () async {
// try {
// setState(() => status = "Sending...");
// final acc = await func();
// setState(() => status = "$method: $acc");
// print("$method: $acc");
// } on LedgerException catch (ex) {
// setState(() => status = "${ex.errorCode.toRadixString(16)} ${ex.message}");
// print("${ex.errorCode.toRadixString(16)} ${ex.message}");
// }
// },
// color: Theme.of(context).primaryColor,
// textColor: Colors.white),
// );
// }
// }
@@ -0,0 +1,256 @@
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/entities/generate_name.dart';
import 'package:cake_wallet/generated/i18n.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/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class SelectHardwareWalletAccountPage extends BasePage {
SelectHardwareWalletAccountPage(this._walletHardwareRestoreVM);
final WalletHardwareRestoreViewModel _walletHardwareRestoreVM;
@override
String get title => S.current.restore_title_from_hardware_wallet;
@override
Widget body(BuildContext context) => SelectHardwareWalletAccountForm(_walletHardwareRestoreVM);
}
class SelectHardwareWalletAccountForm extends StatefulWidget {
SelectHardwareWalletAccountForm(this._walletHardwareRestoreVM);
final WalletHardwareRestoreViewModel _walletHardwareRestoreVM;
@override
_SelectHardwareWalletAccountFormState createState() =>
_SelectHardwareWalletAccountFormState(_walletHardwareRestoreVM);
}
class _SelectHardwareWalletAccountFormState extends State<SelectHardwareWalletAccountForm> {
_SelectHardwareWalletAccountFormState(this._walletHardwareRestoreVM)
: _formKey = GlobalKey<FormState>(),
_controller = TextEditingController();
final GlobalKey<FormState> _formKey;
final WalletHardwareRestoreViewModel _walletHardwareRestoreVM;
final TextEditingController _controller;
@override
void initState() {
super.initState();
_setEffects(context);
if (_walletHardwareRestoreVM.availableAccounts.length == 0) _loadMoreAccounts();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content: Center(
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(top: 0),
child: Form(
key: _formKey,
child: Stack(
alignment: Alignment.centerRight,
children: [
TextFormField(
onChanged: (value) => _walletHardwareRestoreVM.name = value,
controller: _controller,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor,
),
hintText: S.of(context).wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color:
Theme.of(context).extension<NewWalletTheme>()!.underlineColor,
width: 1.0,
),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color:
Theme.of(context).extension<NewWalletTheme>()!.underlineColor,
width: 1.0,
),
),
suffixIcon: Semantics(
label: S.of(context).generate_name,
child: IconButton(
onPressed: () async {
final rName = await generateName();
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
_controller.text = rName;
_walletHardwareRestoreVM.name = rName;
_controller.selection = TextSelection.fromPosition(
TextPosition(offset: _controller.text.length));
});
},
icon: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
color: Theme.of(context).hintColor,
),
width: 34,
height: 34,
child: Image.asset(
'assets/images/refresh_icon.png',
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonIconColor,
),
),
),
),
),
validator: WalletNameValidator(),
),
],
),
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Container(
width: double.infinity,
child: Text(
"Available accounts",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
),
),
Observer(
builder: (context) => Column(
children: _walletHardwareRestoreVM.availableAccounts.map((acc) {
final address = acc.address;
return Padding(
padding: EdgeInsets.only(top: 10),
child: SelectButton(
image: Image.asset(
walletTypeToCryptoCurrency(_walletHardwareRestoreVM.type).iconPath ??
'',
height: 24,
width: 24,
),
text:
"${address.substring(0, 6)}...${address.substring(address.length - 6)}",
showTrailingIcon: false,
height: 54,
isSelected: _walletHardwareRestoreVM.selectedAccount == acc,
onTap: () =>
setState(() => _walletHardwareRestoreVM.selectedAccount = acc),
),
);
}).toList(),
),
),
Padding(
padding: EdgeInsets.only(top: 10),
child: Observer(builder: (context) {
return LoadingPrimaryButton(
onPressed: _loadMoreAccounts,
text: S.of(context).load_more,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isLoading: _walletHardwareRestoreVM.isLoadingMoreAccounts,
);
}),
),
],
),
),
),
bottomSectionPadding: EdgeInsets.all(24),
bottomSection: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: _confirmForm,
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white,
isDisabled: _walletHardwareRestoreVM.name.isEmpty,
);
},
),
),
);
}
Future<void> _loadMoreAccounts() async {
_walletHardwareRestoreVM.isLoadingMoreAccounts = true;
_walletHardwareRestoreVM.getNextAvailableAccounts(5);
}
Future<void> _confirmForm() async {
await _walletHardwareRestoreVM.create();
}
bool _effectsInstalled = false;
void _setEffects(BuildContext context) {
if (_effectsInstalled) return;
reaction((_) => _walletHardwareRestoreVM.error, (String? error) {
if (error != null) {
if (error == S.current.ledger_connection_error)
Navigator.of(context).pop();
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: error,
buttonText: S.of(context).ok,
buttonAction: () {
_walletHardwareRestoreVM.error = null;
Navigator.of(context).pop();
});
});
});
}
});
_effectsInstalled = true;
}
}
@@ -0,0 +1,78 @@
import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
import 'package:flutter/material.dart';
import 'package:ledger_flutter/ledger_flutter.dart';
class DeviceTile extends StatelessWidget {
const DeviceTile({
required this.onPressed,
required this.title,
this.leading,
this.connectionType,
});
final VoidCallback onPressed;
final String title;
final String? leading;
final ConnectionType? connectionType;
String? get connectionTypeIcon {
switch (connectionType) {
case ConnectionType.ble:
return 'assets/images/bluetooth.png';
case ConnectionType.usb:
return 'assets/images/usb.png';
case null:
return null;
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
width: double.infinity,
padding: EdgeInsets.all(24),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: Theme.of(context).cardColor,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
if (leading != null)
Image.asset(
leading!,
height: 30,
color: Theme.of(context).extension<OptionTileTheme>()!.titleColor,
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 16),
child: Text(
title,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<OptionTileTheme>()!.titleColor,
),
),
),
),
if (connectionTypeIcon != null)
Center(
child: Image.asset(
connectionTypeIcon!,
height: 25,
color: Theme.of(context).extension<OptionTileTheme>()!.titleColor,
),
)
],
),
),
);
}
}
@@ -45,6 +45,7 @@ class SideMenuItem extends StatelessWidget {
? Icon(
icon,
color: _setColor(context),
size: 30,
)
: Image.asset(
imagePath ?? '',
@@ -82,7 +82,8 @@ class DesktopSidebarWrapper extends BasePage {
width: sideMenuWidth,
topItems: [
SideMenuItem(
imagePath: 'assets/images/wallet_outline.png',
// imagePath: 'assets/images/wallet_outline.png',
icon: Icons.home,
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard,
onTap: () {
desktopSidebarViewModel.onPageChange(SidebarItem.dashboard);
@@ -148,6 +148,17 @@ class CryptoBalanceWidget extends StatelessWidget {
maxLines: 1,
textAlign: TextAlign.center,
),
if (dashboardViewModel.wallet.isHardwareWallet)
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(
'assets/images/ledger_nano.png',
width: 24,
color: Theme.of(context)
.extension<DashboardPageTheme>()!
.pageTitleTextColor,
),
),
if (dashboardViewModel
.balanceViewModel.isHomeScreenSettingsEnabled)
InkWell(
@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
@@ -10,14 +12,20 @@ import 'package:cake_wallet/themes/theme_base.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/hardware/device_connection_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
class NewWalletTypePage extends BasePage {
NewWalletTypePage({required this.onTypeSelected, required this.isCreate});
NewWalletTypePage({
required this.onTypeSelected,
required this.isCreate,
required this.isHardwareWallet,
});
final void Function(BuildContext, WalletType) onTypeSelected;
final bool isCreate;
final bool isHardwareWallet;
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
@@ -31,15 +39,22 @@ class NewWalletTypePage extends BasePage {
onTypeSelected: onTypeSelected,
walletImage: currentTheme.type == ThemeType.dark ? walletTypeImage : walletTypeLightImage,
isCreate: isCreate,
isHardwareWallet: isHardwareWallet,
);
}
class WalletTypeForm extends StatefulWidget {
WalletTypeForm({required this.onTypeSelected, required this.walletImage, required this.isCreate});
WalletTypeForm({
required this.onTypeSelected,
required this.walletImage,
required this.isCreate,
required this.isHardwareWallet,
});
final void Function(BuildContext, WalletType) onTypeSelected;
final Image walletImage;
final bool isCreate;
final bool isHardwareWallet;
@override
WalletTypeFormState createState() => WalletTypeFormState();
@@ -58,7 +73,11 @@ class WalletTypeFormState extends State<WalletTypeForm> {
@override
void initState() {
types = filteredTypes = availableWalletTypes;
types = filteredTypes = availableWalletTypes
.where((element) =>
!widget.isHardwareWallet ||
DeviceConnectionType.supportedConnectionTypes(element, Platform.isIOS).isNotEmpty)
.toList();
super.initState();
searchController.addListener(() {
@@ -74,76 +93,81 @@ class WalletTypeFormState extends State<WalletTypeForm> {
@override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 48),
child: Text(
S.of(context).choose_wallet_currency,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 48),
child: Text(
S.of(context).choose_wallet_currency,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
Padding(
padding: const EdgeInsets.fromLTRB(24, 24, 24, 12),
child: SearchBarWidget(searchController: searchController, borderRadius: 24),
),
Expanded(
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
...filteredTypes.map((type) => Padding(
padding: EdgeInsets.only(top: 12),
child: SelectButton(
image: Image.asset(
walletTypeToCryptoCurrency(type).iconPath ?? '',
height: 24,
width: 24),
text: walletTypeToDisplayName(type),
showTrailingIcon: false,
height: 54,
isSelected: selected == type,
onTap: () => setState(() => selected = type)),
))
],
),
),
Padding(
padding: EdgeInsets.fromLTRB(24, 24, 24, 12),
child: SearchBarWidget(searchController: searchController, borderRadius: 24),
),
Expanded(
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
...filteredTypes.map(
(type) => Padding(
padding: EdgeInsets.only(top: 12),
child: SelectButton(
image: Image.asset(
walletTypeToCryptoCurrency(type).iconPath ?? '',
height: 24,
width: 24,
),
text: walletTypeToDisplayName(type),
showTrailingIcon: false,
height: 54,
isSelected: selected == type,
onTap: () => setState(() => selected = type),
deviceConnectionTypes: widget.isHardwareWallet
? DeviceConnectionType.supportedConnectionTypes(type, Platform.isIOS)
: [],
),
),
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: PrimaryButton(
onPressed: () => onTypeSelected(),
text: S.of(context).seed_language_next,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isDisabled: selected == null,
),
),
],
),
],
)));
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: PrimaryButton(
onPressed: () => onTypeSelected(),
text: S.of(context).seed_language_next,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isDisabled: selected == null,
),
),
),
],
),
),
);
}
Future<void> onTypeSelected() async {
if (selected == null) {
throw Exception('Wallet Type is not selected yet.');
}
if (selected == null) throw Exception('Wallet Type is not selected yet.');
if (selected == WalletType.haven && widget.isCreate) {
return await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return PopUpCancellableAlertDialog(
contentText: S.of(context).pause_wallet_creation,
actionButtonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
builder: (BuildContext context) => PopUpCancellableAlertDialog(
contentText: S.of(context).pause_wallet_creation,
actionButtonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
),
);
}
@@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
import 'package:cw_core/hardware/device_connection_type.dart';
import 'package:flutter/material.dart';
class SelectButton extends StatelessWidget {
SelectButton({
@@ -16,6 +17,7 @@ class SelectButton extends StatelessWidget {
this.textColor,
this.arrowColor,
this.borderColor,
this.deviceConnectionTypes,
});
final Image? image;
@@ -24,6 +26,7 @@ class SelectButton extends StatelessWidget {
final bool isSelected;
final VoidCallback onTap;
final bool showTrailingIcon;
final List<DeviceConnectionType>? deviceConnectionTypes;
final double height;
final Color? color;
final Color? textColor;
@@ -33,15 +36,26 @@ class SelectButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final backgroundColor = color ?? (isSelected ? Colors.green : Theme.of(context).cardColor);
final effectiveTextColor = textColor ?? (isSelected
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor);
final effectiveArrowColor = arrowColor ?? (isSelected
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
: Theme.of(context).extension<FilterTheme>()!.titlesColor);
final effectiveTextColor = textColor ??
(isSelected
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor);
final effectiveArrowColor = arrowColor ??
(isSelected
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
: Theme.of(context).extension<FilterTheme>()!.titlesColor);
final selectArrowImage = Image.asset('assets/images/select_arrow.png',
color: effectiveArrowColor);
final trailingIcons = <Image>[];
final selectArrowImage =
Image.asset('assets/images/select_arrow.png', color: effectiveArrowColor);
deviceConnectionTypes?.forEach((element) => trailingIcons.add(Image.asset(
element.iconString,
color: effectiveArrowColor,
height: 24,
)));
if (showTrailingIcon) trailingIcons.add(selectArrowImage);
return GestureDetector(
onTap: onTap,
@@ -54,34 +68,33 @@ class SelectButton extends StatelessWidget {
borderRadius: BorderRadius.all(Radius.circular(30)),
color: backgroundColor,
border: borderColor != null ? Border.all(color: borderColor!) : null,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image ?? Offstage(),
Padding(
padding: image != null
? EdgeInsets.only(left: 15)
: EdgeInsets.only(left: 0),
child: Text(
text,
style: TextStyle(
fontSize: textSize,
fontWeight: FontWeight.w500,
color: effectiveTextColor,
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image ?? Offstage(),
Padding(
padding: image != null ? EdgeInsets.only(left: 15) : EdgeInsets.only(left: 0),
child: Text(
text,
style: TextStyle(
fontSize: textSize,
fontWeight: FontWeight.w500,
color: effectiveTextColor,
),
),
),
)
],
)
],
),
),
if (showTrailingIcon) selectArrowImage
...trailingIcons
],
),
),
@@ -1,19 +1,19 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/option_tile.dart';
import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
import 'package:cake_wallet/utils/permission_handler.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:cake_wallet/utils/permission_handler.dart';
class RestoreOptionsPage extends BasePage {
RestoreOptionsPage({required this.isNewInstall});
@@ -22,12 +22,15 @@ class RestoreOptionsPage extends BasePage {
String get title => S.current.restore_restore_wallet;
final bool isNewInstall;
final imageSeedKeys = Image.asset('assets/images/restore_wallet_image.png');
final imageBackup = Image.asset('assets/images/backup.png');
final qrCode = Image.asset('assets/images/restore_qr.png');
@override
Widget body(BuildContext context) {
final imageColor = Theme.of(context).extension<OptionTileTheme>()!.titleColor;
final imageLedger = Image.asset('assets/images/ledger_nano.png', width: 40, color: imageColor);
final imageSeedKeys = Image.asset('assets/images/restore_wallet_image.png', color: imageColor);
final imageBackup = Image.asset('assets/images/backup.png', color: imageColor);
final qrCode = Image.asset('assets/images/restore_qr.png', color: imageColor);
return Center(
child: Container(
width: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
@@ -37,53 +40,37 @@ class RestoreOptionsPage extends BasePage {
child: Column(
children: <Widget>[
OptionTile(
onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletFromSeedKeys,
arguments: isNewInstall),
image: imageSeedKeys,
title: S.of(context).restore_title_from_seed_keys,
description: S.of(context).restore_description_from_seed_keys),
onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletFromSeedKeys,
arguments: isNewInstall),
image: imageSeedKeys,
title: S.of(context).restore_title_from_seed_keys,
description: S.of(context).restore_description_from_seed_keys,
),
if (isNewInstall)
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup),
image: imageBackup,
title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup),
onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup),
image: imageBackup,
title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup,
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
onPressed: () async {
bool isCameraPermissionGranted =
await PermissionHandler.checkPermission(Permission.camera, context);
if (!isCameraPermissionGranted) return;
bool isPinSet = false;
if (isNewInstall) {
await Navigator.pushNamed(context, Routes.setupPin,
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
setupPinContext.close();
isPinSet = true;
});
}
if (!isNewInstall || isPinSet) {
try {
final restoreWallet =
await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
final restoreFromQRViewModel =
getIt.get<WalletRestorationFromQRVM>(param1: restoreWallet.type);
await restoreFromQRViewModel.create(restoreWallet: restoreWallet);
if (restoreFromQRViewModel.state is FailureState) {
_onWalletCreateFailure(context,
'Create wallet state: ${(restoreFromQRViewModel.state as FailureState).error}');
}
} catch (e) {
_onWalletCreateFailure(context, e.toString());
}
}
},
onPressed: () => Navigator.pushNamed(
context, Routes.restoreWalletFromHardwareWallet,
arguments: isNewInstall),
image: imageLedger,
title: S.of(context).restore_title_from_hardware_wallet,
description: S.of(context).restore_description_from_hardware_wallet,
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
onPressed: () => _onScanQRCode(context),
image: qrCode,
title: S.of(context).scan_qr_code,
description: S.of(context).cold_or_recover_wallet),
@@ -105,4 +92,35 @@ class RestoreOptionsPage extends BasePage {
buttonAction: () => Navigator.of(context).pop());
});
}
Future<void> _onScanQRCode(BuildContext context) async {
final isCameraPermissionGranted =
await PermissionHandler.checkPermission(Permission.camera, context);
if (!isCameraPermissionGranted) return;
bool isPinSet = false;
if (isNewInstall) {
await Navigator.pushNamed(context, Routes.setupPin,
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
setupPinContext.close();
isPinSet = true;
});
}
if (!isNewInstall || isPinSet) {
try {
final restoreWallet = await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
final restoreFromQRViewModel =
getIt.get<WalletRestorationFromQRVM>(param1: restoreWallet.type);
await restoreFromQRViewModel.create(restoreWallet: restoreWallet);
if (restoreFromQRViewModel.state is FailureState) {
_onWalletCreateFailure(context,
'Create wallet state: ${(restoreFromQRViewModel.state as FailureState).error}');
}
} catch (e) {
_onWalletCreateFailure(context, e.toString());
}
}
}
}
+54 -14
View File
@@ -1,39 +1,40 @@
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/template.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
import 'package:cake_wallet/src/widgets/add_template_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart';
import 'package:cake_wallet/src/widgets/trail_button.dart';
import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/request_review_handler.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/wallet_type.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/crypto_currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/trail_button.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:cw_core/crypto_currency.dart';
class SendPage extends BasePage {
SendPage({
@@ -369,6 +370,21 @@ class SendPage extends BasePage {
return;
}
if (sendViewModel.wallet.isHardwareWallet) {
if (!sendViewModel.ledgerViewModel.isConnected) {
await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams(
walletType: sendViewModel.walletType,
onConnectDevice: (BuildContext context, _) {
sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet);
Navigator.of(context).pop();
},
));
} else {
sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet);
}
}
final check = sendViewModel.shouldDisplayTotp();
authService.authenticateAction(
context,
@@ -384,7 +400,8 @@ class SendPage extends BasePage {
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isLoading: sendViewModel.state is IsExecutingState ||
sendViewModel.state is TransactionCommitting,
sendViewModel.state is TransactionCommitting ||
sendViewModel.state is IsAwaitingDeviceResponseState,
isDisabled: !sendViewModel.isReadyForSend,
);
},
@@ -395,12 +412,20 @@ class SendPage extends BasePage {
);
}
BuildContext? dialogContext;
void _setEffects(BuildContext context) {
if (_effectsInstalled) {
return;
}
reaction((_) => sendViewModel.state, (ExecutionState state) {
if (dialogContext != null && dialogContext?.mounted == true) {
Navigator.of(dialogContext!).pop();
}
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
@@ -510,6 +535,21 @@ class SendPage extends BasePage {
sendViewModel.clearOutputs();
});
}
if (state is IsAwaitingDeviceResponseState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
dialogContext = context;
return AlertWithOneAction(
alertTitle: S.of(context).proceed_on_device,
alertContent: S.of(context).proceed_on_device_description,
buttonText: S.of(context).cancel,
buttonAction: () => Navigator.of(context).pop());
});
});
}
});
_effectsInstalled = true;
@@ -1,10 +1,14 @@
import 'dart:io';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/wallet_connect_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -12,10 +16,6 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cw_core/battery_optimization_native.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class ConnectionSyncPage extends BasePage {
@@ -106,7 +106,8 @@ class ConnectionSyncPage extends BasePage {
);
},
),
if (isWalletConnectCompatibleChain(dashboardViewModel.wallet.type)) ...[
if (isWalletConnectCompatibleChain(dashboardViewModel.wallet.type) &&
!dashboardViewModel.wallet.isHardwareWallet) ...[ // ToDo: Remove this line once WalletConnect is implemented
WalletConnectTile(
onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing),
),
@@ -509,7 +509,9 @@ abstract class DashboardViewModelBase with Store {
final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type);
final jsonSource = await read(path: path, password: password);
final data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String;
final mnemonic = data['mnemonic'] as String?;
if (mnemonic == null) continue;
final hash = await Cryptography.instance.sha256().hash(utf8.encode(mnemonic));
final seedSha = bytesToHex(hash.bytes);
@@ -0,0 +1,66 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:ledger_flutter/ledger_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
class LedgerViewModel {
final Ledger ledger = Ledger(
options: LedgerOptions(
scanMode: ScanMode.balanced,
maxScanDuration: const Duration(minutes: 5),
),
onPermissionRequest: (_) async {
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.bluetoothAdvertise,
].request();
return statuses.values.where((status) => status.isDenied).isEmpty;
},
);
Future<void> connectLedger(LedgerDevice device) async {
await ledger.connect(device);
if (device.connectionType == ConnectionType.usb) _device = device;
}
LedgerDevice? _device;
bool get isConnected => ledger.devices.isNotEmpty || _device != null;
LedgerDevice get device => _device ?? ledger.devices.first;
void setLedger(WalletBase wallet) {
switch (wallet.type) {
case WalletType.bitcoin:
return bitcoin!.setLedger(wallet, ledger, device);
case WalletType.ethereum:
return ethereum!.setLedger(wallet, ledger, device);
case WalletType.polygon:
return polygon!.setLedger(wallet, ledger, device);
default:
throw Exception('Unexpected wallet type: ${wallet.type}');
}
}
String? interpretErrorCode(String errorCode) {
switch (errorCode) {
case "6985":
return S.current.ledger_error_tx_rejected_by_user;
case "5515":
return S.current.ledger_error_device_locked;
case "6d02": // UNKNOWN_APDU
case "6511":
case "6e00":
return S.current.ledger_error_wrong_app;
default:
return null;
}
}
}
+19 -6
View File
@@ -15,11 +15,13 @@ import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cw_core/exceptions.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter/ledger_flutter.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/template.dart';
import 'package:cake_wallet/core/address_validator.dart';
@@ -63,6 +65,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
this.balanceViewModel,
this.contactListViewModel,
this.transactionDescriptionBox,
this.ledgerViewModel,
) : state = InitialExecutionState(),
currencies = appStore.wallet!.balance.keys.toList(),
selectedCryptoCurrency = appStore.wallet!.currency,
@@ -266,6 +269,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
final SendTemplateViewModel sendTemplateViewModel;
final BalanceViewModel balanceViewModel;
final ContactListViewModel contactListViewModel;
final LedgerViewModel ledgerViewModel;
final FiatConversionStore _fiatConversationStore;
final Box<TransactionDescription> transactionDescriptionBox;
@@ -340,7 +344,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
try {
state = IsExecutingState();
if (wallet.isHardwareWallet) state = IsAwaitingDeviceResponseState();
pendingTransaction = await wallet.createTransaction(_credentials());
if (provider is ThorChainExchangeProvider) {
final outputCount = pendingTransaction?.outputCount ?? 0;
if (outputCount > 10) {
@@ -354,7 +361,15 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
state = ExecutedSuccessfullyState();
return pendingTransaction;
} catch (e) {
state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));
if (e is LedgerException) {
final errorCode = e.errorCode.toRadixString(16);
final fallbackMsg = e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode";
final errorMsg = ledgerViewModel.interpretErrorCode(errorCode) ?? fallbackMsg;
state = FailureState(errorMsg);
} else {
state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));
}
}
return null;
}
@@ -390,15 +405,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
String address = outputs.fold('', (acc, value) {
return value.isParsedAddress
? acc + value.address + '\n' + value.extractedAddress + '\n\n'
: acc + value.address + '\n\n';
? '$acc${value.address}\n${value.extractedAddress}\n\n'
: '$acc${value.address}\n\n';
});
address = address.trim();
String note = outputs.fold('', (acc, value) {
return acc + value.note + '\n';
});
String note = outputs.fold('', (acc, value) => '$acc${value.note}\n');
note = note.trim();
@@ -1,4 +1,5 @@
import 'package:cake_wallet/core/execution_state.dart';
class IsAwaitingDeviceResponseState extends IsExecutingState {}
class TransactionCommitting extends ExecutionState {}
class TransactionCommitted extends ExecutionState {}
+2 -1
View File
@@ -72,6 +72,7 @@ abstract class WalletCreationVMBase with Store {
address: '',
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(),
hardwareWalletType: credentials.hardwareWalletType,
);
credentials.walletInfo = walletInfo;
@@ -99,7 +100,7 @@ abstract class WalletCreationVMBase with Store {
case WalletType.litecoin:
return DerivationInfo(
derivationType: DerivationType.electrum,
derivationPath: "m/0'/0",
derivationPath: "m/0'",
);
default:
return null;
@@ -0,0 +1,110 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
import 'package:cw_core/hardware/hardware_account_data.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter/ledger_flutter.dart';
import 'package:mobx/mobx.dart';
part 'wallet_hardware_restore_view_model.g.dart';
class WalletHardwareRestoreViewModel = WalletHardwareRestoreViewModelBase
with _$WalletHardwareRestoreViewModel;
abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with Store {
final LedgerViewModel ledgerViewModel;
int _nextIndex = 0;
WalletHardwareRestoreViewModelBase(this.ledgerViewModel, AppStore appStore,
WalletCreationService walletCreationService, Box<WalletInfo> walletInfoSource,
{required WalletType type})
: super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true);
@observable
String name = "";
@observable
HardwareAccountData? selectedAccount = null;
@observable
bool isLoadingMoreAccounts = false;
@observable
String? error = null;
// @observable
ObservableList<HardwareAccountData> availableAccounts = ObservableList();
@action
Future<void> getNextAvailableAccounts(int limit) async {
try {
List<HardwareAccountData> accounts;
switch (type) {
case WalletType.bitcoin:
accounts = await bitcoin!
.getHardwareWalletAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
break;
case WalletType.ethereum:
accounts = await ethereum!
.getHardwareWalletAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
break;
case WalletType.polygon:
accounts = await polygon!
.getHardwareWalletAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
break;
default:
return;
}
availableAccounts.addAll(accounts);
_nextIndex += limit;
} on LedgerException catch (e) {
error = ledgerViewModel.interpretErrorCode(e.errorCode.toRadixString(16));
} catch (e) {
error = S.current.ledger_connection_error;
}
isLoadingMoreAccounts = false;
_nextIndex += limit;
}
@override
WalletCredentials getCredentials(dynamic _options) {
WalletCredentials credentials;
switch (type) {
case WalletType.bitcoin:
credentials =
bitcoin!.createBitcoinHardwareWalletCredentials(name: name, accountData: selectedAccount!);
break;
case WalletType.ethereum:
credentials =
ethereum!.createEthereumHardwareWalletCredentials(name: name, hwAccountData: selectedAccount!);
break;
case WalletType.polygon:
credentials = polygon!.createPolygonHardwareWalletCredentials(name: name, hwAccountData: selectedAccount!);
break;
default:
throw Exception('Unexpected type: ${type.toString()}');
}
credentials.hardwareWalletType = HardwareWalletType.ledger;
return credentials;
}
@override
Future<WalletBase> process(WalletCredentials credentials) async {
walletCreationService.changeWalletType(type: type);
return walletCreationService.restoreFromHardwareWallet(credentials);
}
}