CAKE-345 | added monero_output.dart to the app; fixed transaction_history.dart; renamed SendItem on Output; calculated formattedCryptoAmount in the output.dart; used outputs list instead sendItemList; fixed bitcoin_transaction_credentials.dart, electrum_wallet.dart, monero_transaction_creation_credentials.dart, monero_wallet.dart, exchange and send pages, view models

OleksandrSobol committed Aug 10, 2021 at 17:52 UTC 1e3ec8da1c40f1fdb087b55e8bb915c952fd5850
15 files changed +191 -188
cw_monero/lib/monero_output.dart new
+8
@@ -0,0 +1,8 @@
1 +import 'package:flutter/foundation.dart';
2 +
3 +class MoneroOutput {
4 + MoneroOutput({@required this.address, @required this.amount});
5 +
6 + final String address;
7 + final String amount;
8 +}
\ No newline at end of file
cw_monero/lib/transaction_history.dart
+13 -20
@@ -1,5 +1,6 @@
1 import 'dart:ffi';
2 import 'package:cw_monero/convert_utf8_to_string.dart';
3 +import 'package:cw_monero/monero_output.dart';
4 import 'package:cw_monero/structs/ut8_box.dart';
5 import 'package:ffi/ffi.dart';
6 import 'package:flutter/foundation.dart';
@@ -107,21 +108,21 @@ PendingTransactionDescription createTransactionSync(
108 }
109
110 PendingTransactionDescription createTransactionMultDestSync(
110 - {List<String> addresses,
111 + {List<MoneroOutput> outputs,
112 String paymentId,
112 - List<String> amounts,
113 - int size,
113 int priorityRaw,
114 int accountIndex = 0}) {
116 - final List<Pointer<Utf8>> addressesPointers = addresses.map(Utf8.toUtf8).toList();
115 + final int size = outputs.length;
116 + final List<Pointer<Utf8>> addressesPointers = outputs.map((output) =>
117 + Utf8.toUtf8(output.address)).toList();
118 final Pointer<Pointer<Utf8>> addressesPointerPointer = allocate(count: size);
118 -
119 - final List<Pointer<Utf8>> amountsPointers = amounts.map(Utf8.toUtf8).toList();
119 + final List<Pointer<Utf8>> amountsPointers = outputs.map((output) =>
120 + Utf8.toUtf8(output.amount)).toList();
121 final Pointer<Pointer<Utf8>> amountsPointerPointer = allocate(count: size);
122
123 for (int i = 0; i < size; i++) {
123 - addressesPointerPointer[ i ] = addressesPointers[ i ];
124 - amountsPointerPointer[ i ] = amountsPointers[ i ];
124 + addressesPointerPointer[i] = addressesPointers[i];
125 + amountsPointerPointer[i] = amountsPointers[i];
126 }
127
128 final paymentIdPointer = Utf8.toUtf8(paymentId);
@@ -190,18 +191,14 @@ PendingTransactionDescription _createTransactionSync(Map args) {
191 }
192
193 PendingTransactionDescription _createTransactionMultDestSync(Map args) {
193 - final addresses = args['addresses'] as List<String>;
194 + final outputs = args['outputs'] as List<MoneroOutput>;
195 final paymentId = args['paymentId'] as String;
195 - final amounts = args['amounts'] as List<String>;
196 - final size = args['size'] as int;
196 final priorityRaw = args['priorityRaw'] as int;
197 final accountIndex = args['accountIndex'] as int;
198
199 return createTransactionMultDestSync(
201 - addresses: addresses,
200 + outputs: outputs,
201 paymentId: paymentId,
203 - amounts: amounts,
204 - size: size,
202 priorityRaw: priorityRaw,
203 accountIndex: accountIndex);
204 }
@@ -221,17 +218,13 @@ Future<PendingTransactionDescription> createTransaction(
218 });
219
220 Future<PendingTransactionDescription> createTransactionMultDest(
224 - {List<String> addresses,
221 + {List<MoneroOutput> outputs,
222 String paymentId,
226 - List<String> amounts,
227 - int size,
223 int priorityRaw,
224 int accountIndex = 0}) =>
225 compute(_createTransactionMultDestSync, {
231 - 'addresses': addresses,
226 + 'outputs': outputs,
227 'paymentId': paymentId,
233 - 'amounts': amounts,
234 - 'size': size,
228 'priorityRaw': priorityRaw,
229 'accountIndex': accountIndex
230 });
lib/bitcoin/bitcoin_transaction_credentials.dart
+3 -3
@@ -1,9 +1,9 @@
1 import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
2 -import 'package:cake_wallet/view_model/send/send_item.dart';
2 +import 'package:cake_wallet/view_model/send/output.dart';
3
4 class BitcoinTransactionCredentials {
5 - BitcoinTransactionCredentials(this.sendItemList, this.priority);
5 + BitcoinTransactionCredentials(this.outputs, this.priority);
6
7 - final List<SendItem> sendItemList;
7 + final List<Output> outputs;
8 BitcoinTransactionPriority priority;
9 }
lib/bitcoin/electrum_wallet.dart
+24 -30
@@ -158,7 +158,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
158 const minAmount = 546;
159 final transactionCredentials = credentials as BitcoinTransactionCredentials;
160 final inputs = <BitcoinUnspent>[];
161 - final sendItemList = transactionCredentials.sendItemList;
161 + final outputs = transactionCredentials.outputs;
162 + final hasMultiDestination = outputs.length > 1;
163 var allInputsAmount = 0;
164
165 if (unspentCoins.isEmpty) {
@@ -177,61 +178,54 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
178 }
179
180 final allAmountFee = feeAmountForPriority(
180 - transactionCredentials.priority, inputs.length, sendItemList.length);
181 + transactionCredentials.priority, inputs.length, outputs.length);
182 final allAmount = allInputsAmount - allAmountFee;
183
184 var credentialsAmount = 0;
185 var amount = 0;
186 var fee = 0;
187
187 - if (sendItemList.length > 1) {
188 - final sendAllItems = sendItemList.where((item) => item.sendAll).toList();
188 + if (hasMultiDestination) {
189 + final sendAllItems = outputs.where((item) => item.sendAll).toList();
190
191 if (sendAllItems?.isNotEmpty ?? false) {
192 throw BitcoinTransactionWrongBalanceException(currency);
193 }
194
194 - final nullAmountItems = sendItemList.where((item) =>
195 - stringDoubleToBitcoinAmount(item.cryptoAmount.replaceAll(',', '.')) <= 0)
196 - .toList();
195 + final nullAmountItems = outputs.where((item) =>
196 + item.formattedCryptoAmount <= 0).toList();
197
198 if (nullAmountItems?.isNotEmpty ?? false) {
199 throw BitcoinTransactionWrongBalanceException(currency);
200 }
201
202 - credentialsAmount = sendItemList.fold(0, (previousValue, element) =>
203 - previousValue + stringDoubleToBitcoinAmount(
204 - element.cryptoAmount.replaceAll(',', '.')));
202 + credentialsAmount = outputs.fold(0, (acc, value) =>
203 + acc + value.formattedCryptoAmount);
204
206 - if (credentialsAmount > allAmount) {
205 + if (allAmount - credentialsAmount < minAmount) {
206 throw BitcoinTransactionWrongBalanceException(currency);
207 }
208
210 - amount = allAmount - credentialsAmount < minAmount
211 - ? allAmount
212 - : credentialsAmount;
209 + amount = credentialsAmount;
210
214 - fee = amount == allAmount
215 - ? allAmountFee
216 - : calculateEstimatedFee(transactionCredentials.priority, amount,
217 - outputsCount: sendItemList.length + 1);
211 + fee = calculateEstimatedFee(transactionCredentials.priority, amount,
212 + outputsCount: outputs.length + 1);
213 } else {
219 - final sendItem = sendItemList.first;
214 + final output = outputs.first;
215
221 - credentialsAmount = !sendItem.sendAll
222 - ? stringDoubleToBitcoinAmount(
223 - sendItem.cryptoAmount.replaceAll(',', '.'))
216 + credentialsAmount = !output.sendAll
217 + ? output.formattedCryptoAmount
218 : 0;
219
220 if (credentialsAmount > allAmount) {
221 throw BitcoinTransactionWrongBalanceException(currency);
222 }
223
230 - amount = sendItem.sendAll || allAmount - credentialsAmount < minAmount
224 + amount = output.sendAll || allAmount - credentialsAmount < minAmount
225 ? allAmount
226 : credentialsAmount;
227
234 - fee = sendItem.sendAll || amount == allAmount
228 + fee = output.sendAll || amount == allAmount
229 ? allAmountFee
230 : calculateEstimatedFee(transactionCredentials.priority, amount);
231 }
@@ -289,18 +283,18 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
283 }
284 });
285
292 - sendItemList.forEach((item) {
293 - final _amount = item.sendAll
294 - ? amount
295 - : stringDoubleToBitcoinAmount(item.cryptoAmount.replaceAll(',', '.'));
286 + outputs.forEach((item) {
287 + final outputAmount = hasMultiDestination
288 + ? item.formattedCryptoAmount
289 + : amount;
290
291 txb.addOutput(
292 addressToOutputScript(item.address, networkType),
299 - _amount);
293 + outputAmount);
294 });
295
296 final estimatedSize =
303 - estimatedTransactionSize(inputs.length, sendItemList.length + 1);
297 + estimatedTransactionSize(inputs.length, outputs.length + 1);
298 final feeAmount = feeRate(transactionCredentials.priority) * estimatedSize;
299 final changeValue = totalInputAmount - amount - feeAmount;
300
lib/monero/monero_transaction_creation_credentials.dart
+3 -3
@@ -1,11 +1,11 @@
1 import 'package:cake_wallet/entities/transaction_creation_credentials.dart';
2 import 'package:cake_wallet/entities/monero_transaction_priority.dart';
3 -import 'package:cake_wallet/view_model/send/send_item.dart';
3 +import 'package:cake_wallet/view_model/send/output.dart';
4
5 class MoneroTransactionCreationCredentials
6 extends TransactionCreationCredentials {
7 - MoneroTransactionCreationCredentials({this.sendItemList, this.priority});
7 + MoneroTransactionCreationCredentials({this.outputs, this.priority});
8
9 - final List<SendItem> sendItemList;
9 + final List<Output> outputs;
10 final MoneroTransactionPriority priority;
11 }
lib/monero/monero_wallet.dart
+22 -23
@@ -13,6 +13,7 @@ import 'package:cw_monero/transaction_history.dart'
13 import 'package:cw_monero/wallet.dart';
14 import 'package:cw_monero/wallet.dart' as monero_wallet;
15 import 'package:cw_monero/transaction_history.dart' as transaction_history;
16 +import 'package:cw_monero/monero_output.dart';
17 import 'package:cake_wallet/monero/monero_transaction_creation_credentials.dart';
18 import 'package:cake_wallet/monero/pending_monero_transaction.dart';
19 import 'package:cake_wallet/monero/monero_wallet_keys.dart';
@@ -150,8 +151,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
151 @override
152 Future<PendingTransaction> createTransaction(Object credentials) async {
153 final _credentials = credentials as MoneroTransactionCreationCredentials;
153 - final sendItemList = _credentials.sendItemList;
154 - final listSize = sendItemList.length;
154 + final outputs = _credentials.outputs;
155 + final hasMultiDestination = outputs.length > 1;
156 final unlockedBalance =
157 monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id);
158
@@ -161,16 +162,15 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
162 throw MoneroTransactionCreationException('The wallet is not synced.');
163 }
164
164 - if (listSize > 1) {
165 - final sendAllItems = sendItemList.where((item) => item.sendAll).toList();
165 + if (hasMultiDestination) {
166 + final sendAllItems = outputs.where((item) => item.sendAll).toList();
167
168 if (sendAllItems?.isNotEmpty ?? false) {
169 throw MoneroTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
170 }
171
171 - final nullAmountItems = sendItemList.where((item) =>
172 - moneroParseAmount(amount: item.cryptoAmount.replaceAll(',', '.')) <= 0)
173 - .toList();
172 + final nullAmountItems = outputs.where((item) =>
173 + item.formattedCryptoAmount <= 0).toList();
174
175 if (nullAmountItems?.isNotEmpty ?? false) {
176 throw MoneroTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
@@ -178,42 +178,41 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
178
179 var credentialsAmount = 0;
180
181 - credentialsAmount = sendItemList.fold(0, (previousValue, element) =>
182 - previousValue + moneroParseAmount(
183 - amount: element.cryptoAmount.replaceAll(',', '.')));
181 + credentialsAmount = outputs.fold(0, (acc, value) =>
182 + acc + value.formattedCryptoAmount);
183
184 if (unlockedBalance < credentialsAmount) {
185 throw MoneroTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
186 }
187
189 - final addresses = sendItemList.map((e) => e.address).toList();
190 - final amounts = sendItemList.map((e) =>
191 - e.cryptoAmount.replaceAll(',', '.')).toList();
188 + final moneroOutputs = outputs.map((output) =>
189 + MoneroOutput(
190 + address: output.address,
191 + amount: output.cryptoAmount.replaceAll(',', '.')))
192 + .toList();
193
194 pendingTransactionDescription =
195 await transaction_history.createTransactionMultDest(
195 - addresses: addresses,
196 + outputs: moneroOutputs,
197 paymentId: '',
197 - amounts: amounts,
198 - size: listSize,
198 priorityRaw: _credentials.priority.serialize(),
199 accountIndex: walletAddresses.account.id);
200 } else {
202 - final item = sendItemList.first;
203 - final address = item.address;
204 - final amount = item.sendAll
201 + final output = outputs.first;
202 + final address = output.address;
203 + final amount = output.sendAll
204 ? null
206 - : item.cryptoAmount.replaceAll(',', '.');
207 - final formattedAmount = item.sendAll
205 + : output.cryptoAmount.replaceAll(',', '.');
206 + final formattedAmount = output.sendAll
207 ? null
209 - : moneroParseAmount(amount: amount);
208 + : output.formattedCryptoAmount;
209
210 if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
211 (formattedAmount == null && unlockedBalance <= 0)) {
212 final formattedBalance = moneroAmountToString(amount: unlockedBalance);
213
214 throw MoneroTransactionCreationException(
216 - 'Incorrect unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${item.cryptoAmount}.');
215 + 'Incorrect unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
216 }
217
218 pendingTransactionDescription =
lib/src/screens/exchange_trade/exchange_trade_page.dart
+2 -2
@@ -385,8 +385,8 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
385 .pendingTransactionFiatAmount +
386 ' ' +
387 widget.exchangeTradeViewModel.sendViewModel.fiat.title,
388 - sendItemList: widget.exchangeTradeViewModel.sendViewModel
389 - .sendItemList);
388 + outputs: widget.exchangeTradeViewModel.sendViewModel
389 + .outputs);
390 });
391 });
392 }
lib/src/screens/send/send_page.dart
+22 -22
@@ -3,7 +3,7 @@ import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_a
3 import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
4 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
5 import 'package:cake_wallet/src/widgets/template_tile.dart';
6 -import 'package:cake_wallet/view_model/send/send_item.dart';
6 +import 'package:cake_wallet/view_model/send/output.dart';
7 import 'package:flutter/cupertino.dart';
8 import 'package:flutter/material.dart';
9 import 'package:flutter_mobx/flutter_mobx.dart';
@@ -55,16 +55,16 @@ class SendPage extends BasePage {
55 onPressed: () {
56 var pageToJump = controller.page.round() - 1;
57 pageToJump = pageToJump > 0 ? pageToJump : 0;
58 - final item = _defineCurrentSendItem();
59 - sendViewModel.removeSendItem(item);
58 + final output = _defineCurrentOutput();
59 + sendViewModel.removeOutput(output);
60 controller.jumpToPage(pageToJump);
61 })
62 : TrailButton(
63 caption: S.of(context).clear,
64 onPressed: () {
65 - final item = _defineCurrentSendItem();
65 + final output = _defineCurrentOutput();
66 _formKey.currentState.reset();
67 - item.reset();
67 + output.reset();
68 });
69 });
70
@@ -85,13 +85,13 @@ class SendPage extends BasePage {
85 return PageView.builder(
86 scrollDirection: Axis.horizontal,
87 controller: controller,
88 - itemCount: sendViewModel.sendItemList.length,
88 + itemCount: sendViewModel.outputs.length,
89 itemBuilder: (context, index) {
90 - final item = sendViewModel.sendItemList[index];
90 + final output = sendViewModel.outputs[index];
91
92 return SendCard(
93 - key: item.key,
94 - item: item,
93 + key: output.key,
94 + output: output,
95 sendViewModel: sendViewModel,
96 );
97 }
@@ -104,7 +104,7 @@ class SendPage extends BasePage {
104 child: Container(
105 height: 10,
106 child: Observer(builder: (_) {
107 - final count = sendViewModel.sendItemList.length;
107 + final count = sendViewModel.outputs.length;
108
109 return count > 1
110 ? SmoothPageIndicator(
@@ -211,11 +211,11 @@ class SendPage extends BasePage {
211 amount: template.amount,
212 from: template.cryptoCurrency,
213 onTap: () async {
214 - final item = _defineCurrentSendItem();
215 - item.address =
214 + final output = _defineCurrentOutput();
215 + output.address =
216 template.address;
217 - item.setCryptoAmount(template.amount);
218 - final parsedAddress = await item
217 + output.setCryptoAmount(template.amount);
218 + final parsedAddress = await output
219 .applyOpenaliasOrUnstoppableDomains();
220 showAddressAlert(context, parsedAddress);
221 },
@@ -263,7 +263,7 @@ class SendPage extends BasePage {
263 padding: EdgeInsets.only(bottom: 12),
264 child: PrimaryButton(
265 onPressed: () {
266 - sendViewModel.addSendItem();
266 + sendViewModel.addOutput();
267 },
268 text: S.of(context).add_receiver,
269 color: Colors.green,
@@ -274,16 +274,16 @@ class SendPage extends BasePage {
274 return LoadingPrimaryButton(
275 onPressed: () async {
276 if (!_formKey.currentState.validate()) {
277 - if (sendViewModel.sendItemList.length > 1) {
277 + if (sendViewModel.outputs.length > 1) {
278 showErrorValidationAlert(context);
279 }
280
281 return;
282 }
283
284 - final notValidItems = sendViewModel.sendItemList
284 + final notValidItems = sendViewModel.outputs
285 .where((item) =>
286 - item.address.isEmpty || item.cryptoAmount.isEmpty)
286 + item.address.isEmpty || item.cryptoAmount.isEmpty)
287 .toList();
288
289 if (notValidItems?.isNotEmpty ?? false) {
@@ -343,7 +343,7 @@ class SendPage extends BasePage {
343 feeValue: sendViewModel.pendingTransaction.feeFormatted,
344 feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmount
345 + ' ' + sendViewModel.fiat.title,
346 - sendItemList: sendViewModel.sendItemList,
346 + outputs: sendViewModel.outputs,
347 rightButtonText: S.of(context).ok,
348 leftButtonText: S.of(context).cancel,
349 actionRightButton: () {
@@ -381,7 +381,7 @@ class SendPage extends BasePage {
381
382 if (state is TransactionCommitted) {
383 WidgetsBinding.instance.addPostFrameCallback((_) {
384 - sendViewModel.clearSendItemList();
384 + sendViewModel.clearOutputs();
385 });
386 }
387 });
@@ -389,9 +389,9 @@ class SendPage extends BasePage {
389 _effectsInstalled = true;
390 }
391
392 - SendItem _defineCurrentSendItem() {
392 + Output _defineCurrentOutput() {
393 final itemCount = controller.page.round();
394 - return sendViewModel.sendItemList[itemCount];
394 + return sendViewModel.outputs[itemCount];
395 }
396
397 void showErrorValidationAlert(BuildContext context) async {
lib/src/screens/send/send_template_page.dart
+11 -11
@@ -14,7 +14,7 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
14
15 class SendTemplatePage extends BasePage {
16 SendTemplatePage({@required this.sendTemplateViewModel}) {
17 - sendTemplateViewModel.sendItem.reset();
17 + sendTemplateViewModel.output.reset();
18 }
19
20 final SendTemplateViewModel sendTemplateViewModel;
@@ -258,21 +258,21 @@ class SendTemplatePage extends BasePage {
258 return;
259 }
260
261 - final item = sendTemplateViewModel.sendItem;
261 + final output = sendTemplateViewModel.output;
262
263 - reaction((_) => item.fiatAmount, (String amount) {
263 + reaction((_) => output.fiatAmount, (String amount) {
264 if (amount != _fiatAmountController.text) {
265 _fiatAmountController.text = amount;
266 }
267 });
268
269 - reaction((_) => item.cryptoAmount, (String amount) {
269 + reaction((_) => output.cryptoAmount, (String amount) {
270 if (amount != _cryptoAmountController.text) {
271 _cryptoAmountController.text = amount;
272 }
273 });
274
275 - reaction((_) => item.address, (String address) {
275 + reaction((_) => output.address, (String address) {
276 if (address != _addressController.text) {
277 _addressController.text = address;
278 }
@@ -281,24 +281,24 @@ class SendTemplatePage extends BasePage {
281 _cryptoAmountController.addListener(() {
282 final amount = _cryptoAmountController.text;
283
284 - if (amount != item.cryptoAmount) {
285 - item.setCryptoAmount(amount);
284 + if (amount != output.cryptoAmount) {
285 + output.setCryptoAmount(amount);
286 }
287 });
288
289 _fiatAmountController.addListener(() {
290 final amount = _fiatAmountController.text;
291
292 - if (amount != item.fiatAmount) {
293 - item.setFiatAmount(amount);
292 + if (amount != output.fiatAmount) {
293 + output.setFiatAmount(amount);
294 }
295 });
296
297 _addressController.addListener(() {
298 final address = _addressController.text;
299
300 - if (item.address != address) {
301 - item.address = address;
300 + if (output.address != address) {
301 + output.address = address;
302 }
303 });
304
lib/src/screens/send/widgets/confirm_sending_alert.dart
+7 -7
@@ -1,5 +1,5 @@
1 import 'package:cake_wallet/palette.dart';
2 -import 'package:cake_wallet/view_model/send/send_item.dart';
2 +import 'package:cake_wallet/view_model/send/output.dart';
3 import 'package:flutter/material.dart';
4 import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
@@ -13,14 +13,14 @@ class ConfirmSendingAlert extends BaseAlertDialog {
13 @required this.fee,
14 @required this.feeValue,
15 @required this.feeFiatAmount,
16 - @required this.sendItemList,
16 + @required this.outputs,
17 @required this.leftButtonText,
18 @required this.rightButtonText,
19 @required this.actionLeftButton,
20 @required this.actionRightButton,
21 this.alertBarrierDismissible = true
22 }) {
23 - itemCount = sendItemList.length;
23 + itemCount = outputs.length;
24 recipientTitle = itemCount > 1
25 ? S.current.transaction_details_recipient_address
26 : S.current.recipient_address;
@@ -33,7 +33,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
33 final String fee;
34 final String feeValue;
35 final String feeFiatAmount;
36 - final List<SendItem> sendItemList;
36 + final List<Output> outputs;
37 final String leftButtonText;
38 final String rightButtonText;
39 final VoidCallback actionLeftButton;
@@ -179,10 +179,10 @@ class ConfirmSendingAlert extends BaseAlertDialog {
179 physics: NeverScrollableScrollPhysics(),
180 itemCount: itemCount,
181 itemBuilder: (context, index) {
182 - final item = sendItemList[index];
182 + final item = outputs[index];
183 final _address = item.address;
184 final _amount =
185 - item.cryptoAmount.replaceAll(',', '.');
185 + item.cryptoAmount.replaceAll(',', '.');
186
187 return Column(
188 children: [
@@ -225,7 +225,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
225 : Padding(
226 padding: EdgeInsets.only(top: 8),
227 child: Text(
228 - sendItemList.first.address,
228 + outputs.first.address,
229 style: TextStyle(
230 fontSize: 12,
231 fontWeight: FontWeight.w600,
lib/src/screens/send/widgets/send_card.dart
+34 -34
@@ -4,7 +4,7 @@ import 'package:cake_wallet/routes.dart';
4 import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart';
5 import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
6 import 'package:cake_wallet/src/widgets/picker.dart';
7 -import 'package:cake_wallet/view_model/send/send_item.dart';
7 +import 'package:cake_wallet/view_model/send/output.dart';
8 import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
9 import 'package:flutter/cupertino.dart';
10 import 'package:flutter/material.dart';
@@ -19,21 +19,21 @@ import 'package:cake_wallet/generated/i18n.dart';
19 import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
20
21 class SendCard extends StatefulWidget {
22 - SendCard({Key key, @required this.item, @required this.sendViewModel}) : super(key: key);
22 + SendCard({Key key, @required this.output, @required this.sendViewModel}) : super(key: key);
23
24 - final SendItem item;
24 + final Output output;
25 final SendViewModel sendViewModel;
26
27 @override
28 SendCardState createState() => SendCardState(
29 - item: item,
29 + output: output,
30 sendViewModel: sendViewModel
31 );
32 }
33
34 class SendCardState extends State<SendCard>
35 with AutomaticKeepAliveClientMixin<SendCard> {
36 - SendCardState({@required this.item, @required this.sendViewModel})
36 + SendCardState({@required this.output, @required this.sendViewModel})
37 : addressController = TextEditingController(),
38 cryptoAmountController = TextEditingController(),
39 fiatAmountController = TextEditingController(),
@@ -45,7 +45,7 @@ class SendCardState extends State<SendCard>
45 static const prefixIconWidth = 34.0;
46 static const prefixIconHeight = 34.0;
47
48 - final SendItem item;
48 + final Output output;
49 final SendViewModel sendViewModel;
50
51 final TextEditingController addressController;
@@ -143,7 +143,7 @@ class SendCardState extends State<SendCard>
143 .decorationColor),
144 onPushPasteButton: (context) async {
145 final parsedAddress =
146 - await item.applyOpenaliasOrUnstoppableDomains();
146 + await output.applyOpenaliasOrUnstoppableDomains();
147 showAddressAlert(context, parsedAddress);
148 },
149 validator: sendViewModel.addressValidator,
@@ -189,7 +189,7 @@ class SendCardState extends State<SendCard>
189 .decorationColor,
190 fontWeight: FontWeight.w500,
191 fontSize: 14),
192 - validator: item.sendAll
192 + validator: output.sendAll
193 ? sendViewModel.allAmountValidator
194 : sendViewModel
195 .amountValidator),
@@ -201,7 +201,7 @@ class SendCardState extends State<SendCard>
201 height: prefixIconHeight,
202 child: InkWell(
203 onTap: () async =>
204 - item.setSendAll(),
204 + output.setSendAll(),
205 child: Container(
206 decoration: BoxDecoration(
207 color: Theme.of(context)
@@ -349,7 +349,7 @@ class SendCardState extends State<SendCard>
349 crossAxisAlignment: CrossAxisAlignment.end,
350 children: [
351 Text(
352 - item
352 + output
353 .estimatedFee
354 .toString() +
355 ' ' +
@@ -366,7 +366,7 @@ class SendCardState extends State<SendCard>
366 padding:
367 EdgeInsets.only(top: 5),
368 child: Text(
369 - item
369 + output
370 .estimatedFeeFiatAmount
371 + ' ' +
372 sendViewModel
@@ -435,10 +435,10 @@ class SendCardState extends State<SendCard>
435 }
436
437 void _setEffects(BuildContext context) {
438 - addressController.text = item.address;
439 - cryptoAmountController.text = item.cryptoAmount;
440 - fiatAmountController.text = item.fiatAmount;
441 - noteController.text = item.note;
438 + addressController.text = output.address;
439 + cryptoAmountController.text = output.cryptoAmount;
440 + fiatAmountController.text = output.fiatAmount;
441 + noteController.text = output.note;
442
443 if (_effectsInstalled) {
444 return;
@@ -447,48 +447,48 @@ class SendCardState extends State<SendCard>
447 cryptoAmountController.addListener(() {
448 final amount = cryptoAmountController.text;
449
450 - if (item.sendAll && amount != S.current.all) {
451 - item.sendAll = false;
450 + if (output.sendAll && amount != S.current.all) {
451 + output.sendAll = false;
452 }
453
454 - if (amount != item.cryptoAmount) {
455 - item.setCryptoAmount(amount);
454 + if (amount != output.cryptoAmount) {
455 + output.setCryptoAmount(amount);
456 }
457 });
458
459 fiatAmountController.addListener(() {
460 final amount = fiatAmountController.text;
461
462 - if (amount != item.fiatAmount) {
463 - item.sendAll = false;
464 - item.setFiatAmount(amount);
462 + if (amount != output.fiatAmount) {
463 + output.sendAll = false;
464 + output.setFiatAmount(amount);
465 }
466 });
467
468 noteController.addListener(() {
469 final note = noteController.text ?? '';
470
471 - if (note != item.note) {
472 - item.note = note;
471 + if (note != output.note) {
472 + output.note = note;
473 }
474 });
475
476 - reaction((_) => item.sendAll, (bool all) {
476 + reaction((_) => output.sendAll, (bool all) {
477 if (all) {
478 cryptoAmountController.text = S.current.all;
479 fiatAmountController.text = null;
480 }
481 });
482
483 - reaction((_) => item.fiatAmount, (String amount) {
483 + reaction((_) => output.fiatAmount, (String amount) {
484 if (amount != fiatAmountController.text) {
485 fiatAmountController.text = amount;
486 }
487 });
488
489 - reaction((_) => item.cryptoAmount, (String amount) {
490 - if (item.sendAll && amount != S.current.all) {
491 - item.sendAll = false;
489 + reaction((_) => output.cryptoAmount, (String amount) {
490 + if (output.sendAll && amount != S.current.all) {
491 + output.sendAll = false;
492 }
493
494 if (amount != cryptoAmountController.text) {
@@ -496,7 +496,7 @@ class SendCardState extends State<SendCard>
496 }
497 });
498
499 - reaction((_) => item.address, (String address) {
499 + reaction((_) => output.address, (String address) {
500 if (address != addressController.text) {
501 addressController.text = address;
502 }
@@ -505,12 +505,12 @@ class SendCardState extends State<SendCard>
505 addressController.addListener(() {
506 final address = addressController.text;
507
508 - if (item.address != address) {
509 - item.address = address;
508 + if (output.address != address) {
509 + output.address = address;
510 }
511 });
512
513 - reaction((_) => item.note, (String note) {
513 + reaction((_) => output.note, (String note) {
514 if (note != noteController.text) {
515 noteController.text = note;
516 }
@@ -518,7 +518,7 @@ class SendCardState extends State<SendCard>
518
519 addressFocusNode.addListener(() async {
520 if (!addressFocusNode.hasFocus && addressController.text.isNotEmpty) {
521 - final parsedAddress = await item.applyOpenaliasOrUnstoppableDomains();
521 + final parsedAddress = await output.applyOpenaliasOrUnstoppableDomains();
522 showAddressAlert(context, parsedAddress);
523 }
524 });
lib/view_model/exchange/exchange_trade_view_model.dart
+4 -4
@@ -79,11 +79,11 @@ abstract class ExchangeTradeViewModelBase with Store {
79 return;
80 }
81
82 - sendViewModel.clearSendItemList();
83 - final item = sendViewModel.sendItemList.first;
82 + sendViewModel.clearOutputs();
83 + final output = sendViewModel.outputs.first;
84
85 - item.address = trade.inputAddress;
86 - item.setCryptoAmount(trade.amount);
85 + output.address = trade.inputAddress;
86 + output.setCryptoAmount(trade.amount);
87 await sendViewModel.createTransaction();
88 }
89
lib/view_model/send/output.dart renamed
+16 -7
@@ -15,14 +15,14 @@ import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
15 import 'package:cake_wallet/store/settings_store.dart';
16 import 'package:cake_wallet/generated/i18n.dart';
17
18 -part 'send_item.g.dart';
18 +part 'output.g.dart';
19
20 const String cryptoNumberPattern = '0.0';
21
22 -class SendItem = SendItemBase with _$SendItem;
22 +class Output = OutputBase with _$Output;
23
24 -abstract class SendItemBase with Store {
25 - SendItemBase(this._wallet, this._settingsStore, this._fiatConversationStore)
24 +abstract class OutputBase with Store {
25 + OutputBase(this._wallet, this._settingsStore, this._fiatConversationStore)
26 :_cryptoNumberFormat = NumberFormat(cryptoNumberPattern) {
27 reset();
28 _setCryptoNumMaximumFractionDigits();
@@ -47,8 +47,8 @@ abstract class SendItemBase with Store {
47 bool sendAll;
48
49 @computed
50 - double get estimatedFee {
51 - int amount;
50 + int get formattedCryptoAmount {
51 + int amount = 0;
52
53 try {
54 if (cryptoAmount?.isNotEmpty ?? false) {
@@ -72,9 +72,18 @@ abstract class SendItemBase with Store {
72 amount = _amount;
73 }
74 }
75 + } catch(e) {
76 + amount = 0;
77 + }
78 +
79 + return amount;
80 + }
81
82 + @computed
83 + double get estimatedFee {
84 + try {
85 final fee = _wallet.calculateEstimatedFee(
77 - _settingsStore.priority[_wallet.type], amount);
86 + _settingsStore.priority[_wallet.type], formattedCryptoAmount);
87
88 if (_wallet is ElectrumWallet) {
89 return bitcoinAmountToDouble(amount: fee);
lib/view_model/send/send_template_view_model.dart
+3 -3
@@ -1,4 +1,4 @@
1 -import 'package:cake_wallet/view_model/send/send_item.dart';
1 +import 'package:cake_wallet/view_model/send/output.dart';
2 import 'package:mobx/mobx.dart';
3 import 'package:cake_wallet/entities/template.dart';
4 import 'package:cake_wallet/store/templates/send_template_store.dart';
@@ -21,10 +21,10 @@ abstract class SendTemplateViewModelBase with Store {
21 SendTemplateViewModelBase(this._wallet, this._settingsStore,
22 this._sendTemplateStore, this._fiatConversationStore) {
23
24 - sendItem = SendItem(_wallet, _settingsStore, _fiatConversationStore);
24 + output = Output(_wallet, _settingsStore, _fiatConversationStore);
25 }
26
27 - SendItem sendItem;
27 + Output output;
28
29 Validator get amountValidator => AmountValidator(type: _wallet.type);
30
lib/view_model/send/send_view_model.dart
+19 -19
@@ -2,7 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
2 import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
3 import 'package:cake_wallet/entities/transaction_description.dart';
4 import 'package:cake_wallet/entities/transaction_priority.dart';
5 -import 'package:cake_wallet/view_model/send/send_item.dart';
5 +import 'package:cake_wallet/view_model/send/output.dart';
6 import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
7 import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
8 import 'package:hive/hive.dart';
@@ -42,33 +42,33 @@ abstract class SendViewModelBase with Store {
42 _settingsStore.priority[_wallet.type] = priorities.first;
43 }
44
45 - sendItemList = ObservableList<SendItem>()
46 - ..add(SendItem(_wallet, _settingsStore, _fiatConversationStore));
45 + outputs = ObservableList<Output>()
46 + ..add(Output(_wallet, _settingsStore, _fiatConversationStore));
47 }
48
49 @observable
50 ExecutionState state;
51
52 - ObservableList<SendItem> sendItemList;
52 + ObservableList<Output> outputs;
53
54 @action
55 - void addSendItem() {
56 - sendItemList.add(SendItem(_wallet, _settingsStore, _fiatConversationStore));
55 + void addOutput() {
56 + outputs.add(Output(_wallet, _settingsStore, _fiatConversationStore));
57 }
58
59 @action
60 - void removeSendItem(SendItem item) {
61 - sendItemList.remove(item);
60 + void removeOutput(Output output) {
61 + outputs.remove(output);
62 }
63
64 @action
65 - void clearSendItemList() {
66 - sendItemList.clear();
67 - addSendItem();
65 + void clearOutputs() {
66 + outputs.clear();
67 + addOutput();
68 }
69
70 @computed
71 - bool get isBatchSending => sendItemList.length > 1;
71 + bool get isBatchSending => outputs.length > 1;
72
73 @computed
74 String get pendingTransactionFiatAmount {
@@ -150,14 +150,14 @@ abstract class SendViewModelBase with Store {
150
151 @action
152 Future<void> commitTransaction() async {
153 - String address = sendItemList.fold('', (previousValue, item) {
154 - return previousValue + item.address + '\n';
153 + String address = outputs.fold('', (acc, value) {
154 + return acc + value.address + '\n';
155 });
156
157 address = address.trim();
158
159 - String note = sendItemList.fold('', (previousValue, item) {
160 - return previousValue + item.note + '\n';
159 + String note = outputs.fold('', (acc, value) {
160 + return acc + value.note + '\n';
161 });
162
163 note = note.trim();
@@ -192,17 +192,17 @@ abstract class SendViewModelBase with Store {
192 final priority = _settingsStore.priority[_wallet.type];
193
194 return BitcoinTransactionCredentials(
195 - sendItemList, priority as BitcoinTransactionPriority);
195 + outputs, priority as BitcoinTransactionPriority);
196 case WalletType.litecoin:
197 final priority = _settingsStore.priority[_wallet.type];
198
199 return BitcoinTransactionCredentials(
200 - sendItemList, priority as BitcoinTransactionPriority);
200 + outputs, priority as BitcoinTransactionPriority);
201 case WalletType.monero:
202 final priority = _settingsStore.priority[_wallet.type];
203
204 return MoneroTransactionCreationCredentials(
205 - sendItemList: sendItemList,
205 + outputs: outputs,
206 priority: priority as MoneroTransactionPriority);
207 default:
208 return null;