Bitcoin derivations (#1089)

* - Update and Fix Conflicts with main

* Add Balances for ERC20 tokens

* Fix conflicts with main

* Add erc20 abi json

* Add send erc20 tokens initial function

* add missing getHeightByDate in Haven [skip ci]

* Allow contacts and wallets from the same tag

* Add Shiba Inu icon

* Add send ERC-20 tokens initial flow

* Add missing import in generated file

* Add initial approach for transaction sending for ERC-20 tokens

* Refactor signing/sending transactions

* Add initial flow for transactions subscription

* Refactor signing/sending transactions

* Add home settings icon

* Fix conflicts with main

* Initial flow for home settings

* Add logic flow for adding erc20 tokens

* Fix initial UI

* Finalize UI for Tokens

* Integrate UI with Ethereum flow

* Add "Enable/Disable" feature for ERC20 tokens

* Add initial Erc20 tokens

* Add Sorting and Pin Native Token features

* Fix price sorting

* Sort tokens list as well when Sort criteria changes

* - Improve sorting balances flow
- Add initial add token from search bar flow

* Fix Accounts Popup UI

* Fix Pin native token

* Fix Enabling/Disabling tokens
Fix sorting by fiat once app is opened
Improve token availability mechanism

* Fix deleting token
Fix renaming tokens

* Fix issue with search

* Add more tokens

* - Fix scroll issue
- Add ERC20 tokens placeholder image in picker

* - Separate and organize default erc20 tokens
- Fix scrolling
- Add token placeholder images in picker
- Sort disabled tokens alphabetically

* Change BNB token initial availability [skip ci]

* Fix Conflicts with main

* Fix Conflicts with main

* Add Verse ERC20 token to the initial tokens list

* Add rename wallet to Ethereum

* Integrate EtherScan API for fetching address transactions
Generate Ethereum specific secrets in Ethereum package

* Adjust transactions fiat price for ERC20 tokens

* Free Up GitHub Actions Ubuntu Runner Disk Space

* Free Up GitHub Actions Ubuntu Runner Disk space (trial 2)

* Fix Transaction Fee display

* Save transaction history

* Enhance loading time for erc20 tokens transactions

* Minor Fixes and Enhancements

* Fix sending erc20
fix block explorer issue

* Fix int overflow

* Fix transaction amount conversions

* Minor: `slow` -> `Slow` [skip-ci]

* initial changes

* more base config stuff

* config changes

* successfully builds!

* save

* successfully add nano wallet

* save

* seed generation

* receive screen + node screen working

* tx history working and fiat fixes

* balance working

* derivation updates

* nano-unfinished

* sends working

* remove fees from send screen, send and receive transactions working

* fixes + auto receive incoming txs

* fix for scanning QR codes

* save

* update translations

* fixes

* more fixes

* more strings

* small fix

* fix github actions workflow

* potential fix

* potential fix

* ci/cd fix

* change rep working

* seed generation fixes

* fixes

* save

* change rep screen functional

* save

* banano changes

* fixes, start adding ui for PoW

* pow node changes

* update translations

* fix

* account changing barely working

* save

* disable account generation

* small fix

* save

* UI work

* save

* fixes after merge main

* fixes

* remove monero stuff, work on derivation ui

* lots of fixes + finish up seed derivation

* last minute fixes

* node related fixes

* more fixes

* small fix

* more fixes

* fixes

* pretty big refactor for pow, still some bugs

* finally works!

* get transactions after send

* fix

* merge conflict fixes

* save

* fix pow node showing up twice

* done

* initial changes

* small fix

* more merge fixes

* fixes

* more fixes

* fix

* save

* fix manage pow nodes setting appearing on other wallets

* fix contact bug

* fixes

* fiat fixes

* save

* save

* save

* save

* updates

* cleanup

* restore fix

* fixes

* remove deprecated alert

* fix

* small fix

* remove outdated warning

* electrum restore fixes

* fixes

* fixes

* fix

* derivation fixes

* nano fixes pt.1

* nano fixes pt.2

* bip39 fixes

* pownode refactor

* nodes pages fixes

* observer fix

* ssl fix

* remove old references

* remove unused imports

* code cleanup

* small fix

* small potential fix

* save

* derivation fixes

* deterministic fix

* fix pt.2

* derivation class fixes

* review fixes from nano that also apply here

* formatting

* stuff that should've stayed deleted

* post merge fixes

* remove problematic imports and duplicate changes

* Delete lib/nano/nano.dart

* move wallet restore page proxy code to the view model

* fix dashboard page indicators being the same color

* debatably better refactoring of derivationInfo, migration needed

* additional refactor improvements

* blanket comment some stuff out to narrow down this issue

* refactor fixes

* fix nano exchange

* fix , bug, i.e. replace , with . when making a nano transaction

* fix nano sending, update restore page wording, and other minor fixes

* write migration for existing bitcoin and nano wallets

* merge fixes

* minor fixes

* use default derivation type when restoring from qr code

* fixes for restoring

* fixes

* fixes

* merge fix

* Fix issues with Creating Electrum and Restoring Bip39

* updates & fixes

* Add missing case for no transactions BIP39 wallet restore

* Make the default BIP39 the 84 derivation path

* Add Samourai Deposit

* litecoin mnemonic error fix

* Bip39 passphrase support (#1412)

* save

* passphrase working

* fix for when loading wallets + translation update

* minor fix

* Fix Nano

* minor fix [skip ci]

---------

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

* change error state seed conditions into throwables [skip ci]

* litecoin fixes

* Bip39 minor enhancements (#1416)

* minor enhancements

* rename bitcoin_derivations -> electrum_derivations

* Remove duplicate derivations
handle default case

* minor fix

* Enable passphrase for Litecoin

* obscure text of passphrase

---------

Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
Co-authored-by: Justin Ehrenhofer <justin.ehrenhofer@gmail.com>
Co-authored-by: fossephate <fosse@book.local>
This commit is contained in:
Matthew Fosse
2024-04-30 03:49:56 +03:00
committed by GitHub
co-authored by OmarHatem Justin Ehrenhofer fossephate
parent 9e4a7f4331
commit 509b92e97f
60 changed files with 908 additions and 341 deletions
@@ -1,8 +1,5 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:flutter/material.dart';
@@ -13,15 +10,14 @@ class WalletRestoreChooseDerivationPage extends BasePage {
WalletRestoreChooseDerivationPage(this.walletRestoreChooseDerivationViewModel) {}
@override
Widget middle(BuildContext context) => Observer(
builder: (_) => Text(
S.current.choose_derivation,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: titleColor(context)),
));
Widget middle(BuildContext context) => Text(
S.current.choose_derivation,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: titleColor(context)),
);
final WalletRestoreChooseDerivationViewModel walletRestoreChooseDerivationViewModel;
DerivationType derivationType = DerivationType.unknown;
@@ -105,7 +101,7 @@ class WalletRestoreChooseDerivationPage extends BasePage {
),
),
Text(
"${S.current.transactions}: ${derivation.height}",
"${S.current.transactions}: ${derivation.transactionsCount}",
style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith(
fontSize: 16,
fontWeight: FontWeight.w500,
@@ -20,6 +20,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
{Key? key,
required this.displayLanguageSelector,
required this.displayBlockHeightSelector,
required this.displayPassphrase,
required this.type,
required this.seedTypeViewModel,
this.blockHeightFocusNode,
@@ -31,6 +32,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
final WalletType type;
final bool displayLanguageSelector;
final bool displayBlockHeightSelector;
final bool displayPassphrase;
final SeedTypeViewModel seedTypeViewModel;
final FocusNode? blockHeightFocusNode;
final Function(bool)? onHeightOrDateEntered;
@@ -48,6 +50,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
formKey = GlobalKey<FormState>(),
languageController = TextEditingController(),
nameTextEditingController = TextEditingController(),
passphraseController = TextEditingController(),
seedTypeController = TextEditingController();
final GlobalKey<SeedWidgetState> seedWidgetStateKey;
@@ -55,6 +58,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
final TextEditingController languageController;
final TextEditingController nameTextEditingController;
final TextEditingController seedTypeController;
final TextEditingController passphraseController;
final GlobalKey<FormState> formKey;
late ReactionDisposer moneroSeedTypeReaction;
String language;
@@ -166,15 +170,15 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
),
if (widget.displayLanguageSelector)
GestureDetector(
onTap: () async {
await showPopUp<void>(
context: context,
builder: (_) => SeedLanguagePicker(
selected: language,
onItemSelected: _changeLanguage,
seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy,
));
},
onTap: () async {
await showPopUp<void>(
context: context,
builder: (_) => SeedLanguagePicker(
selected: language,
onItemSelected: _changeLanguage,
seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy,
));
},
child: Container(
color: Colors.transparent,
padding: EdgeInsets.only(top: 20.0),
@@ -194,6 +198,14 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
key: blockchainHeightKey,
onHeightOrDateEntered: widget.onHeightOrDateEntered,
hasDatePicker: widget.type == WalletType.monero),
if (widget.displayPassphrase) ...[
const SizedBox(height: 10),
BaseTextFormField(
hintText: S.current.passphrase,
controller: passphraseController,
obscureText: true,
),
]
]));
}
@@ -1,7 +1,5 @@
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/nano/nano.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart';
@@ -9,7 +7,6 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.da
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
@@ -17,7 +14,6 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
import 'package:cw_core/nano_account_info_response.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
@@ -42,6 +38,7 @@ class WalletRestorePage extends BasePage {
displayBlockHeightSelector:
walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector,
displayPassphrase: walletRestoreViewModel.hasPassphrase,
type: walletRestoreViewModel.type,
key: walletRestoreFromSeedFormKey,
blockHeightFocusNode: _blockHeightFocusNode,
@@ -99,8 +96,10 @@ class WalletRestorePage extends BasePage {
final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
final FocusNode _blockHeightFocusNode;
DerivationType derivationType = DerivationType.unknown;
String? derivationPath = null;
// DerivationType derivationType = DerivationType.unknown;
// String? derivationPath = null;
DerivationInfo? derivationInfo;
@override
Widget body(BuildContext context) {
@@ -298,6 +297,11 @@ class WalletRestorePage extends BasePage {
-1;
}
if (walletRestoreViewModel.hasPassphrase) {
credentials['passphrase'] =
walletRestoreFromSeedFormKey.currentState!.passphraseController.text;
}
credentials['name'] =
walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
} else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
@@ -318,58 +322,11 @@ class WalletRestorePage extends BasePage {
}
}
credentials['derivationType'] = this.derivationType;
credentials['derivationPath'] = this.derivationPath;
credentials['derivationInfo'] = this.derivationInfo;
credentials['walletType'] = walletRestoreViewModel.type;
return credentials;
}
Future<List<DerivationInfo>> getDerivationInfo(dynamic credentials) async {
var list = <DerivationInfo>[];
var walletType = credentials["walletType"] as WalletType;
var appStore = getIt.get<AppStore>();
var node = appStore.settingsStore.getCurrentNode(walletType);
switch (walletType) {
case WalletType.nano:
String? mnemonic = credentials['seed'] as String?;
String? seedKey = credentials['private_key'] as String?;
AccountInfoResponse? bip39Info = await nanoUtil!.getInfoFromSeedOrMnemonic(
DerivationType.bip39,
mnemonic: mnemonic,
seedKey: seedKey,
node: node);
AccountInfoResponse? standardInfo = await nanoUtil!.getInfoFromSeedOrMnemonic(
DerivationType.nano,
mnemonic: mnemonic,
seedKey: seedKey,
node: node,
);
if (standardInfo?.balance != null) {
list.add(DerivationInfo(
derivationType: DerivationType.nano,
balance: nanoUtil!.getRawAsUsableString(standardInfo!.balance, nanoUtil!.rawPerNano),
address: standardInfo.address!,
height: standardInfo.confirmationHeight,
));
}
if (bip39Info?.balance != null) {
list.add(DerivationInfo(
derivationType: DerivationType.bip39,
balance: nanoUtil!.getRawAsUsableString(bip39Info!.balance, nanoUtil!.rawPerNano),
address: bip39Info.address!,
height: bip39Info.confirmationHeight,
));
}
break;
default:
break;
}
return list;
}
Future<void> _confirmForm(BuildContext context) async {
// Dismissing all visible keyboard to provide context for navigation
FocusManager.instance.primaryFocus?.unfocus();
@@ -398,51 +355,46 @@ class WalletRestorePage extends BasePage {
walletRestoreViewModel.state = IsExecutingState();
List<DerivationType> derivationTypes =
await walletRestoreViewModel.getDerivationTypes(_credentials());
DerivationInfo? dInfo;
if (derivationTypes[0] == DerivationType.unknown || derivationTypes.length > 1) {
// push screen to choose the derivation type:
List<DerivationInfo> derivations = await getDerivationInfo(_credentials());
// get info about the different derivations:
List<DerivationInfo> derivations =
await walletRestoreViewModel.getDerivationInfo(_credentials());
int derivationsWithHistory = 0;
int derivationWithHistoryIndex = 0;
for (int i = 0; i < derivations.length; i++) {
if (derivations[i].height > 0) {
derivationsWithHistory++;
derivationWithHistoryIndex = i;
}
int derivationsWithHistory = 0;
int derivationWithHistoryIndex = 0;
for (int i = 0; i < derivations.length; i++) {
if (derivations[i].transactionsCount > 0) {
derivationsWithHistory++;
derivationWithHistoryIndex = i;
}
DerivationInfo? derivationInfo;
if (derivationsWithHistory > 1) {
derivationInfo = await Navigator.of(context).pushNamed(Routes.restoreWalletChooseDerivation,
arguments: derivations) as DerivationInfo?;
} else if (derivationsWithHistory == 1) {
derivationInfo = derivations[derivationWithHistoryIndex];
} else if (derivationsWithHistory == 0) {
// default derivation:
derivationInfo = DerivationInfo(
derivationType: derivationTypes[0],
derivationPath: "m/0'/1",
height: 0,
);
}
if (derivationInfo == null) {
walletRestoreViewModel.state = InitialExecutionState();
return;
}
this.derivationType = derivationInfo.derivationType;
this.derivationPath = derivationInfo.derivationPath;
} else {
// electrum derivation:
this.derivationType = derivationTypes[0];
this.derivationPath = "m/0'/1";
}
walletRestoreViewModel.state = InitialExecutionState();
if (derivationsWithHistory > 1) {
dInfo = await Navigator.of(context).pushNamed(
Routes.restoreWalletChooseDerivation,
arguments: derivations,
) as DerivationInfo?;
} else if (derivationsWithHistory == 1) {
dInfo = derivations[derivationWithHistoryIndex];
}
// get the default derivation for this wallet type:
if (dInfo == null) {
// we only return 1 derivation if we're pretty sure we know which one to use:
if (derivations.length == 1) {
dInfo = derivations.first;
} else {
// if we have multiple possible derivations, and none have histories
// we just default to the most common one:
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
}
}
this.derivationInfo = dInfo;
if (this.derivationInfo == null) {
this.derivationInfo = walletRestoreViewModel.getDefaultDerivation();
}
walletRestoreViewModel.create(options: _credentials());
}
@@ -343,7 +343,9 @@ class WalletListBodyState extends State<WalletListBody> {
});
}
} catch (e) {
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
if (this.mounted) {
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
}
},
conditionToDetermineIfToUse2FA:
@@ -22,6 +22,7 @@ class BaseTextFormField extends StatelessWidget {
this.enabled = true,
this.readOnly = false,
this.enableInteractiveSelection = true,
this.obscureText = false,
this.validator,
this.textStyle,
this.placeholderTextStyle,
@@ -57,6 +58,7 @@ class BaseTextFormField extends StatelessWidget {
final String? initialValue;
final double borderWidth;
final void Function(String)? onSubmit;
final bool obscureText;
@override
Widget build(BuildContext context) {
@@ -70,6 +72,7 @@ class BaseTextFormField extends StatelessWidget {
textInputAction: textInputAction,
textAlign: textAlign,
autovalidateMode: autovalidateMode,
obscureText: obscureText,
maxLines: maxLines,
inputFormatters: inputFormatters,
enabled: enabled,