This commit is contained in:
Matthew Fosse 2024-05-28 08:39:24 -07:00
commit fdf0c46380
11 changed files with 170 additions and 113 deletions

View file

@ -253,7 +253,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static CryptoCurrency fromString(String name, {CryptoCurrency? walletCurrency}) {
try {
return CryptoCurrency.all.firstWhere((element) =>
element.title.toLowerCase() == name &&
element.title.toLowerCase() == name.toLowerCase() &&
(element.tag == null ||
element.tag == walletCurrency?.title ||
element.tag == walletCurrency?.tag));

View file

@ -399,6 +399,7 @@ extern "C"
return false;
}
wallet->store(std::string(path));
change_current_wallet(wallet);
return true;
}
@ -464,6 +465,16 @@ extern "C"
return strdup(get_current_wallet()->address(account_index, address_index).c_str());
}
char *get_cache_attribute(char *name)
{
return strdup(get_current_wallet()->getCacheAttribute(std::string(name)).c_str());
}
bool set_cache_attribute(char *name, char *value)
{
get_current_wallet()->setCacheAttribute(std::string(name), std::string(value));
return true;
}
const char *seed()
{

View file

@ -154,3 +154,7 @@ typedef freeze_coin = Void Function(Int32 index);
typedef thaw_coin = Void Function(Int32 index);
typedef sign_message = Pointer<Utf8> Function(Pointer<Utf8> message, Pointer<Utf8> address);
typedef get_cache_attribute = Pointer<Utf8> Function(Pointer<Utf8> name);
typedef set_cache_attribute = Int8 Function(Pointer<Utf8> name, Pointer<Utf8> value);

View file

@ -154,3 +154,7 @@ typedef FreezeCoin = void Function(int);
typedef ThawCoin = void Function(int);
typedef SignMessage = Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>);
typedef GetCacheAttribute = Pointer<Utf8> Function(Pointer<Utf8>);
typedef SetCacheAttribute = int Function(Pointer<Utf8>, Pointer<Utf8>);

View file

@ -1,26 +1,24 @@
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:cw_monero/api/convert_utf8_to_string.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:cw_monero/api/types.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
int _boolToInt(bool value) => value ? 1 : 0;
final getFileNameNative = moneroApi
.lookup<NativeFunction<get_filename>>('get_filename')
.asFunction<GetFilename>();
final getFileNameNative =
moneroApi.lookup<NativeFunction<get_filename>>('get_filename').asFunction<GetFilename>();
final getSeedNative =
moneroApi.lookup<NativeFunction<get_seed>>('seed').asFunction<GetSeed>();
final getSeedNative = moneroApi.lookup<NativeFunction<get_seed>>('seed').asFunction<GetSeed>();
final getAddressNative = moneroApi
.lookup<NativeFunction<get_address>>('get_address')
.asFunction<GetAddress>();
final getAddressNative =
moneroApi.lookup<NativeFunction<get_address>>('get_address').asFunction<GetAddress>();
final getFullBalanceNative = moneroApi
.lookup<NativeFunction<get_full_balanace>>('get_full_balance')
@ -38,41 +36,34 @@ final getNodeHeightNative = moneroApi
.lookup<NativeFunction<get_node_height>>('get_node_height')
.asFunction<GetNodeHeight>();
final isConnectedNative = moneroApi
.lookup<NativeFunction<is_connected>>('is_connected')
.asFunction<IsConnected>();
final isConnectedNative =
moneroApi.lookup<NativeFunction<is_connected>>('is_connected').asFunction<IsConnected>();
final setupNodeNative = moneroApi
.lookup<NativeFunction<setup_node>>('setup_node')
.asFunction<SetupNode>();
final setupNodeNative =
moneroApi.lookup<NativeFunction<setup_node>>('setup_node').asFunction<SetupNode>();
final startRefreshNative = moneroApi
.lookup<NativeFunction<start_refresh>>('start_refresh')
.asFunction<StartRefresh>();
final startRefreshNative =
moneroApi.lookup<NativeFunction<start_refresh>>('start_refresh').asFunction<StartRefresh>();
final connecToNodeNative = moneroApi
.lookup<NativeFunction<connect_to_node>>('connect_to_node')
.asFunction<ConnectToNode>();
final setRefreshFromBlockHeightNative = moneroApi
.lookup<NativeFunction<set_refresh_from_block_height>>(
'set_refresh_from_block_height')
.lookup<NativeFunction<set_refresh_from_block_height>>('set_refresh_from_block_height')
.asFunction<SetRefreshFromBlockHeight>();
final setRecoveringFromSeedNative = moneroApi
.lookup<NativeFunction<set_recovering_from_seed>>(
'set_recovering_from_seed')
.lookup<NativeFunction<set_recovering_from_seed>>('set_recovering_from_seed')
.asFunction<SetRecoveringFromSeed>();
final storeNative =
moneroApi.lookup<NativeFunction<store_c>>('store').asFunction<Store>();
final storeNative = moneroApi.lookup<NativeFunction<store_c>>('store').asFunction<Store>();
final setPasswordNative =
moneroApi.lookup<NativeFunction<set_password>>('set_password').asFunction<SetPassword>();
final setListenerNative = moneroApi
.lookup<NativeFunction<set_listener>>('set_listener')
.asFunction<SetListener>();
final setListenerNative =
moneroApi.lookup<NativeFunction<set_listener>>('set_listener').asFunction<SetListener>();
final getSyncingHeightNative = moneroApi
.lookup<NativeFunction<get_syncing_height>>('get_syncing_height')
@ -83,8 +74,7 @@ final isNeededToRefreshNative = moneroApi
.asFunction<IsNeededToRefresh>();
final isNewTransactionExistNative = moneroApi
.lookup<NativeFunction<is_new_transaction_exist>>(
'is_new_transaction_exist')
.lookup<NativeFunction<is_new_transaction_exist>>('is_new_transaction_exist')
.asFunction<IsNewTransactionExist>();
final getSecretViewKeyNative = moneroApi
@ -107,9 +97,8 @@ final closeCurrentWalletNative = moneroApi
.lookup<NativeFunction<close_current_wallet>>('close_current_wallet')
.asFunction<CloseCurrentWallet>();
final onStartupNative = moneroApi
.lookup<NativeFunction<on_startup>>('on_startup')
.asFunction<OnStartup>();
final onStartupNative =
moneroApi.lookup<NativeFunction<on_startup>>('on_startup').asFunction<OnStartup>();
final rescanBlockchainAsyncNative = moneroApi
.lookup<NativeFunction<rescan_blockchain>>('rescan_blockchain')
@ -123,13 +112,19 @@ final setTrustedDaemonNative = moneroApi
.lookup<NativeFunction<set_trusted_daemon>>('set_trusted_daemon')
.asFunction<SetTrustedDaemon>();
final trustedDaemonNative = moneroApi
.lookup<NativeFunction<trusted_daemon>>('trusted_daemon')
.asFunction<TrustedDaemon>();
final trustedDaemonNative =
moneroApi.lookup<NativeFunction<trusted_daemon>>('trusted_daemon').asFunction<TrustedDaemon>();
final signMessageNative = moneroApi
.lookup<NativeFunction<sign_message>>('sign_message')
.asFunction<SignMessage>();
final signMessageNative =
moneroApi.lookup<NativeFunction<sign_message>>('sign_message').asFunction<SignMessage>();
final getCacheAttributeNative = moneroApi
.lookup<NativeFunction<get_cache_attribute>>('get_cache_attribute')
.asFunction<GetCacheAttribute>();
final setCacheAttributeNative = moneroApi
.lookup<NativeFunction<set_cache_attribute>>('set_cache_attribute')
.asFunction<SetCacheAttribute>();
int getSyncingHeight() => getSyncingHeightNative();
@ -144,11 +139,9 @@ String getSeed() => convertUTF8ToString(pointer: getSeedNative());
String getAddress({int accountIndex = 0, int addressIndex = 0}) =>
convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex));
int getFullBalance({int accountIndex = 0}) =>
getFullBalanceNative(accountIndex);
int getFullBalance({int accountIndex = 0}) => getFullBalanceNative(accountIndex);
int getUnlockedBalance({int accountIndex = 0}) =>
getUnlockedBalanceNative(accountIndex);
int getUnlockedBalance({int accountIndex = 0}) => getUnlockedBalanceNative(accountIndex);
int getCurrentHeight() => getCurrentHeightNative();
@ -187,7 +180,7 @@ bool setupNodeSync(
passwordPointer,
_boolToInt(useSSL),
_boolToInt(isLightWallet),
socksProxyAddressPointer,
socksProxyAddressPointer,
errorMessagePointer) !=
0;
@ -202,8 +195,7 @@ bool setupNodeSync(
}
if (!isSetupNode) {
throw SetupWalletException(
message: convertUTF8ToString(pointer: errorMessagePointer));
throw SetupWalletException(message: convertUTF8ToString(pointer: errorMessagePointer));
}
return isSetupNode;
@ -213,8 +205,7 @@ void startRefreshSync() => startRefreshNative();
Future<bool> connectToNode() async => connecToNodeNative() != 0;
void setRefreshFromBlockHeight({required int height}) =>
setRefreshFromBlockHeightNative(height);
void setRefreshFromBlockHeight({required int height}) => setRefreshFromBlockHeightNative(height);
void setRecoveringFromSeed({required bool isRecovery}) =>
setRecoveringFromSeedNative(_boolToInt(isRecovery));
@ -230,7 +221,7 @@ void setPasswordSync(String password) {
final errorMessagePointer = calloc<Utf8Box>();
final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0;
calloc.free(passwordPointer);
if (!changed) {
final message = errorMessagePointer.ref.getValue();
calloc.free(errorMessagePointer);
@ -242,24 +233,19 @@ void setPasswordSync(String password) {
void closeCurrentWallet() => closeCurrentWalletNative();
String getSecretViewKey() =>
convertUTF8ToString(pointer: getSecretViewKeyNative());
String getSecretViewKey() => convertUTF8ToString(pointer: getSecretViewKeyNative());
String getPublicViewKey() =>
convertUTF8ToString(pointer: getPublicViewKeyNative());
String getPublicViewKey() => convertUTF8ToString(pointer: getPublicViewKeyNative());
String getSecretSpendKey() =>
convertUTF8ToString(pointer: getSecretSpendKeyNative());
String getSecretSpendKey() => convertUTF8ToString(pointer: getSecretSpendKeyNative());
String getPublicSpendKey() =>
convertUTF8ToString(pointer: getPublicSpendKeyNative());
String getPublicSpendKey() => convertUTF8ToString(pointer: getPublicSpendKeyNative());
class SyncListener {
SyncListener(this.onNewBlock, this.onNewTransaction)
: _cachedBlockchainHeight = 0,
_lastKnownBlockHeight = 0,
_initialSyncHeight = 0;
SyncListener(this.onNewBlock, this.onNewTransaction)
: _cachedBlockchainHeight = 0,
_lastKnownBlockHeight = 0,
_initialSyncHeight = 0;
void Function(int, int, double) onNewBlock;
void Function() onNewTransaction;
@ -281,8 +267,7 @@ class SyncListener {
_cachedBlockchainHeight = 0;
_lastKnownBlockHeight = 0;
_initialSyncHeight = 0;
_updateSyncInfoTimer ??=
Timer.periodic(Duration(milliseconds: 1200), (_) async {
_updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: 1200), (_) async {
if (isNewTransactionExist()) {
onNewTransaction();
}
@ -321,8 +306,8 @@ class SyncListener {
void stop() => _updateSyncInfoTimer?.cancel();
}
SyncListener setListeners(void Function(int, int, double) onNewBlock,
void Function() onNewTransaction) {
SyncListener setListeners(
void Function(int, int, double) onNewBlock, void Function() onNewTransaction) {
final listener = SyncListener(onNewBlock, onNewTransaction);
setListenerNative();
return listener;
@ -364,7 +349,7 @@ Future<void> setupNode(
bool isLightWallet = false}) =>
compute<Map<String, Object?>, void>(_setupNodeSync, {
'address': address,
'login': login ,
'login': login,
'password': password,
'useSSL': useSSL,
'isLightWallet': isLightWallet,
@ -397,3 +382,23 @@ String signMessage(String message, {String address = ""}) {
return signature;
}
bool setCacheAttribute(String name, String value) {
final namePointer = name.toNativeUtf8();
final valuePointer = value.toNativeUtf8();
final isSet = setCacheAttributeNative(namePointer, valuePointer);
calloc.free(namePointer);
calloc.free(valuePointer);
return isSet == 1;
}
String getCacheAttribute(String name) {
final namePointer = name.toNativeUtf8();
final value = convertUTF8ToString(pointer: getCacheAttributeNative(namePointer));
calloc.free(namePointer);
return value;
}

View file

@ -15,8 +15,8 @@ class SolanaBalance extends Balance {
String _balanceFormatted() {
String stringBalance = balance.toString();
if (stringBalance.toString().length >= 6) {
stringBalance = stringBalance.substring(0, 6);
if (stringBalance.toString().length >= 12) {
stringBalance = stringBalance.substring(0, 12);
}
return stringBalance;
}

View file

@ -15,6 +15,7 @@ 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/secure_storage.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/core/wallet_connect/web3wallet_service.dart';
@ -219,6 +220,7 @@ 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';
@ -242,6 +244,7 @@ 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/tron/tron.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';
@ -274,6 +277,7 @@ import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.
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/link_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.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';
@ -317,6 +321,7 @@ import 'package:cake_wallet/view_model/wallet_seed_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';
@ -328,7 +333,6 @@ 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:cake_wallet/core/secure_storage.dart';
import 'buy/dfx/dfx_buy_provider.dart';
import 'core/totp_request_details.dart';
@ -891,8 +895,8 @@ Future<void> setup({
getIt.registerFactory(() => ConnectionSyncPage(getIt.get<DashboardViewModel>()));
getIt.registerFactory(
() => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>(), getIt.get<AuthService>()));
getIt.registerFactory(() => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>(),
getIt.get<AuthService>(), getIt.get<AppStore>().wallet!.isHardwareWallet));
getIt.registerFactory(() => PrivacyPage(getIt.get<PrivacySettingsViewModel>()));

View file

@ -46,6 +46,7 @@ class ContactPage extends BasePage {
final TextEditingController _nameController;
final TextEditingController _currencyTypeController;
final TextEditingController _addressController;
bool _isEffectsApplied = false;
@override
Widget body(BuildContext context) {
@ -53,15 +54,7 @@ class ContactPage extends BasePage {
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
height: 8);
reaction((_) => contactViewModel.state, (ExecutionState state) {
if (state is FailureState) {
_onContactSavingFailure(context, state.error);
}
if (state is ExecutedSuccessfullyState) {
_onContactSavedSuccessfully(context);
}
});
_setEffects(context);
return Observer(
builder: (_) => ScrollableWithBottomSection(
@ -177,4 +170,22 @@ class ContactPage extends BasePage {
void _onContactSavedSuccessfully(BuildContext context) =>
Navigator.of(context).pop();
void _setEffects(BuildContext context) {
if (_isEffectsApplied) {
return;
}
_isEffectsApplied = true;
reaction((_) => contactViewModel.state, (ExecutionState state) {
if (state is FailureState) {
_onContactSavingFailure(context, state.error);
}
if (state is ExecutedSuccessfullyState) {
_onContactSavedSuccessfully(context);
}
});
}
}

View file

@ -9,14 +9,13 @@ import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.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/widgets/standard_list.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class SecurityBackupPage extends BasePage {
SecurityBackupPage(this._securitySettingsViewModel, this._authService);
SecurityBackupPage(this._securitySettingsViewModel, this._authService, [this._isHardwareWallet = false]);
final AuthService _authService;
@ -25,20 +24,23 @@ class SecurityBackupPage extends BasePage {
final SecuritySettingsViewModel _securitySettingsViewModel;
final bool _isHardwareWallet;
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(mainAxisSize: MainAxisSize.min, children: [
SettingsCellWithArrow(
title: S.current.show_keys,
handler: (_) => _authService.authenticateAction(
context,
route: Routes.showKeys,
conditionToDetermineIfToUse2FA: _securitySettingsViewModel
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
if (!_isHardwareWallet)
SettingsCellWithArrow(
title: S.current.show_keys,
handler: (_) => _authService.authenticateAction(
context,
route: Routes.showKeys,
conditionToDetermineIfToUse2FA:
_securitySettingsViewModel.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
),
),
),
SettingsCellWithArrow(
title: S.current.create_backup,
handler: (_) => _authService.authenticateAction(

View file

@ -562,24 +562,29 @@ abstract class DashboardViewModelBase with Store {
Future<ServicesResponse> getServicesStatus() async {
try {
final res = await http.get(Uri.parse("https://service-api.cakewallet.com/v1/active-notices"));
if (isEnabledBulletinAction) {
final res = await http.get(Uri.parse("https://service-api.cakewallet.com/v1/active-notices"));
if (res.statusCode < 200 || res.statusCode >= 300) {
throw res.body;
if (res.statusCode < 200 || res.statusCode >= 300) {
throw res.body;
}
final oldSha = sharedPreferences.getString(PreferencesKey.serviceStatusShaKey);
final hash = await Cryptography.instance.sha256().hash(utf8.encode(res.body));
final currentSha = bytesToHex(hash.bytes);
final hasUpdates = oldSha != currentSha;
return ServicesResponse.fromJson(
json.decode(res.body) as Map<String, dynamic>,
hasUpdates,
currentSha,
);
}
final oldSha = sharedPreferences.getString(PreferencesKey.serviceStatusShaKey);
final hash = await Cryptography.instance.sha256().hash(utf8.encode(res.body));
final currentSha = bytesToHex(hash.bytes);
final hasUpdates = oldSha != currentSha;
return ServicesResponse.fromJson(
json.decode(res.body) as Map<String, dynamic>,
hasUpdates,
currentSha,
);
else {
return ServicesResponse([], false, '');
}
} catch (_) {
return ServicesResponse([], false, '');
}

View file

@ -139,8 +139,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
String get pendingTransactionFeeFiatAmount {
try {
if (pendingTransaction != null) {
final currency =
isEVMCompatibleChain(walletType) ? wallet.currency : selectedCryptoCurrency;
final currency = pendingTransactionFeeCurrency(walletType);
final fiat = calculateFiatAmount(
price: _fiatConversationStore.prices[currency]!,
cryptoAmount: pendingTransaction!.feeFormatted);
@ -153,6 +152,18 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
}
}
CryptoCurrency pendingTransactionFeeCurrency(WalletType type) {
switch (type) {
case WalletType.ethereum:
case WalletType.polygon:
case WalletType.tron:
case WalletType.solana:
return wallet.currency;
default:
return selectedCryptoCurrency;
}
}
FiatCurrency get fiat => _settingsStore.fiatCurrency;
TransactionPriority get transactionPriority {