mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-18 05:51:34 +02:00
* feat: add support for Ethereum EIP-681 URIs in PaymentRequest and implement test coverage * test: add unit test for EIP-681 URI handling with contract and no chainId [skip-ci] * fix(CW-1114): rounding error for really large numbers * fix: wrong decimals for eth fee display * fix: limit fractional digits to a maximum of `decimals` in `format_fixed` function [skip-ci] * test: add unit tests for `formatFixed` function * refactor: replace custom balance formatting with `formatFixed` utility function * refactor: improve dEuroViewModel formatting and computation logic * refactor: extract and simplify interest action handlers in dEuro savings page * fix: deuro add should show edit Add bottom sheet * feat: block user from entering amount bigger than the max * feat: use new tooltip bottomsheet for dEuro * feat: add pull down to refresh dEuro savings --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
253 lines
7.5 KiB
Dart
253 lines
7.5 KiB
Dart
import 'package:cake_wallet/core/execution_state.dart';
|
|
import 'package:cake_wallet/core/utilities.dart';
|
|
import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
|
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
|
import 'package:cake_wallet/store/app_store.dart';
|
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
|
import 'package:cake_wallet/store/settings_store.dart';
|
|
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
|
import 'package:cake_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_type.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,
|
|
) {
|
|
reloadInterestRate();
|
|
reloadSavingsUserData();
|
|
}
|
|
|
|
final BalanceViewModel balanceViewModel;
|
|
final SettingsStore _settingsStore;
|
|
final FiatConversionStore _fiatConversationStore;
|
|
|
|
@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 =>
|
|
ethereum!.formatterEthereumAmountToDouble(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 =>
|
|
ethereum!.formatterEthereumAmountToDouble(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 ethereum!.getDEuroSavingsApproved(_appStore.wallet!);
|
|
savingsBalance = await ethereum!.getDEuroSavingsBalance(_appStore.wallet!);
|
|
accruedInterest = await ethereum!.getDEuroAccruedInterest(_appStore.wallet!);
|
|
isLoading = false;
|
|
}
|
|
|
|
@action
|
|
Future<void> reloadInterestRate() async {
|
|
final interestRateRaw = await ethereum!.getDEuroInterestRate(_appStore.wallet!);
|
|
|
|
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.priority[WalletType.ethereum]!;
|
|
approvalTransaction = await ethereum!.enableDEuroSaving(_appStore.wallet!, priority);
|
|
state = InitialExecutionState();
|
|
} catch (e) {
|
|
state = FailureState(e.toString());
|
|
}
|
|
}
|
|
|
|
@action
|
|
Future<void> prepareSavingsEdit(String amountRaw, bool isAdding) async {
|
|
try {
|
|
state = TransactionCommitting();
|
|
final amount = parseFixed(amountRaw, 18);
|
|
final priority = _appStore.settingsStore.priority[WalletType.ethereum]!;
|
|
actionType = isAdding ? DEuroActionType.deposit : DEuroActionType.withdraw;
|
|
transaction = await (isAdding
|
|
? ethereum!.addDEuroSaving(_appStore.wallet!, amount, priority)
|
|
: ethereum!.removeDEuroSaving(_appStore.wallet!, amount, priority));
|
|
state = InitialExecutionState();
|
|
} catch (e) {
|
|
state = FailureState(e.toString());
|
|
}
|
|
}
|
|
|
|
Future<void> prepareCollectInterest() => prepareSavingsEdit(accruedInterestFormated, false);
|
|
|
|
Future<void> prepareReinvestInterest() async {
|
|
try {
|
|
state = TransactionCommitting();
|
|
actionType = DEuroActionType.reinvest;
|
|
final priority = _appStore.settingsStore.priority[WalletType.ethereum]!;
|
|
transaction = await ethereum!.reinvestDEuroInterest(_appStore.wallet!, priority);
|
|
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;
|
|
}
|