Generic fixes (#1304)

* fix mobx no element error

* fix mobx issue

* Remove unused code

* Enhance error handling for monero sync failure case

* Separate litecoin mnemonic exception from bitcoin

* - Enable onramper for polygon
- Add Kaspa validation

* Set null as the default length of address validation

* Modify EVM fee text [skip ci]

* Add seed length option to polygon

* Add digibyte

* Update configure_cake_wallet.sh and fix conflicts

* Pin bottom section

* Fix Solana missing isTestnet param
This commit is contained in:
Omar Hatem 2024-02-23 19:09:24 +02:00 committed by GitHub
parent a3a35f05e1
commit cbc0c3afd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 84 additions and 70 deletions

1
.gitignore vendored
View file

@ -86,6 +86,7 @@ cw_monero/cw_monero/android/.cxx/
**/*.g.dart **/*.g.dart
android/key.properties android/key.properties
android/app/key.jks
**/tool/.secrets-prod.json **/tool/.secrets-prod.json
**/tool/.secrets-test.json **/tool/.secrets-test.json

BIN
assets/images/digibyte.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -30,6 +30,7 @@ cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_solana && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_ethereum && flutter pub get && cd .. cd cw_ethereum && flutter pub get && cd ..
cd cw_polygon && flutter pub get && cd .. cd cw_polygon && flutter pub get && cd ..
flutter packages pub run build_runner build --delete-conflicting-outputs flutter packages pub run build_runner build --delete-conflicting-outputs

View file

@ -1,7 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart'; import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';

View file

@ -2,7 +2,7 @@ import 'dart:io';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart'; import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
import 'package:cw_bitcoin/litecoin_wallet.dart'; import 'package:cw_bitcoin/litecoin_wallet.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
@ -101,7 +101,7 @@ class LitecoinWalletService extends WalletService<
Future<LitecoinWallet> restoreFromSeed( Future<LitecoinWallet> restoreFromSeed(
BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
if (!validateMnemonic(credentials.mnemonic)) { if (!validateMnemonic(credentials.mnemonic)) {
throw BitcoinMnemonicIsIncorrectException(); throw LitecoinMnemonicIsIncorrectException();
} }
final wallet = await LitecoinWalletBase.create( final wallet = await LitecoinWalletBase.create(

View file

@ -3,3 +3,9 @@ class BitcoinMnemonicIsIncorrectException implements Exception {
String toString() => String toString() =>
'Bitcoin mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.'; 'Bitcoin mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
} }
class LitecoinMnemonicIsIncorrectException implements Exception {
@override
String toString() =>
'Litecoin mnemonic has incorrect format. Mnemonic should contain 24 words separated by space.';
}

View file

@ -9,7 +9,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
required this.decimals, required this.decimals,
this.fullName, this.fullName,
this.iconPath, this.iconPath,
this.tag, this.enabled = false, this.tag,
this.enabled = false,
}) })
: super(title: title, raw: raw); : super(title: title, raw: raw);
@ -100,6 +101,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
CryptoCurrency.usdtPoly, CryptoCurrency.usdtPoly,
CryptoCurrency.usdcEPoly, CryptoCurrency.usdcEPoly,
CryptoCurrency.kaspa, CryptoCurrency.kaspa,
CryptoCurrency.digibyte,
]; ];
static const havenCurrencies = [ static const havenCurrencies = [
@ -211,8 +213,9 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const banano = CryptoCurrency(title: 'BAN', fullName: 'Banano', raw: 86, name: 'banano', iconPath: 'assets/images/nano_icon.png', decimals: 29); static const banano = CryptoCurrency(title: 'BAN', fullName: 'Banano', raw: 86, name: 'banano', iconPath: 'assets/images/nano_icon.png', decimals: 29);
static const usdtPoly = CryptoCurrency(title: 'USDT', tag: 'POLY', fullName: 'Tether USD (PoS)', raw: 87, name: 'usdtpoly', iconPath: 'assets/images/usdt_icon.png', decimals: 6); static const usdtPoly = CryptoCurrency(title: 'USDT', tag: 'POLY', fullName: 'Tether USD (PoS)', raw: 87, name: 'usdtpoly', iconPath: 'assets/images/usdt_icon.png', decimals: 6);
static const usdcEPoly = CryptoCurrency(title: 'USDC.E', tag: 'POLY', fullName: 'USD Coin (PoS)', raw: 88, name: 'usdcepoly', iconPath: 'assets/images/usdc_icon.png', decimals: 6); static const usdcEPoly = CryptoCurrency(title: 'USDC.E', tag: 'POLY', fullName: 'USD Coin (PoS)', raw: 88, name: 'usdcepoly', iconPath: 'assets/images/usdc_icon.png', decimals: 6);
static const kaspa = CryptoCurrency(title: 'KAS', fullName: 'Kaspa', raw: 89, name: 'kaspa', iconPath: 'assets/images/kaspa_icon.png', decimals: 8); static const kaspa = CryptoCurrency(title: 'KAS', fullName: 'Kaspa', raw: 89, name: 'kas', iconPath: 'assets/images/kaspa_icon.png', decimals: 8);
static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'USDT Tether', raw: 90, name: 'usdtsol', iconPath: 'assets/images/usdt_icon.png', decimals: 6); static const digibyte = CryptoCurrency(title: 'DGB', fullName: 'DigiByte', raw: 90, name: 'dgb', iconPath: 'assets/images/digibyte.png', decimals: 8);
static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'USDT Tether', raw: 90, name: 'usdtsol', iconPath: 'assets/images/usdt_icon.png', decimals: 6);
static final Map<int, CryptoCurrency> _rawCurrencyMap = static final Map<int, CryptoCurrency> _rawCurrencyMap =

View file

@ -14,6 +14,7 @@ abstract class EVMChainWalletAddressesBase extends WalletAddresses with Store {
super(walletInfo); super(walletInfo);
@override @override
@observable
String address; String address;
@override @override

View file

@ -576,15 +576,19 @@ abstract class MoneroWalletBase
return; return;
} }
final height = _getHeightByDate(walletInfo.date); int height = 0;
try {
if (height > MIN_RESTORE_HEIGHT) { height = _getHeightByDate(walletInfo.date);
monero_wallet.setRecoveringFromSeed(isRecovery: true); } catch (e, s) {
monero_wallet.setRefreshFromBlockHeight(height: height); onError?.call(FlutterErrorDetails(
return; exception: e,
stack: s,
library: this.runtimeType.toString(),
));
} }
throw Exception("height isn't > $MIN_RESTORE_HEIGHT!"); monero_wallet.setRecoveringFromSeed(isRecovery: true);
monero_wallet.setRefreshFromBlockHeight(height: height);
} }
int _getHeightDistance(DateTime date) { int _getHeightDistance(DateTime date) {
@ -600,7 +604,7 @@ abstract class MoneroWalletBase
final heightDistance = _getHeightDistance(date); final heightDistance = _getHeightDistance(date);
if (nodeHeight <= 0) { if (nodeHeight <= 0) {
// the node returned 0 (an error state), so lets just restore from cache: // the node returned 0 (an error state)
throw Exception("nodeHeight is <= 0!"); throw Exception("nodeHeight is <= 0!");
} }

View file

@ -25,7 +25,6 @@ class DefaultSPLTokens {
mintAddress: '2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk', mintAddress: '2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk',
decimal: 6, decimal: 6,
mint: 'soEth', mint: 'soEth',
enabled: true,
iconPath: 'assets/images/eth_icon.png', iconPath: 'assets/images/eth_icon.png',
), ),
SPLToken( SPLToken(
@ -34,7 +33,6 @@ class DefaultSPLTokens {
mintAddress: 'So11111111111111111111111111111111111111112', mintAddress: 'So11111111111111111111111111111111111111112',
decimal: 9, decimal: 9,
mint: 'WSOL', mint: 'WSOL',
enabled: true,
iconPath: 'assets/images/sol_icon.png', iconPath: 'assets/images/sol_icon.png',
), ),
SPLToken( SPLToken(

View file

@ -19,7 +19,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
@override @override
Future<SolanaWallet> create(SolanaNewWalletCredentials credentials) async { Future<SolanaWallet> create(SolanaNewWalletCredentials credentials, {bool? isTestnet}) async {
final strength = credentials.seedPhraseLength == 24 ? 256 : 128; final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
final mnemonic = bip39.generateMnemonic(strength: strength); final mnemonic = bip39.generateMnemonic(strength: strength);
@ -67,7 +67,8 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
} }
@override @override
Future<SolanaWallet> restoreFromKeys(SolanaRestoreWalletFromPrivateKey credentials) async { Future<SolanaWallet> restoreFromKeys(SolanaRestoreWalletFromPrivateKey credentials,
{bool? isTestnet}) async {
final wallet = SolanaWallet( final wallet = SolanaWallet(
password: credentials.password!, password: credentials.password!,
privateKey: credentials.privateKey, privateKey: credentials.privateKey,
@ -82,7 +83,8 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
} }
@override @override
Future<SolanaWallet> restoreFromSeed(SolanaRestoreWalletFromSeedCredentials credentials) async { Future<SolanaWallet> restoreFromSeed(SolanaRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async {
if (!bip39.validateMnemonic(credentials.mnemonic)) { if (!bip39.validateMnemonic(credentials.mnemonic)) {
throw SolanaMnemonicIsIncorrectException(); throw SolanaMnemonicIsIncorrectException();
} }

View file

@ -1,12 +1,6 @@
part of 'bitcoin_cash.dart'; part of 'bitcoin_cash.dart';
class CWBitcoinCash extends BitcoinCash { class CWBitcoinCash extends BitcoinCash {
@override
String getMnemonic(int? strength) => Mnemonic.generate();
@override
Uint8List getSeedFromMnemonic(String seed) => Mnemonic.toSeed(seed);
@override @override
String getCashAddrFormat(String address) => AddressUtils.getCashAddrFormat(address); String getCashAddrFormat(String address) => AddressUtils.getCashAddrFormat(address);

View file

@ -257,9 +257,9 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.near: case CryptoCurrency.near:
return [64]; return [64];
case CryptoCurrency.btcln: case CryptoCurrency.btcln:
return null; case CryptoCurrency.kaspa:
default: default:
return []; return null;
} }
} }

View file

@ -66,7 +66,7 @@ class ProvidersHelper {
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood]; return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood];
case WalletType.polygon: case WalletType.polygon:
return [ProviderType.askEachTime, ProviderType.dfx]; return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx];
case WalletType.solana: case WalletType.solana:
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood]; return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood];
case WalletType.none: case WalletType.none:
@ -89,7 +89,7 @@ class ProvidersHelper {
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
return [ProviderType.askEachTime, ProviderType.moonpaySell]; return [ProviderType.askEachTime, ProviderType.moonpaySell];
case WalletType.polygon: case WalletType.polygon:
return [ProviderType.askEachTime, ProviderType.dfx]; return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx];
case WalletType.solana: case WalletType.solana:
return [ return [
ProviderType.askEachTime, ProviderType.askEachTime,

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/template.dart'; import 'package:cake_wallet/entities/template.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
import 'package:cake_wallet/src/screens/send/widgets/send_card.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/add_template_button.dart';
@ -420,7 +421,9 @@ class SendPage extends BasePage {
amount: S.of(_dialogContext).send_amount, amount: S.of(_dialogContext).send_amount,
amountValue: sendViewModel.pendingTransaction!.amountFormatted, amountValue: sendViewModel.pendingTransaction!.amountFormatted,
fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted, fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted,
fee: S.of(_dialogContext).send_fee, fee: isEVMCompatibleChain(sendViewModel.walletType)
? S.of(_dialogContext).send_estimated_fee
: S.of(_dialogContext).send_fee,
feeValue: sendViewModel.pendingTransaction!.feeFormatted, feeValue: sendViewModel.pendingTransaction!.feeFormatted,
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted, feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted,
outputs: sendViewModel.outputs, outputs: sendViewModel.outputs,

View file

@ -14,37 +14,28 @@ class ScrollableWithBottomSection extends StatefulWidget {
final EdgeInsets? bottomSectionPadding; final EdgeInsets? bottomSectionPadding;
@override @override
ScrollableWithBottomSectionState createState() => ScrollableWithBottomSectionState createState() => ScrollableWithBottomSectionState();
ScrollableWithBottomSectionState();
} }
class ScrollableWithBottomSectionState class ScrollableWithBottomSectionState extends State<ScrollableWithBottomSection> {
extends State<ScrollableWithBottomSection> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) { return Column(
return SingleChildScrollView( children: [
// physics: Expanded(
// const AlwaysScrollableScrollPhysics(), // const NeverScrollableScrollPhysics(), // child: SingleChildScrollView(
child: ConstrainedBox( child: Padding(
constraints: BoxConstraints( padding: widget.contentPadding ?? EdgeInsets.only(left: 20, right: 20),
minHeight: constraints.heightConstraints().maxHeight), child: widget.content,
child: Column( ),
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: widget.contentPadding ??
EdgeInsets.only(left: 20, right: 20),
child: widget.content,
),
Padding(
padding: widget.bottomSectionPadding ??
EdgeInsets.only(bottom: 20, right: 20, left: 20),
child: widget.bottomSection)
],
), ),
), ),
); Padding(
}); padding: widget.bottomSectionPadding?.copyWith(top: 10) ??
EdgeInsets.only(top: 10, bottom: 20, right: 20, left: 20),
child: widget.bottomSection,
),
],
);
} }
} }

View file

@ -27,11 +27,25 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
final SettingsStore _settingsStore; final SettingsStore _settingsStore;
bool get hasSeedPhraseLengthOption => bool get hasSeedPhraseLengthOption {
type == WalletType.bitcoinCash || // convert to switch case so that it give a syntax error when adding a new wallet type
type == WalletType.ethereum || // thus we don't forget about it
type == WalletType.polygon || switch (type) {
type == WalletType.solana; case WalletType.ethereum:
case WalletType.bitcoinCash:
case WalletType.polygon:
case WalletType.solana:
return true;
case WalletType.monero:
case WalletType.none:
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.haven:
case WalletType.nano:
case WalletType.banano:
return false;
}
}
bool get hasSeedTypeOption => type == WalletType.monero; bool get hasSeedTypeOption => type == WalletType.monero;

View file

@ -15,7 +15,8 @@ class UnspentCoinsListViewModel = UnspentCoinsListViewModelBase with _$UnspentCo
abstract class UnspentCoinsListViewModelBase with Store { abstract class UnspentCoinsListViewModelBase with Store {
UnspentCoinsListViewModelBase( UnspentCoinsListViewModelBase(
{required this.wallet, required Box<UnspentCoinsInfo> unspentCoinsInfo}) {required this.wallet, required Box<UnspentCoinsInfo> unspentCoinsInfo})
: _unspentCoinsInfo = unspentCoinsInfo { : _unspentCoinsInfo = unspentCoinsInfo,
_items = ObservableList<UnspentCoinsItem>() {
_updateUnspentCoinsInfo(); _updateUnspentCoinsInfo();
_updateUnspents(); _updateUnspents();
} }
@ -23,7 +24,8 @@ abstract class UnspentCoinsListViewModelBase with Store {
WalletBase wallet; WalletBase wallet;
final Box<UnspentCoinsInfo> _unspentCoinsInfo; final Box<UnspentCoinsInfo> _unspentCoinsInfo;
final ObservableList<UnspentCoinsItem> _items = ObservableList(); @observable
ObservableList<UnspentCoinsItem> _items;
@computed @computed
ObservableList<UnspentCoinsItem> get items => _items; ObservableList<UnspentCoinsItem> get items => _items;

View file

@ -20,9 +20,7 @@ abstract class WalletKeysViewModelBase with Store {
WalletKeysViewModelBase(this._appStore) WalletKeysViewModelBase(this._appStore)
: title = _appStore.wallet!.type == WalletType.bitcoin || : title = _appStore.wallet!.type == WalletType.bitcoin ||
_appStore.wallet!.type == WalletType.litecoin || _appStore.wallet!.type == WalletType.litecoin ||
_appStore.wallet!.type == WalletType.bitcoinCash || _appStore.wallet!.type == WalletType.bitcoinCash
_appStore.wallet!.type == WalletType.ethereum ||
_appStore.wallet!.type == WalletType.polygon
? S.current.wallet_seed ? S.current.wallet_seed
: S.current.wallet_keys, : S.current.wallet_keys,
_restoreHeight = _appStore.wallet!.walletInfo.restoreHeight, _restoreHeight = _appStore.wallet!.walletInfo.restoreHeight,

View file

@ -729,10 +729,6 @@ import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
const bitcoinCashCwPart = "part 'cw_bitcoin_cash.dart';"; const bitcoinCashCwPart = "part 'cw_bitcoin_cash.dart';";
const bitcoinCashContent = """ const bitcoinCashContent = """
abstract class BitcoinCash { abstract class BitcoinCash {
String getMnemonic(int? strength);
Uint8List getSeedFromMnemonic(String seed);
String getCashAddrFormat(String address); String getCashAddrFormat(String address);
WalletService createBitcoinCashWalletService( WalletService createBitcoinCashWalletService(