Files
jwinterm 04203654f1 Rename Flutter project + binary: cake_wallet -> hash_wallet
The big one. Touches 760+ files. Locks in the user's decision to break
import-line compatibility with upstream cake-tech/cake_wallet in exchange
for a binary named hash_wallet on every platform.

Changes:
- pubspec_description.yaml: project name cake_wallet -> hash_wallet
- 762 Dart files: every 'package:cake_wallet/...' import rewritten to
  'package:hash_wallet/...' (lib/, cw_*/lib/, tool/, test/, integration_test/)
- linux/CMakeLists.txt: BINARY_NAME cake_wallet -> hash_wallet
- windows/CMakeLists.txt: project + BINARY_NAME (CakeWallet -> HashWallet)
- windows/runner/Runner.rc: company / file / product strings to Hash Wallet
  + Such Software LLC copyright
- macos/Runner.xcodeproj/project_base.pbxproj: cake_wallet_new.app refs
  -> hash_wallet.app (macOS output bundle name)
- com.suchsoftware.HashWallet.yml (Flatpak): all binary path refs
  cake_wallet -> hash_wallet (e.g. /app/hash_wallet/hash_wallet)
- linux/com.suchsoftware.HashWallet.desktop: Exec=hash_wallet
- scripts/android/copy_monero_deps.sh: CW_DIR target path
- configure_hash_wallet.sh: error message typo (old filename)

INTENTIONALLY UNCHANGED:
- cw_* subpackage names (cw_core, cw_bitcoin, cw_wownero, etc.) — they
  remain as the historical 'cake wallet' naming; renaming them would
  cascade through every sub-package's internal structure
- Method channel name 'com.cake_wallet/native_utils' in iOS/macOS/Android
  AppDelegates and MainActivity — it's a string identifier that has to
  match the Dart side exactly. Renaming requires both-sides change; it's
  internal-only and not user-visible.
- ghcr.io/cake-tech/cake_wallet:debian13-... docker image references in
  build scripts — that's Cake Labs' published image we use as the build
  environment, not ours.

User data directory now follows the new binary name:
  ~/.local/share/hash_wallet/  (was ~/.local/share/cake_wallet/)
  ~/.config/hash_wallet/       (was ~/.config/cake_wallet/)
Existing users on the old name will need migration if any exist.
2026-05-16 11:33:25 -04:00

289 lines
8.7 KiB
Dart

import 'package:hash_wallet/core/execution_state.dart';
import 'package:hash_wallet/core/utilities.dart';
import 'package:hash_wallet/entities/calculate_fiat_amount.dart';
import 'package:hash_wallet/entities/fiat_currency.dart';
import 'package:hash_wallet/evm/evm.dart';
import 'package:hash_wallet/store/app_store.dart';
import 'package:hash_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:hash_wallet/store/settings_store.dart';
import 'package:hash_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:hash_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
import 'package:hash_wallet/view_model/send/send_view_model_state.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/parse_fixed.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:mobx/mobx.dart';
part 'deuro_view_model.g.dart';
class DEuroViewModel = DEuroViewModelBase with _$DEuroViewModel;
abstract class DEuroViewModelBase with Store {
final AppStore _appStore;
static BigInt get MIN_ACCRUED_INTEREST => BigInt.parse("1000000000000");
DEuroViewModelBase(
this._appStore, this.balanceViewModel, this._settingsStore, this._fiatConversationStore,
[this.hardwareWalletViewModel]) {
reloadInterestRate();
reloadSavingsUserData();
}
final BalanceViewModel balanceViewModel;
final HardwareWalletViewModel? hardwareWalletViewModel;
final SettingsStore _settingsStore;
final FiatConversionStore _fiatConversationStore;
WalletBase get wallet => this._appStore.wallet!;
@computed
bool get isFiatDisabled => balanceViewModel.isFiatDisabled;
@computed
bool get isFistTime => _settingsStore.shouldShowDEuroDisclaimer;
@action
void acceptDisclaimer() => _settingsStore.shouldShowDEuroDisclaimer = false;
FiatCurrency get fiat => _settingsStore.fiatCurrency;
@computed
String get pendingTransactionFiatAmountFormatted {
final amount = transaction == null ? '0.00' : _getDEuroFiatAmount(transaction!.amountFormatted);
return isFiatDisabled ? '' : '$amount ${fiat.title}';
}
@computed
String get pendingTransactionFeeFiatAmountFormatted {
var amount = '0.00';
try {
if (transaction != null) {
final feeCurrency = CryptoCurrency.eth;
amount = calculateFiatAmount(
price: _fiatConversationStore.prices[feeCurrency]!,
cryptoAmount: transaction!.feeFormattedValue,
);
}
} catch (_) {}
return isFiatDisabled ? '' : '$amount ${fiat.title}';
}
@computed
String get accountBalanceFormated {
final dEuroKey = balanceViewModel.balances.keys
.firstWhereOrNull((e) => e.title == CryptoCurrency.deuro.title);
if (dEuroKey == null) return '0.00';
return balanceViewModel.balances[dEuroKey]?.availableBalance ?? '0.00';
}
@observable
BigInt savingsBalance = BigInt.zero;
@computed
String get savingsBalanceFormated =>
evm!.formatterEVMAmountToDouble(amount: savingsBalance).toStringAsFixed(6);
@computed
String get fiatSavingsBalanceFormated => _getDEuroFiatAmount(savingsBalanceFormated);
@observable
ExecutionState state = InitialExecutionState();
@observable
String interestRateFormated = '0';
@observable
BigInt accruedInterest = BigInt.zero;
@computed
String get accruedInterestFormated =>
evm!.formatterEVMAmountToDouble(amount: accruedInterest).toStringAsFixed(6);
@computed
String get fiatAccruedInterestFormated => _getDEuroFiatAmount(accruedInterestFormated);
@observable
BigInt approvedTokens = BigInt.zero;
@computed
bool get isEnabled => approvedTokens > BigInt.zero;
@computed
bool get isSavingsActionsEnabled => isEnabled && accruedInterest >= MIN_ACCRUED_INTEREST;
@observable
bool isLoading = true;
@observable
DEuroActionType actionType = DEuroActionType.none;
@observable
PendingTransaction? transaction = null;
@observable
PendingTransaction? approvalTransaction = null;
@action
Future<void> reloadSavingsUserData() async {
approvedTokens = await evm!.getDEuroSavingsApproved(_appStore.wallet!) ?? BigInt.zero;
savingsBalance = await evm!.getDEuroSavingsBalance(_appStore.wallet!) ?? BigInt.zero;
accruedInterest = await evm!.getDEuroAccruedInterest(_appStore.wallet!) ?? BigInt.zero;
isLoading = false;
}
@action
Future<void> reloadInterestRate() async {
final interestRateRaw = await evm!.getDEuroInterestRate(_appStore.wallet!) ?? BigInt.zero;
interestRateFormated = (interestRateRaw / BigInt.from(10000)).toString();
}
@action
Future<void> prepareApproval() async {
final ethBalance = balanceViewModel.balances[CryptoCurrency.eth]?.availableBalance ?? "0";
if ((double.tryParse(ethBalance) ?? 0) == 0) {
state = NoEtherState();
return;
}
try {
state = TransactionCommitting();
final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
final approval = await evm!.enableDEuroSaving(_appStore.wallet!, priority);
if (approval == null) {
throw Exception('DEuro saving not available');
}
approvalTransaction = approval;
state = InitialExecutionState();
} catch (e) {
state = FailureState(e.toString());
}
}
@action
Future<void> prepareSavingsEdit(String amountRaw, bool isAdding) async {
try {
state = TransactionCommitting();
if (amountRaw.isEmpty || amountRaw.trim().isEmpty) {
throw Exception('Invalid amount: amount cannot be empty');
}
final amount = tryParseFixed(amountRaw, 18);
if (amount == BigInt.zero || amount == null) {
throw Exception('Invalid amount: amount cannot be zero');
}
final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
actionType = isAdding ? DEuroActionType.deposit : DEuroActionType.withdraw;
final tx = await (isAdding
? evm!.addDEuroSaving(_appStore.wallet!, amount, priority)
: evm!.removeDEuroSaving(_appStore.wallet!, amount, priority));
if (tx == null) {
throw Exception('DEuro saving not available');
}
transaction = tx;
state = InitialExecutionState();
} catch (e) {
state = FailureState(e.toString());
}
}
Future<void> prepareCollectInterest() async {
if (accruedInterest < MIN_ACCRUED_INTEREST) {
state = FailureState('Accrued interest is below minimum threshold');
return;
}
final formatted = accruedInterestFormated;
if (formatted.isEmpty || formatted == '0.000000') {
state = FailureState('Invalid accrued interest amount');
return;
}
await prepareSavingsEdit(formatted, false);
}
Future<void> prepareReinvestInterest() async {
try {
state = TransactionCommitting();
actionType = DEuroActionType.reinvest;
final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
final tx = await evm!.reinvestDEuroInterest(_appStore.wallet!, priority);
if (tx == null) {
throw Exception('DEuro saving not available');
}
transaction = tx;
state = InitialExecutionState();
} catch (e) {
state = FailureState(e.toString());
}
}
@action
Future<void> commitTransaction() async {
if (transaction != null) {
try {
state = TransactionCommitting();
await transaction!.commit();
transaction = null;
actionType = DEuroActionType.none;
reloadSavingsUserData();
state = TransactionCommitted();
} catch (e) {
state = FailureState(e.toString());
}
}
}
@action
Future<void> commitApprovalTransaction() async {
if (approvalTransaction != null) {
try {
state = TransactionCommitting();
await approvalTransaction!.commit();
approvalTransaction = null;
reloadSavingsUserData();
state = TransactionCommitted();
} catch (e) {
state = FailureState(e.toString());
}
}
}
@action
void dismissTransaction() {
transaction = null;
approvalTransaction = null;
actionType = DEuroActionType.none;
state = InitialExecutionState();
}
String _getDEuroFiatAmount(String amount) {
try {
var dEuro = CryptoCurrency.deuro;
final keys = _fiatConversationStore.prices.keys.toList();
for (final key in keys) {
if (key.title == "DEURO") dEuro = key;
}
return calculateFiatAmount(
price: _fiatConversationStore.prices[dEuro]!,
cryptoAmount: amount,
);
} catch (_) {
return '0.00';
}
}
}
class NoEtherState extends ExecutionState {}
enum DEuroActionType {
deposit,
withdraw,
reinvest,
none;
}