Generic Fixes (#1122)

* Fix Hive issue

* Disable RobinHood for Nano

* Validate context is still mounted [skip ci]

* Disable Exolix for new exchanges
Remove duplicate ethereum case

* add nano/banano to manifest/info.plist

* fix qr code issues for nano

* Add Nano-wallet to restore form qr
Add iOS keychain accessibility config

* support app links for ethereum and nano [skip ci]

* catch exceptions from gas price and estimated gas

* Add bitcoin cash to app links
Fix restore from QR for bitcoin cash

* Fixate bottom buttons for create/restore wallet in wallet list page

---------

Co-authored-by: fosse <matt.cfosse@gmail.com>
This commit is contained in:
Omar Hatem 2023-10-13 14:49:00 +03:00 committed by GitHub
parent 66301ff247
commit 426ac99e34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 275 additions and 188 deletions

View file

@ -52,6 +52,15 @@
<data android:scheme="litecoin" />
<data android:scheme="litecoin-wallet" />
<data android:scheme="litecoin_wallet" />
<data android:scheme="ethereum" />
<data android:scheme="ethereum-wallet" />
<data android:scheme="ethereum_wallet" />
<data android:scheme="nano" />
<data android:scheme="nano-wallet" />
<data android:scheme="nano_wallet" />
<data android:scheme="bitcoincash" />
<data android:scheme="bitcoincash-wallet" />
<data android:scheme="bitcoincash_wallet" />
</intent-filter>
</activity>
<meta-data

View file

@ -42,13 +42,21 @@ class EthereumClient {
await _client!.getBalance(address);
Future<int> getGasUnitPrice() async {
final gasPrice = await _client!.getGasPrice();
return gasPrice.getInWei.toInt();
try {
final gasPrice = await _client!.getGasPrice();
return gasPrice.getInWei.toInt();
} catch (_) {
return 0;
}
}
Future<int> getEstimatedGas() async {
final estimatedGas = await _client!.estimateGas();
return estimatedGas.toInt();
try {
final estimatedGas = await _client!.estimateGas();
return estimatedGas.toInt();
} catch (_) {
return 0;
}
}
Future<PendingEthereumTransaction> signTransaction({

View file

@ -100,6 +100,66 @@
<string>litecoin-wallet</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>ethereum</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ethereum</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>ethereum-wallet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ethereum-wallet</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>nano</string>
<key>CFBundleURLSchemes</key>
<array>
<string>nano</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>nano-wallet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>nano-wallet</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>bitcoincash</string>
<key>CFBundleURLSchemes</key>
<array>
<string>bitcoincash</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>bitcoincash-wallet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>bitcoincash-wallet</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>

View file

@ -53,8 +53,6 @@ class MainActions {
case WalletType.litecoin:
case WalletType.ethereum:
case WalletType.bitcoinCash:
case WalletType.nano:
case WalletType.banano:
switch (defaultBuyProvider) {
case BuyProviderType.AskEachTime:
Navigator.pushNamed(context, Routes.buy);
@ -67,6 +65,8 @@ class MainActions {
break;
}
break;
case WalletType.nano:
case WalletType.banano:
case WalletType.monero:
await getIt.get<OnRamperBuyProvider>().launchProvider(context);
break;

View file

@ -97,7 +97,7 @@ Future<void> initializeAppConfigs() async {
CakeHive.registerAdapter(WalletInfoAdapter());
}
if (!Hive.isAdapterRegistered(DERIVATION_TYPE_TYPE_ID)) {
if (!CakeHive.isAdapterRegistered(DERIVATION_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(DerivationTypeAdapter());
}
@ -125,14 +125,17 @@ Future<void> initializeAppConfigs() async {
CakeHive.registerAdapter(AnonpayInvoiceInfoAdapter());
}
final secureStorage = FlutterSecureStorage();
final secureStorage = FlutterSecureStorage(
iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
);
final transactionDescriptionsBoxKey =
await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey);
final ordersBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Order.boxKey);
final contacts = await CakeHive.openBox<Contact>(Contact.boxName);
final nodes = await CakeHive.openBox<Node>(Node.boxName);
final powNodes = await CakeHive.openBox<Node>(Node.boxName + "pow");// must be different from Node.boxName
final powNodes =
await CakeHive.openBox<Node>(Node.boxName + "pow"); // must be different from Node.boxName
final transactionDescriptions = await CakeHive.openBox<TransactionDescription>(
TransactionDescription.boxName,
encryptionKey: transactionDescriptionsBoxKey);

View file

@ -194,7 +194,9 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
contractAddress: _contractAddressController.text,
decimal: int.parse(_tokenDecimalController.text),
));
Navigator.pop(context);
if (context.mounted) {
Navigator.pop(context);
}
}
},
text: S.of(context).save,

View file

@ -5,7 +5,6 @@ import 'package:cake_wallet/utils/request_review_handler.dart';
import 'package:mobx/mobx.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/execution_state.dart';
@ -26,16 +25,15 @@ import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
void showInformation(
ExchangeTradeViewModel exchangeTradeViewModel, BuildContext context) {
final fetchingLabel = S.current.fetching;
final trade = exchangeTradeViewModel.trade;
final walletName = exchangeTradeViewModel.wallet.name;
final information = exchangeTradeViewModel.isSendable
? S.current.exchange_result_confirm(
trade.amount ?? fetchingLabel, trade.from.toString(), walletName) +
trade.amount, trade.from.toString(), walletName) +
exchangeTradeViewModel.extraInfo
: S.current.exchange_result_description(
trade.amount ?? fetchingLabel, trade.from.toString()) +
trade.amount, trade.from.toString()) +
exchangeTradeViewModel.extraInfo;
showPopUp<void>(
@ -177,7 +175,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
),
itemBuilder: (context, index) {
final item = widget.exchangeTradeViewModel.items[index];
final value = item.data ?? fetchingLabel;
final value = item.data;
final content = ListRow(
title: item.title,

View file

@ -13,7 +13,6 @@ import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
@ -65,169 +64,178 @@ class WalletListBodyState extends State<WalletListBody> {
return Container(
padding: EdgeInsets.only(top: 16),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 20),
content: Container(
child: Observer(
builder: (_) => ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (_, index) =>
Divider(color: Theme.of(context).colorScheme.background, height: 32),
itemCount: widget.walletListViewModel.wallets.length,
itemBuilder: (__, index) {
final wallet = widget.walletListViewModel.wallets[index];
final currentColor = wallet.isCurrent
? Theme.of(context)
.extension<WalletListTheme>()!
.createNewWalletButtonBackgroundColor
: Theme.of(context).colorScheme.background;
final row = GestureDetector(
onTap: () => wallet.isCurrent ? null : _loadWallet(wallet),
child: Container(
height: tileHeight,
width: double.infinity,
child: Row(
children: <Widget>[
Container(
height: tileHeight,
width: 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(4), bottomRight: Radius.circular(4)),
color: currentColor),
),
Expanded(
child: Container(
height: tileHeight,
padding: EdgeInsets.only(left: 20, right: 20),
color: Theme.of(context).colorScheme.background,
alignment: Alignment.centerLeft,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
wallet.isEnabled ? _imageFor(type: wallet.type) : nonWalletTypeIcon,
SizedBox(width: 10),
Flexible(
child: Text(
wallet.name,
maxLines: null,
softWrap: true,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w500,
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
),
],
child: Column(
children: [
Expanded(
child: Container(
child: Observer(
builder: (_) => ListView.separated(
physics: const BouncingScrollPhysics(),
separatorBuilder: (_, index) =>
Divider(color: Theme.of(context).colorScheme.background, height: 32),
itemCount: widget.walletListViewModel.wallets.length,
itemBuilder: (__, index) {
final wallet = widget.walletListViewModel.wallets[index];
final currentColor = wallet.isCurrent
? Theme.of(context)
.extension<WalletListTheme>()!
.createNewWalletButtonBackgroundColor
: Theme.of(context).colorScheme.background;
final row = GestureDetector(
onTap: () => wallet.isCurrent ? null : _loadWallet(wallet),
child: Container(
height: tileHeight,
width: double.infinity,
child: Row(
children: <Widget>[
Container(
height: tileHeight,
width: 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(4),
bottomRight: Radius.circular(4)),
color: currentColor),
),
),
),
],
),
),
);
return wallet.isCurrent
? row
: Row(
children: [
Expanded(child: row),
GestureDetector(
onTap: () => Navigator.of(context).pushNamed(Routes.walletEdit,
arguments: [widget.walletListViewModel, wallet]),
child: Container(
padding: EdgeInsets.only(right: 20),
child: Center(
child: Container(
height: 40,
width: 44,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.extension<ReceivePageTheme>()!
.iconsBackgroundColor,
),
child: Icon(
Icons.edit,
size: 14,
color:
Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
),
Expanded(
child: Container(
height: tileHeight,
padding: EdgeInsets.only(left: 20, right: 20),
color: Theme.of(context).colorScheme.background,
alignment: Alignment.centerLeft,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
wallet.isEnabled
? _imageFor(type: wallet.type)
: nonWalletTypeIcon,
SizedBox(width: 10),
Flexible(
child: Text(
wallet.name,
maxLines: null,
softWrap: true,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.extension<CakeTextTheme>()!
.titleColor,
),
),
),
],
),
),
),
),
],
);
},
],
),
),
);
return wallet.isCurrent
? row
: Row(
children: [
Expanded(child: row),
GestureDetector(
onTap: () => Navigator.of(context).pushNamed(Routes.walletEdit,
arguments: [widget.walletListViewModel, wallet]),
child: Container(
padding: EdgeInsets.only(right: 20),
child: Center(
child: Container(
height: 40,
width: 44,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.extension<ReceivePageTheme>()!
.iconsBackgroundColor,
),
child: Icon(
Icons.edit,
size: 14,
color: Theme.of(context)
.extension<ReceivePageTheme>()!
.iconsColor,
),
),
),
),
),
],
);
},
),
),
),
),
),
bottomSectionPadding: EdgeInsets.only(bottom: 24, right: 24, left: 24),
bottomSection: Column(
children: <Widget>[
PrimaryImageButton(
onPressed: () {
//TODO(David): Find a way to optimize this
if (isSingleCoin) {
if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) {
widget.authService.authenticateAction(
context,
route: Routes.newWallet,
arguments: widget.walletListViewModel.currentWalletType,
conditionToDetermineIfToUse2FA:
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
);
} else {
Navigator.of(context).pushNamed(
Routes.newWallet,
arguments: widget.walletListViewModel.currentWalletType,
);
}
} else {
if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) {
widget.authService.authenticateAction(
context,
route: Routes.newWalletType,
conditionToDetermineIfToUse2FA:
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
);
} else {
Navigator.of(context).pushNamed(Routes.newWalletType);
}
}
},
image: newWalletImage,
text: S.of(context).wallet_list_create_new_wallet,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: <Widget>[
PrimaryImageButton(
onPressed: () {
//TODO(David): Find a way to optimize this
if (isSingleCoin) {
if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) {
widget.authService.authenticateAction(
context,
route: Routes.newWallet,
arguments: widget.walletListViewModel.currentWalletType,
conditionToDetermineIfToUse2FA:
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
);
} else {
Navigator.of(context).pushNamed(
Routes.newWallet,
arguments: widget.walletListViewModel.currentWalletType,
);
}
} else {
if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) {
widget.authService.authenticateAction(
context,
route: Routes.newWalletType,
conditionToDetermineIfToUse2FA:
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
);
} else {
Navigator.of(context).pushNamed(Routes.newWalletType);
}
}
},
image: newWalletImage,
text: S.of(context).wallet_list_create_new_wallet,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
SizedBox(height: 10.0),
PrimaryImageButton(
onPressed: () {
if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) {
widget.authService.authenticateAction(
context,
route: Routes.restoreOptions,
arguments: false,
conditionToDetermineIfToUse2FA:
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
);
} else {
Navigator.of(context).pushNamed(Routes.restoreOptions, arguments: false);
}
},
image: restoreWalletImage,
text: S.of(context).wallet_list_restore_wallet,
color: Theme.of(context).cardColor,
textColor: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor,
)
],
),
SizedBox(height: 10.0),
PrimaryImageButton(
onPressed: () {
if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) {
widget.authService.authenticateAction(
context,
route: Routes.restoreOptions,
arguments: false,
conditionToDetermineIfToUse2FA:
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
);
} else {
Navigator.of(context).pushNamed(Routes.restoreOptions, arguments: false);
}
},
image: restoreWalletImage,
text: S.of(context).wallet_list_restore_wallet,
color: Theme.of(context).cardColor,
textColor: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor,
)
],
),
),
],
),
);
}

View file

@ -161,7 +161,7 @@ class ExceptionHandler {
"Handshake error in client",
"Error while launching http",
"OS Error: Network is unreachable",
"ClientException: Write failed, uri=https:",
"ClientException: Write failed, uri=http",
];
static Future<void> _addDeviceInfo(File file) async {

View file

@ -1,3 +1,4 @@
class FeatureFlag {
static const bool isCakePayEnabled = false;
static const bool isExolixEnabled = false;
}

View file

@ -17,10 +17,12 @@ class PaymentRequest {
}
if (nano != null) {
if (address.contains("nano")) {
amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerNano);
} else if (address.contains("ban")) {
amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerBanano);
if (amount.isNotEmpty) {
if (address.contains("nano")) {
amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerNano);
} else if (address.contains("ban")) {
amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerBanano);
}
}
}

View file

@ -97,13 +97,6 @@ class TransactionListItem extends ActionListItem with Keyable {
nano!.getTransactionAmountRaw(transaction).toString(), nanoUtil!.rawPerNano)),
price: price);
break;
case WalletType.ethereum:
final asset = ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction);
final price = balanceViewModel.fiatConvertationStore.prices[asset];
amount = calculateFiatAmountRaw(
cryptoAmount: ethereum!.formatterEthereumAmountToDouble(transaction: transaction),
price: price);
break;
default:
break;
}

View file

@ -16,6 +16,7 @@ import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dar
import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart';
import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trocador/trocador_request.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/store/app_store.dart';
@ -154,7 +155,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
SideShiftExchangeProvider(),
SimpleSwapExchangeProvider(),
TrocadorExchangeProvider(useTorOnly: _useTorOnly),
ExolixExchangeProvider(),
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
];
@observable

View file

@ -73,10 +73,12 @@ class WalletRestoreFromQRCode {
case 'litecoin-wallet':
return WalletType.litecoin;
case 'bitcoincash':
case 'bitcoinCash-wallet':
case 'bitcoincash-wallet':
return WalletType.bitcoinCash;
case 'ethereum':
case 'ethereum-wallet':
return WalletType.ethereum;
case 'nano':
case 'nano-wallet':
return WalletType.nano;
default:

View file

@ -148,7 +148,7 @@ abstract class WalletKeysViewModelBase with Store {
case WalletType.ethereum:
return 'ethereum-wallet';
case WalletType.bitcoinCash:
return 'bitcoinCash-wallet';
return 'bitcoincash-wallet';
case WalletType.nano:
return 'nano-wallet';
case WalletType.banano: