dev
dart 307 lines 9.39 KB
Raw
1 import 'package:cake_wallet/core/execution_state.dart';
2 import 'package:cake_wallet/core/utilities.dart';
3 import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
4 import 'package:cake_wallet/entities/fiat_currency.dart';
5 import 'package:cake_wallet/evm/evm.dart';
6 import 'package:cake_wallet/store/app_store.dart';
7 import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
8 import 'package:cake_wallet/store/settings_store.dart';
9 import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
10 import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
11 import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
12 import 'package:cw_core/amount/money.dart';
13 import 'package:cw_core/crypto_currency.dart';
14 import 'package:cw_core/parse_fixed.dart';
15 import 'package:cw_core/pending_transaction.dart';
16 import 'package:cw_core/wallet_base.dart';
17 import 'package:mobx/mobx.dart';
18
19 part 'deuro_view_model.g.dart';
20
21 class DEuroViewModel = DEuroViewModelBase with _$DEuroViewModel;
22
23 abstract class DEuroViewModelBase with Store {
24 final AppStore _appStore;
25
26 static Money get MIN_ACCRUED_INTEREST =>
27 Money(BigInt.parse("1000000000000"), CryptoCurrency.deuro);
28
29 static Money get ZERO => Money.zero(CryptoCurrency.deuro);
30
31 DEuroViewModelBase(
32 this._appStore, this.balanceViewModel, this._settingsStore, this._fiatConversationStore,
33 [this.hardwareWalletViewModel]) {
34 reloadInterestRate();
35 reloadSavingsUserData();
36 }
37
38 final BalanceViewModel balanceViewModel;
39 final HardwareWalletViewModel? hardwareWalletViewModel;
40 final SettingsStore _settingsStore;
41 final FiatConversionStore _fiatConversationStore;
42
43 WalletBase get wallet => this._appStore.wallet!;
44
45 @computed
46 bool get isFiatDisabled => balanceViewModel.isFiatDisabled;
47
48 @computed
49 bool get isFistTime => _settingsStore.shouldShowDEuroDisclaimer;
50
51 @action
52 void acceptDisclaimer() => _settingsStore.shouldShowDEuroDisclaimer = false;
53
54 FiatCurrency get fiat => _settingsStore.fiatCurrency;
55
56 @computed
57 String get pendingTransactionFiatAmountFormatted {
58 final amount = transaction == null ? "0.00" : _getDEuroFiatAmount(transaction!.amount);
59 return isFiatDisabled ? "" : "$amount ${fiat.symbol}";
60 }
61
62 @computed
63 String get pendingTransactionFeeFiatAmountFormatted {
64 var amount = "0.00";
65 try {
66 if (transaction != null) {
67 final feeCurrency = CryptoCurrency.eth;
68 amount = calculateFiatAmount(
69 price: _fiatConversationStore.prices[feeCurrency]!,
70 cryptoAmount: transaction!.feeFormattedValue,
71 );
72 }
73 } catch (_) {}
74 return isFiatDisabled ? "" : "$amount ${fiat.symbol}";
75 }
76
77 @computed
78 Money get accountBalance {
79 final dEuroKey = balanceViewModel.balances.keys
80 .firstWhereOrNull((e) => e.title == CryptoCurrency.deuro.title);
81 if (dEuroKey == null) return ZERO;
82 return wallet.balance[dEuroKey]?.available ?? ZERO;
83 }
84
85 @observable
86 Money savingsBalance = ZERO;
87
88 @computed
89 String get fiatSavingsBalanceFormated => _getDEuroFiatAmount(savingsBalance);
90
91 @observable
92 Money? savingsBalanceV1 = null;
93
94 @computed
95 String get fiatSavingsBalanceV1Formated => _getDEuroFiatAmount(savingsBalanceV1);
96
97 @observable
98 ExecutionState state = InitialExecutionState();
99
100 @observable
101 String interestRateFormated = "0";
102
103 @observable
104 Money accruedInterest = ZERO;
105
106 @computed
107 String get accruedInterestFormated => accruedInterest.toStringWithPrecision(fractionalDigits: 6);
108
109 @computed
110 String get fiatAccruedInterestFormated => _getDEuroFiatAmount(accruedInterest);
111
112 @observable
113 BigInt approvedTokens = BigInt.zero;
114
115 @computed
116 bool get isEnabled => approvedTokens > BigInt.zero;
117
118 @computed
119 bool get isSavingsActionsEnabled => isEnabled && accruedInterest >= MIN_ACCRUED_INTEREST;
120
121 @observable
122 bool isLoading = true;
123
124 @observable
125 DEuroActionType actionType = DEuroActionType.none;
126
127 @observable
128 PendingTransaction? transaction = null;
129
130 @observable
131 PendingTransaction? approvalTransaction = null;
132
133 @action
134 Future<void> reloadSavingsUserData() async {
135 approvedTokens = await evm!.getDEuroSavingsApproved(_appStore.wallet!) ?? BigInt.zero;
136 savingsBalance = await evm!.getDEuroSavingsBalance(_appStore.wallet!) ?? ZERO;
137 accruedInterest = await evm!.getDEuroAccruedInterest(_appStore.wallet!) ?? ZERO;
138
139 final v1Balance = await evm!.getDEuroSavingsV1Balance(_appStore.wallet!) ?? ZERO;
140 savingsBalanceV1 = v1Balance.isZero ? null : v1Balance;
141
142 isLoading = false;
143 }
144
145 @action
146 Future<void> reloadInterestRate() async {
147 final interestRateRaw = await evm!.getDEuroInterestRate(_appStore.wallet!) ?? BigInt.zero;
148
149 interestRateFormated = (interestRateRaw / BigInt.from(10000)).toString();
150 }
151
152 @action
153 Future<void> prepareApproval() async {
154 final ethBalance = balanceViewModel.balances[CryptoCurrency.eth]?.availableBalance ?? "0";
155 if ((double.tryParse(ethBalance) ?? 0) == 0) {
156 state = NoEtherState();
157 return;
158 }
159 try {
160 state = TransactionCommitting();
161 final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
162 final approval = await evm!.enableDEuroSaving(_appStore.wallet!, priority);
163 if (approval == null) throw Exception("DEuro saving not available");
164
165 approvalTransaction = approval;
166 state = InitialExecutionState();
167 } catch (e) {
168 state = FailureState(e.toString());
169 }
170 }
171
172 @action
173 Future<void> prepareSavingsEdit(String amountRaw, bool isAdding) async {
174 try {
175 state = TransactionCommitting();
176
177 if (amountRaw.isEmpty || amountRaw.trim().isEmpty) {
178 throw Exception("Invalid amount: amount cannot be empty");
179 }
180
181 final amount = tryParseFixed(amountRaw, 18);
182
183 if (amount == BigInt.zero || amount == null) {
184 throw Exception("Invalid amount: amount cannot be zero");
185 }
186
187 final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
188 actionType = isAdding ? DEuroActionType.deposit : DEuroActionType.withdraw;
189 final tx = await (isAdding
190 ? evm!.addDEuroSaving(_appStore.wallet!, amount, priority)
191 : evm!.removeDEuroSaving(_appStore.wallet!, amount, priority));
192 if (tx == null) throw Exception("DEuro saving not available");
193
194 transaction = tx;
195 state = InitialExecutionState();
196 } catch (e) {
197 state = FailureState(e.toString());
198 }
199 }
200
201 @action
202 Future<void> prepareSavingsV1Withdraw() async {
203 try {
204 state = TransactionCommitting();
205 actionType = DEuroActionType.withdraw;
206 final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
207 final tx = await evm!.withdrawDEuroSavingV1(_appStore.wallet!, priority);
208 if (tx == null) throw Exception("DEuro saving not available");
209
210 transaction = tx;
211 state = InitialExecutionState();
212 } catch (e) {
213 state = FailureState(e.toString());
214 }
215 }
216
217 Future<void> prepareCollectInterest() async {
218 if (accruedInterest < MIN_ACCRUED_INTEREST) {
219 state = FailureState("Accrued interest is below minimum threshold");
220 return;
221 }
222
223 final formatted = accruedInterestFormated;
224 if (formatted.isEmpty || formatted == "0.000000") {
225 state = FailureState("Invalid accrued interest amount");
226 return;
227 }
228
229 await prepareSavingsEdit(formatted, false);
230 }
231
232 Future<void> prepareReinvestInterest() async {
233 try {
234 state = TransactionCommitting();
235 actionType = DEuroActionType.reinvest;
236 final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
237 final tx = await evm!.reinvestDEuroInterest(_appStore.wallet!, priority);
238 if (tx == null) throw Exception("DEuro saving not available");
239
240 transaction = tx;
241 state = InitialExecutionState();
242 } catch (e) {
243 state = FailureState(e.toString());
244 }
245 }
246
247 @action
248 Future<void> commitTransaction() async {
249 if (transaction != null) {
250 try {
251 state = TransactionCommitting();
252 await transaction!.commit();
253 transaction = null;
254 actionType = DEuroActionType.none;
255 reloadSavingsUserData();
256 state = TransactionCommitted();
257 } catch (e) {
258 state = FailureState(e.toString());
259 }
260 }
261 }
262
263 @action
264 Future<void> commitApprovalTransaction() async {
265 if (approvalTransaction != null) {
266 try {
267 state = TransactionCommitting();
268 await approvalTransaction!.commit();
269 approvalTransaction = null;
270 reloadSavingsUserData();
271 state = TransactionCommitted();
272 } catch (e) {
273 state = FailureState(e.toString());
274 }
275 }
276 }
277
278 @action
279 void dismissTransaction() {
280 transaction = null;
281 approvalTransaction = null;
282 actionType = DEuroActionType.none;
283 state = InitialExecutionState();
284 }
285
286 String _getDEuroFiatAmount(Money? amount) {
287 if (amount == null) return "0.00";
288
289 try {
290 var dEuro = CryptoCurrency.deuro;
291 final keys = _fiatConversationStore.prices.keys.toList();
292 for (final key in keys) {
293 if (key.title == "DEURO") dEuro = key;
294 }
295 return calculateFiatAmount(
296 price: _fiatConversationStore.prices[dEuro]!,
297 cryptoAmount: amount.toStringWithPrecision(fractionalDigits: 6),
298 );
299 } catch (_) {
300 return "0.00";
301 }
302 }
303 }
304
305 class NoEtherState extends ExecutionState {}
306
307 enum DEuroActionType { deposit, withdraw, reinvest, none }