mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-03-12 09:32:33 +00:00
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
This commit is contained in:
parent
1dd3f69b1c
commit
1e3ec8da1c
15 changed files with 191 additions and 188 deletions
8
cw_monero/lib/monero_output.dart
Normal file
8
cw_monero/lib/monero_output.dart
Normal file
|
@ -0,0 +1,8 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class MoneroOutput {
|
||||
MoneroOutput({@required this.address, @required this.amount});
|
||||
|
||||
final String address;
|
||||
final String amount;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:cw_monero/convert_utf8_to_string.dart';
|
||||
import 'package:cw_monero/monero_output.dart';
|
||||
import 'package:cw_monero/structs/ut8_box.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -107,21 +108,21 @@ PendingTransactionDescription createTransactionSync(
|
|||
}
|
||||
|
||||
PendingTransactionDescription createTransactionMultDestSync(
|
||||
{List<String> addresses,
|
||||
{List<MoneroOutput> outputs,
|
||||
String paymentId,
|
||||
List<String> amounts,
|
||||
int size,
|
||||
int priorityRaw,
|
||||
int accountIndex = 0}) {
|
||||
final List<Pointer<Utf8>> addressesPointers = addresses.map(Utf8.toUtf8).toList();
|
||||
final int size = outputs.length;
|
||||
final List<Pointer<Utf8>> addressesPointers = outputs.map((output) =>
|
||||
Utf8.toUtf8(output.address)).toList();
|
||||
final Pointer<Pointer<Utf8>> addressesPointerPointer = allocate(count: size);
|
||||
|
||||
final List<Pointer<Utf8>> amountsPointers = amounts.map(Utf8.toUtf8).toList();
|
||||
final List<Pointer<Utf8>> amountsPointers = outputs.map((output) =>
|
||||
Utf8.toUtf8(output.amount)).toList();
|
||||
final Pointer<Pointer<Utf8>> amountsPointerPointer = allocate(count: size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
addressesPointerPointer[ i ] = addressesPointers[ i ];
|
||||
amountsPointerPointer[ i ] = amountsPointers[ i ];
|
||||
addressesPointerPointer[i] = addressesPointers[i];
|
||||
amountsPointerPointer[i] = amountsPointers[i];
|
||||
}
|
||||
|
||||
final paymentIdPointer = Utf8.toUtf8(paymentId);
|
||||
|
@ -190,18 +191,14 @@ PendingTransactionDescription _createTransactionSync(Map args) {
|
|||
}
|
||||
|
||||
PendingTransactionDescription _createTransactionMultDestSync(Map args) {
|
||||
final addresses = args['addresses'] as List<String>;
|
||||
final outputs = args['outputs'] as List<MoneroOutput>;
|
||||
final paymentId = args['paymentId'] as String;
|
||||
final amounts = args['amounts'] as List<String>;
|
||||
final size = args['size'] as int;
|
||||
final priorityRaw = args['priorityRaw'] as int;
|
||||
final accountIndex = args['accountIndex'] as int;
|
||||
|
||||
return createTransactionMultDestSync(
|
||||
addresses: addresses,
|
||||
outputs: outputs,
|
||||
paymentId: paymentId,
|
||||
amounts: amounts,
|
||||
size: size,
|
||||
priorityRaw: priorityRaw,
|
||||
accountIndex: accountIndex);
|
||||
}
|
||||
|
@ -221,17 +218,13 @@ Future<PendingTransactionDescription> createTransaction(
|
|||
});
|
||||
|
||||
Future<PendingTransactionDescription> createTransactionMultDest(
|
||||
{List<String> addresses,
|
||||
{List<MoneroOutput> outputs,
|
||||
String paymentId,
|
||||
List<String> amounts,
|
||||
int size,
|
||||
int priorityRaw,
|
||||
int accountIndex = 0}) =>
|
||||
compute(_createTransactionMultDestSync, {
|
||||
'addresses': addresses,
|
||||
'outputs': outputs,
|
||||
'paymentId': paymentId,
|
||||
'amounts': amounts,
|
||||
'size': size,
|
||||
'priorityRaw': priorityRaw,
|
||||
'accountIndex': accountIndex
|
||||
});
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_item.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
|
||||
class BitcoinTransactionCredentials {
|
||||
BitcoinTransactionCredentials(this.sendItemList, this.priority);
|
||||
BitcoinTransactionCredentials(this.outputs, this.priority);
|
||||
|
||||
final List<SendItem> sendItemList;
|
||||
final List<Output> outputs;
|
||||
BitcoinTransactionPriority priority;
|
||||
}
|
||||
|
|
|
@ -158,7 +158,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
|
|||
const minAmount = 546;
|
||||
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
||||
final inputs = <BitcoinUnspent>[];
|
||||
final sendItemList = transactionCredentials.sendItemList;
|
||||
final outputs = transactionCredentials.outputs;
|
||||
final hasMultiDestination = outputs.length > 1;
|
||||
var allInputsAmount = 0;
|
||||
|
||||
if (unspentCoins.isEmpty) {
|
||||
|
@ -177,61 +178,54 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
|
|||
}
|
||||
|
||||
final allAmountFee = feeAmountForPriority(
|
||||
transactionCredentials.priority, inputs.length, sendItemList.length);
|
||||
transactionCredentials.priority, inputs.length, outputs.length);
|
||||
final allAmount = allInputsAmount - allAmountFee;
|
||||
|
||||
var credentialsAmount = 0;
|
||||
var amount = 0;
|
||||
var fee = 0;
|
||||
|
||||
if (sendItemList.length > 1) {
|
||||
final sendAllItems = sendItemList.where((item) => item.sendAll).toList();
|
||||
if (hasMultiDestination) {
|
||||
final sendAllItems = outputs.where((item) => item.sendAll).toList();
|
||||
|
||||
if (sendAllItems?.isNotEmpty ?? false) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
final nullAmountItems = sendItemList.where((item) =>
|
||||
stringDoubleToBitcoinAmount(item.cryptoAmount.replaceAll(',', '.')) <= 0)
|
||||
.toList();
|
||||
final nullAmountItems = outputs.where((item) =>
|
||||
item.formattedCryptoAmount <= 0).toList();
|
||||
|
||||
if (nullAmountItems?.isNotEmpty ?? false) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
credentialsAmount = sendItemList.fold(0, (previousValue, element) =>
|
||||
previousValue + stringDoubleToBitcoinAmount(
|
||||
element.cryptoAmount.replaceAll(',', '.')));
|
||||
credentialsAmount = outputs.fold(0, (acc, value) =>
|
||||
acc + value.formattedCryptoAmount);
|
||||
|
||||
if (credentialsAmount > allAmount) {
|
||||
if (allAmount - credentialsAmount < minAmount) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
amount = allAmount - credentialsAmount < minAmount
|
||||
? allAmount
|
||||
: credentialsAmount;
|
||||
amount = credentialsAmount;
|
||||
|
||||
fee = amount == allAmount
|
||||
? allAmountFee
|
||||
: calculateEstimatedFee(transactionCredentials.priority, amount,
|
||||
outputsCount: sendItemList.length + 1);
|
||||
fee = calculateEstimatedFee(transactionCredentials.priority, amount,
|
||||
outputsCount: outputs.length + 1);
|
||||
} else {
|
||||
final sendItem = sendItemList.first;
|
||||
final output = outputs.first;
|
||||
|
||||
credentialsAmount = !sendItem.sendAll
|
||||
? stringDoubleToBitcoinAmount(
|
||||
sendItem.cryptoAmount.replaceAll(',', '.'))
|
||||
credentialsAmount = !output.sendAll
|
||||
? output.formattedCryptoAmount
|
||||
: 0;
|
||||
|
||||
if (credentialsAmount > allAmount) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
amount = sendItem.sendAll || allAmount - credentialsAmount < minAmount
|
||||
amount = output.sendAll || allAmount - credentialsAmount < minAmount
|
||||
? allAmount
|
||||
: credentialsAmount;
|
||||
|
||||
fee = sendItem.sendAll || amount == allAmount
|
||||
fee = output.sendAll || amount == allAmount
|
||||
? allAmountFee
|
||||
: calculateEstimatedFee(transactionCredentials.priority, amount);
|
||||
}
|
||||
|
@ -289,18 +283,18 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
|
|||
}
|
||||
});
|
||||
|
||||
sendItemList.forEach((item) {
|
||||
final _amount = item.sendAll
|
||||
? amount
|
||||
: stringDoubleToBitcoinAmount(item.cryptoAmount.replaceAll(',', '.'));
|
||||
outputs.forEach((item) {
|
||||
final outputAmount = hasMultiDestination
|
||||
? item.formattedCryptoAmount
|
||||
: amount;
|
||||
|
||||
txb.addOutput(
|
||||
addressToOutputScript(item.address, networkType),
|
||||
_amount);
|
||||
outputAmount);
|
||||
});
|
||||
|
||||
final estimatedSize =
|
||||
estimatedTransactionSize(inputs.length, sendItemList.length + 1);
|
||||
estimatedTransactionSize(inputs.length, outputs.length + 1);
|
||||
final feeAmount = feeRate(transactionCredentials.priority) * estimatedSize;
|
||||
final changeValue = totalInputAmount - amount - feeAmount;
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:cake_wallet/entities/transaction_creation_credentials.dart';
|
||||
import 'package:cake_wallet/entities/monero_transaction_priority.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_item.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
|
||||
class MoneroTransactionCreationCredentials
|
||||
extends TransactionCreationCredentials {
|
||||
MoneroTransactionCreationCredentials({this.sendItemList, this.priority});
|
||||
MoneroTransactionCreationCredentials({this.outputs, this.priority});
|
||||
|
||||
final List<SendItem> sendItemList;
|
||||
final List<Output> outputs;
|
||||
final MoneroTransactionPriority priority;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:cw_monero/transaction_history.dart'
|
|||
import 'package:cw_monero/wallet.dart';
|
||||
import 'package:cw_monero/wallet.dart' as monero_wallet;
|
||||
import 'package:cw_monero/transaction_history.dart' as transaction_history;
|
||||
import 'package:cw_monero/monero_output.dart';
|
||||
import 'package:cake_wallet/monero/monero_transaction_creation_credentials.dart';
|
||||
import 'package:cake_wallet/monero/pending_monero_transaction.dart';
|
||||
import 'package:cake_wallet/monero/monero_wallet_keys.dart';
|
||||
|
@ -150,8 +151,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
final _credentials = credentials as MoneroTransactionCreationCredentials;
|
||||
final sendItemList = _credentials.sendItemList;
|
||||
final listSize = sendItemList.length;
|
||||
final outputs = _credentials.outputs;
|
||||
final hasMultiDestination = outputs.length > 1;
|
||||
final unlockedBalance =
|
||||
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id);
|
||||
|
||||
|
@ -161,16 +162,15 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
throw MoneroTransactionCreationException('The wallet is not synced.');
|
||||
}
|
||||
|
||||
if (listSize > 1) {
|
||||
final sendAllItems = sendItemList.where((item) => item.sendAll).toList();
|
||||
if (hasMultiDestination) {
|
||||
final sendAllItems = outputs.where((item) => item.sendAll).toList();
|
||||
|
||||
if (sendAllItems?.isNotEmpty ?? false) {
|
||||
throw MoneroTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
|
||||
}
|
||||
|
||||
final nullAmountItems = sendItemList.where((item) =>
|
||||
moneroParseAmount(amount: item.cryptoAmount.replaceAll(',', '.')) <= 0)
|
||||
.toList();
|
||||
final nullAmountItems = outputs.where((item) =>
|
||||
item.formattedCryptoAmount <= 0).toList();
|
||||
|
||||
if (nullAmountItems?.isNotEmpty ?? false) {
|
||||
throw MoneroTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
|
||||
|
@ -178,42 +178,41 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
|
||||
var credentialsAmount = 0;
|
||||
|
||||
credentialsAmount = sendItemList.fold(0, (previousValue, element) =>
|
||||
previousValue + moneroParseAmount(
|
||||
amount: element.cryptoAmount.replaceAll(',', '.')));
|
||||
credentialsAmount = outputs.fold(0, (acc, value) =>
|
||||
acc + value.formattedCryptoAmount);
|
||||
|
||||
if (unlockedBalance < credentialsAmount) {
|
||||
throw MoneroTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
|
||||
}
|
||||
|
||||
final addresses = sendItemList.map((e) => e.address).toList();
|
||||
final amounts = sendItemList.map((e) =>
|
||||
e.cryptoAmount.replaceAll(',', '.')).toList();
|
||||
final moneroOutputs = outputs.map((output) =>
|
||||
MoneroOutput(
|
||||
address: output.address,
|
||||
amount: output.cryptoAmount.replaceAll(',', '.')))
|
||||
.toList();
|
||||
|
||||
pendingTransactionDescription =
|
||||
await transaction_history.createTransactionMultDest(
|
||||
addresses: addresses,
|
||||
outputs: moneroOutputs,
|
||||
paymentId: '',
|
||||
amounts: amounts,
|
||||
size: listSize,
|
||||
priorityRaw: _credentials.priority.serialize(),
|
||||
accountIndex: walletAddresses.account.id);
|
||||
} else {
|
||||
final item = sendItemList.first;
|
||||
final address = item.address;
|
||||
final amount = item.sendAll
|
||||
final output = outputs.first;
|
||||
final address = output.address;
|
||||
final amount = output.sendAll
|
||||
? null
|
||||
: item.cryptoAmount.replaceAll(',', '.');
|
||||
final formattedAmount = item.sendAll
|
||||
: output.cryptoAmount.replaceAll(',', '.');
|
||||
final formattedAmount = output.sendAll
|
||||
? null
|
||||
: moneroParseAmount(amount: amount);
|
||||
: output.formattedCryptoAmount;
|
||||
|
||||
if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
|
||||
(formattedAmount == null && unlockedBalance <= 0)) {
|
||||
final formattedBalance = moneroAmountToString(amount: unlockedBalance);
|
||||
|
||||
throw MoneroTransactionCreationException(
|
||||
'Incorrect unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${item.cryptoAmount}.');
|
||||
'Incorrect unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
|
||||
}
|
||||
|
||||
pendingTransactionDescription =
|
||||
|
|
|
@ -385,8 +385,8 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
.pendingTransactionFiatAmount +
|
||||
' ' +
|
||||
widget.exchangeTradeViewModel.sendViewModel.fiat.title,
|
||||
sendItemList: widget.exchangeTradeViewModel.sendViewModel
|
||||
.sendItemList);
|
||||
outputs: widget.exchangeTradeViewModel.sendViewModel
|
||||
.outputs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_a
|
|||
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/template_tile.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_item.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
@ -55,16 +55,16 @@ class SendPage extends BasePage {
|
|||
onPressed: () {
|
||||
var pageToJump = controller.page.round() - 1;
|
||||
pageToJump = pageToJump > 0 ? pageToJump : 0;
|
||||
final item = _defineCurrentSendItem();
|
||||
sendViewModel.removeSendItem(item);
|
||||
final output = _defineCurrentOutput();
|
||||
sendViewModel.removeOutput(output);
|
||||
controller.jumpToPage(pageToJump);
|
||||
})
|
||||
: TrailButton(
|
||||
caption: S.of(context).clear,
|
||||
onPressed: () {
|
||||
final item = _defineCurrentSendItem();
|
||||
final output = _defineCurrentOutput();
|
||||
_formKey.currentState.reset();
|
||||
item.reset();
|
||||
output.reset();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -85,13 +85,13 @@ class SendPage extends BasePage {
|
|||
return PageView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
controller: controller,
|
||||
itemCount: sendViewModel.sendItemList.length,
|
||||
itemCount: sendViewModel.outputs.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = sendViewModel.sendItemList[index];
|
||||
final output = sendViewModel.outputs[index];
|
||||
|
||||
return SendCard(
|
||||
key: item.key,
|
||||
item: item,
|
||||
key: output.key,
|
||||
output: output,
|
||||
sendViewModel: sendViewModel,
|
||||
);
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ class SendPage extends BasePage {
|
|||
child: Container(
|
||||
height: 10,
|
||||
child: Observer(builder: (_) {
|
||||
final count = sendViewModel.sendItemList.length;
|
||||
final count = sendViewModel.outputs.length;
|
||||
|
||||
return count > 1
|
||||
? SmoothPageIndicator(
|
||||
|
@ -211,11 +211,11 @@ class SendPage extends BasePage {
|
|||
amount: template.amount,
|
||||
from: template.cryptoCurrency,
|
||||
onTap: () async {
|
||||
final item = _defineCurrentSendItem();
|
||||
item.address =
|
||||
final output = _defineCurrentOutput();
|
||||
output.address =
|
||||
template.address;
|
||||
item.setCryptoAmount(template.amount);
|
||||
final parsedAddress = await item
|
||||
output.setCryptoAmount(template.amount);
|
||||
final parsedAddress = await output
|
||||
.applyOpenaliasOrUnstoppableDomains();
|
||||
showAddressAlert(context, parsedAddress);
|
||||
},
|
||||
|
@ -263,7 +263,7 @@ class SendPage extends BasePage {
|
|||
padding: EdgeInsets.only(bottom: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
sendViewModel.addSendItem();
|
||||
sendViewModel.addOutput();
|
||||
},
|
||||
text: S.of(context).add_receiver,
|
||||
color: Colors.green,
|
||||
|
@ -274,16 +274,16 @@ class SendPage extends BasePage {
|
|||
return LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
if (sendViewModel.sendItemList.length > 1) {
|
||||
if (sendViewModel.outputs.length > 1) {
|
||||
showErrorValidationAlert(context);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final notValidItems = sendViewModel.sendItemList
|
||||
final notValidItems = sendViewModel.outputs
|
||||
.where((item) =>
|
||||
item.address.isEmpty || item.cryptoAmount.isEmpty)
|
||||
item.address.isEmpty || item.cryptoAmount.isEmpty)
|
||||
.toList();
|
||||
|
||||
if (notValidItems?.isNotEmpty ?? false) {
|
||||
|
@ -343,7 +343,7 @@ class SendPage extends BasePage {
|
|||
feeValue: sendViewModel.pendingTransaction.feeFormatted,
|
||||
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmount
|
||||
+ ' ' + sendViewModel.fiat.title,
|
||||
sendItemList: sendViewModel.sendItemList,
|
||||
outputs: sendViewModel.outputs,
|
||||
rightButtonText: S.of(context).ok,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () {
|
||||
|
@ -381,7 +381,7 @@ class SendPage extends BasePage {
|
|||
|
||||
if (state is TransactionCommitted) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
sendViewModel.clearSendItemList();
|
||||
sendViewModel.clearOutputs();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -389,9 +389,9 @@ class SendPage extends BasePage {
|
|||
_effectsInstalled = true;
|
||||
}
|
||||
|
||||
SendItem _defineCurrentSendItem() {
|
||||
Output _defineCurrentOutput() {
|
||||
final itemCount = controller.page.round();
|
||||
return sendViewModel.sendItemList[itemCount];
|
||||
return sendViewModel.outputs[itemCount];
|
||||
}
|
||||
|
||||
void showErrorValidationAlert(BuildContext context) async {
|
||||
|
|
|
@ -14,7 +14,7 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
|||
|
||||
class SendTemplatePage extends BasePage {
|
||||
SendTemplatePage({@required this.sendTemplateViewModel}) {
|
||||
sendTemplateViewModel.sendItem.reset();
|
||||
sendTemplateViewModel.output.reset();
|
||||
}
|
||||
|
||||
final SendTemplateViewModel sendTemplateViewModel;
|
||||
|
@ -258,21 +258,21 @@ class SendTemplatePage extends BasePage {
|
|||
return;
|
||||
}
|
||||
|
||||
final item = sendTemplateViewModel.sendItem;
|
||||
final output = sendTemplateViewModel.output;
|
||||
|
||||
reaction((_) => item.fiatAmount, (String amount) {
|
||||
reaction((_) => output.fiatAmount, (String amount) {
|
||||
if (amount != _fiatAmountController.text) {
|
||||
_fiatAmountController.text = amount;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => item.cryptoAmount, (String amount) {
|
||||
reaction((_) => output.cryptoAmount, (String amount) {
|
||||
if (amount != _cryptoAmountController.text) {
|
||||
_cryptoAmountController.text = amount;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => item.address, (String address) {
|
||||
reaction((_) => output.address, (String address) {
|
||||
if (address != _addressController.text) {
|
||||
_addressController.text = address;
|
||||
}
|
||||
|
@ -281,24 +281,24 @@ class SendTemplatePage extends BasePage {
|
|||
_cryptoAmountController.addListener(() {
|
||||
final amount = _cryptoAmountController.text;
|
||||
|
||||
if (amount != item.cryptoAmount) {
|
||||
item.setCryptoAmount(amount);
|
||||
if (amount != output.cryptoAmount) {
|
||||
output.setCryptoAmount(amount);
|
||||
}
|
||||
});
|
||||
|
||||
_fiatAmountController.addListener(() {
|
||||
final amount = _fiatAmountController.text;
|
||||
|
||||
if (amount != item.fiatAmount) {
|
||||
item.setFiatAmount(amount);
|
||||
if (amount != output.fiatAmount) {
|
||||
output.setFiatAmount(amount);
|
||||
}
|
||||
});
|
||||
|
||||
_addressController.addListener(() {
|
||||
final address = _addressController.text;
|
||||
|
||||
if (item.address != address) {
|
||||
item.address = address;
|
||||
if (output.address != address) {
|
||||
output.address = address;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_item.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
@ -13,14 +13,14 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
@required this.fee,
|
||||
@required this.feeValue,
|
||||
@required this.feeFiatAmount,
|
||||
@required this.sendItemList,
|
||||
@required this.outputs,
|
||||
@required this.leftButtonText,
|
||||
@required this.rightButtonText,
|
||||
@required this.actionLeftButton,
|
||||
@required this.actionRightButton,
|
||||
this.alertBarrierDismissible = true
|
||||
}) {
|
||||
itemCount = sendItemList.length;
|
||||
itemCount = outputs.length;
|
||||
recipientTitle = itemCount > 1
|
||||
? S.current.transaction_details_recipient_address
|
||||
: S.current.recipient_address;
|
||||
|
@ -33,7 +33,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
final String fee;
|
||||
final String feeValue;
|
||||
final String feeFiatAmount;
|
||||
final List<SendItem> sendItemList;
|
||||
final List<Output> outputs;
|
||||
final String leftButtonText;
|
||||
final String rightButtonText;
|
||||
final VoidCallback actionLeftButton;
|
||||
|
@ -179,10 +179,10 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
final item = sendItemList[index];
|
||||
final item = outputs[index];
|
||||
final _address = item.address;
|
||||
final _amount =
|
||||
item.cryptoAmount.replaceAll(',', '.');
|
||||
item.cryptoAmount.replaceAll(',', '.');
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
|
@ -225,7 +225,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
: Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
sendItemList.first.address,
|
||||
outputs.first.address,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:cake_wallet/routes.dart';
|
|||
import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_item.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -19,21 +19,21 @@ import 'package:cake_wallet/generated/i18n.dart';
|
|||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
|
||||
class SendCard extends StatefulWidget {
|
||||
SendCard({Key key, @required this.item, @required this.sendViewModel}) : super(key: key);
|
||||
SendCard({Key key, @required this.output, @required this.sendViewModel}) : super(key: key);
|
||||
|
||||
final SendItem item;
|
||||
final Output output;
|
||||
final SendViewModel sendViewModel;
|
||||
|
||||
@override
|
||||
SendCardState createState() => SendCardState(
|
||||
item: item,
|
||||
output: output,
|
||||
sendViewModel: sendViewModel
|
||||
);
|
||||
}
|
||||
|
||||
class SendCardState extends State<SendCard>
|
||||
with AutomaticKeepAliveClientMixin<SendCard> {
|
||||
SendCardState({@required this.item, @required this.sendViewModel})
|
||||
SendCardState({@required this.output, @required this.sendViewModel})
|
||||
: addressController = TextEditingController(),
|
||||
cryptoAmountController = TextEditingController(),
|
||||
fiatAmountController = TextEditingController(),
|
||||
|
@ -45,7 +45,7 @@ class SendCardState extends State<SendCard>
|
|||
static const prefixIconWidth = 34.0;
|
||||
static const prefixIconHeight = 34.0;
|
||||
|
||||
final SendItem item;
|
||||
final Output output;
|
||||
final SendViewModel sendViewModel;
|
||||
|
||||
final TextEditingController addressController;
|
||||
|
@ -143,7 +143,7 @@ class SendCardState extends State<SendCard>
|
|||
.decorationColor),
|
||||
onPushPasteButton: (context) async {
|
||||
final parsedAddress =
|
||||
await item.applyOpenaliasOrUnstoppableDomains();
|
||||
await output.applyOpenaliasOrUnstoppableDomains();
|
||||
showAddressAlert(context, parsedAddress);
|
||||
},
|
||||
validator: sendViewModel.addressValidator,
|
||||
|
@ -189,7 +189,7 @@ class SendCardState extends State<SendCard>
|
|||
.decorationColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14),
|
||||
validator: item.sendAll
|
||||
validator: output.sendAll
|
||||
? sendViewModel.allAmountValidator
|
||||
: sendViewModel
|
||||
.amountValidator),
|
||||
|
@ -201,7 +201,7 @@ class SendCardState extends State<SendCard>
|
|||
height: prefixIconHeight,
|
||||
child: InkWell(
|
||||
onTap: () async =>
|
||||
item.setSendAll(),
|
||||
output.setSendAll(),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
|
@ -349,7 +349,7 @@ class SendCardState extends State<SendCard>
|
|||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
item
|
||||
output
|
||||
.estimatedFee
|
||||
.toString() +
|
||||
' ' +
|
||||
|
@ -366,7 +366,7 @@ class SendCardState extends State<SendCard>
|
|||
padding:
|
||||
EdgeInsets.only(top: 5),
|
||||
child: Text(
|
||||
item
|
||||
output
|
||||
.estimatedFeeFiatAmount
|
||||
+ ' ' +
|
||||
sendViewModel
|
||||
|
@ -435,10 +435,10 @@ class SendCardState extends State<SendCard>
|
|||
}
|
||||
|
||||
void _setEffects(BuildContext context) {
|
||||
addressController.text = item.address;
|
||||
cryptoAmountController.text = item.cryptoAmount;
|
||||
fiatAmountController.text = item.fiatAmount;
|
||||
noteController.text = item.note;
|
||||
addressController.text = output.address;
|
||||
cryptoAmountController.text = output.cryptoAmount;
|
||||
fiatAmountController.text = output.fiatAmount;
|
||||
noteController.text = output.note;
|
||||
|
||||
if (_effectsInstalled) {
|
||||
return;
|
||||
|
@ -447,48 +447,48 @@ class SendCardState extends State<SendCard>
|
|||
cryptoAmountController.addListener(() {
|
||||
final amount = cryptoAmountController.text;
|
||||
|
||||
if (item.sendAll && amount != S.current.all) {
|
||||
item.sendAll = false;
|
||||
if (output.sendAll && amount != S.current.all) {
|
||||
output.sendAll = false;
|
||||
}
|
||||
|
||||
if (amount != item.cryptoAmount) {
|
||||
item.setCryptoAmount(amount);
|
||||
if (amount != output.cryptoAmount) {
|
||||
output.setCryptoAmount(amount);
|
||||
}
|
||||
});
|
||||
|
||||
fiatAmountController.addListener(() {
|
||||
final amount = fiatAmountController.text;
|
||||
|
||||
if (amount != item.fiatAmount) {
|
||||
item.sendAll = false;
|
||||
item.setFiatAmount(amount);
|
||||
if (amount != output.fiatAmount) {
|
||||
output.sendAll = false;
|
||||
output.setFiatAmount(amount);
|
||||
}
|
||||
});
|
||||
|
||||
noteController.addListener(() {
|
||||
final note = noteController.text ?? '';
|
||||
|
||||
if (note != item.note) {
|
||||
item.note = note;
|
||||
if (note != output.note) {
|
||||
output.note = note;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => item.sendAll, (bool all) {
|
||||
reaction((_) => output.sendAll, (bool all) {
|
||||
if (all) {
|
||||
cryptoAmountController.text = S.current.all;
|
||||
fiatAmountController.text = null;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => item.fiatAmount, (String amount) {
|
||||
reaction((_) => output.fiatAmount, (String amount) {
|
||||
if (amount != fiatAmountController.text) {
|
||||
fiatAmountController.text = amount;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => item.cryptoAmount, (String amount) {
|
||||
if (item.sendAll && amount != S.current.all) {
|
||||
item.sendAll = false;
|
||||
reaction((_) => output.cryptoAmount, (String amount) {
|
||||
if (output.sendAll && amount != S.current.all) {
|
||||
output.sendAll = false;
|
||||
}
|
||||
|
||||
if (amount != cryptoAmountController.text) {
|
||||
|
@ -496,7 +496,7 @@ class SendCardState extends State<SendCard>
|
|||
}
|
||||
});
|
||||
|
||||
reaction((_) => item.address, (String address) {
|
||||
reaction((_) => output.address, (String address) {
|
||||
if (address != addressController.text) {
|
||||
addressController.text = address;
|
||||
}
|
||||
|
@ -505,12 +505,12 @@ class SendCardState extends State<SendCard>
|
|||
addressController.addListener(() {
|
||||
final address = addressController.text;
|
||||
|
||||
if (item.address != address) {
|
||||
item.address = address;
|
||||
if (output.address != address) {
|
||||
output.address = address;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => item.note, (String note) {
|
||||
reaction((_) => output.note, (String note) {
|
||||
if (note != noteController.text) {
|
||||
noteController.text = note;
|
||||
}
|
||||
|
@ -518,7 +518,7 @@ class SendCardState extends State<SendCard>
|
|||
|
||||
addressFocusNode.addListener(() async {
|
||||
if (!addressFocusNode.hasFocus && addressController.text.isNotEmpty) {
|
||||
final parsedAddress = await item.applyOpenaliasOrUnstoppableDomains();
|
||||
final parsedAddress = await output.applyOpenaliasOrUnstoppableDomains();
|
||||
showAddressAlert(context, parsedAddress);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -79,11 +79,11 @@ abstract class ExchangeTradeViewModelBase with Store {
|
|||
return;
|
||||
}
|
||||
|
||||
sendViewModel.clearSendItemList();
|
||||
final item = sendViewModel.sendItemList.first;
|
||||
sendViewModel.clearOutputs();
|
||||
final output = sendViewModel.outputs.first;
|
||||
|
||||
item.address = trade.inputAddress;
|
||||
item.setCryptoAmount(trade.amount);
|
||||
output.address = trade.inputAddress;
|
||||
output.setCryptoAmount(trade.amount);
|
||||
await sendViewModel.createTransaction();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,14 @@ import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
|||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
part 'send_item.g.dart';
|
||||
part 'output.g.dart';
|
||||
|
||||
const String cryptoNumberPattern = '0.0';
|
||||
|
||||
class SendItem = SendItemBase with _$SendItem;
|
||||
class Output = OutputBase with _$Output;
|
||||
|
||||
abstract class SendItemBase with Store {
|
||||
SendItemBase(this._wallet, this._settingsStore, this._fiatConversationStore)
|
||||
abstract class OutputBase with Store {
|
||||
OutputBase(this._wallet, this._settingsStore, this._fiatConversationStore)
|
||||
:_cryptoNumberFormat = NumberFormat(cryptoNumberPattern) {
|
||||
reset();
|
||||
_setCryptoNumMaximumFractionDigits();
|
||||
|
@ -47,8 +47,8 @@ abstract class SendItemBase with Store {
|
|||
bool sendAll;
|
||||
|
||||
@computed
|
||||
double get estimatedFee {
|
||||
int amount;
|
||||
int get formattedCryptoAmount {
|
||||
int amount = 0;
|
||||
|
||||
try {
|
||||
if (cryptoAmount?.isNotEmpty ?? false) {
|
||||
|
@ -72,9 +72,18 @@ abstract class SendItemBase with Store {
|
|||
amount = _amount;
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
amount = 0;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
@computed
|
||||
double get estimatedFee {
|
||||
try {
|
||||
final fee = _wallet.calculateEstimatedFee(
|
||||
_settingsStore.priority[_wallet.type], amount);
|
||||
_settingsStore.priority[_wallet.type], formattedCryptoAmount);
|
||||
|
||||
if (_wallet is ElectrumWallet) {
|
||||
return bitcoinAmountToDouble(amount: fee);
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:cake_wallet/view_model/send/send_item.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/entities/template.dart';
|
||||
import 'package:cake_wallet/store/templates/send_template_store.dart';
|
||||
|
@ -21,10 +21,10 @@ abstract class SendTemplateViewModelBase with Store {
|
|||
SendTemplateViewModelBase(this._wallet, this._settingsStore,
|
||||
this._sendTemplateStore, this._fiatConversationStore) {
|
||||
|
||||
sendItem = SendItem(_wallet, _settingsStore, _fiatConversationStore);
|
||||
output = Output(_wallet, _settingsStore, _fiatConversationStore);
|
||||
}
|
||||
|
||||
SendItem sendItem;
|
||||
Output output;
|
||||
|
||||
Validator get amountValidator => AmountValidator(type: _wallet.type);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
|
|||
import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/entities/transaction_priority.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_item.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
@ -42,33 +42,33 @@ abstract class SendViewModelBase with Store {
|
|||
_settingsStore.priority[_wallet.type] = priorities.first;
|
||||
}
|
||||
|
||||
sendItemList = ObservableList<SendItem>()
|
||||
..add(SendItem(_wallet, _settingsStore, _fiatConversationStore));
|
||||
outputs = ObservableList<Output>()
|
||||
..add(Output(_wallet, _settingsStore, _fiatConversationStore));
|
||||
}
|
||||
|
||||
@observable
|
||||
ExecutionState state;
|
||||
|
||||
ObservableList<SendItem> sendItemList;
|
||||
ObservableList<Output> outputs;
|
||||
|
||||
@action
|
||||
void addSendItem() {
|
||||
sendItemList.add(SendItem(_wallet, _settingsStore, _fiatConversationStore));
|
||||
void addOutput() {
|
||||
outputs.add(Output(_wallet, _settingsStore, _fiatConversationStore));
|
||||
}
|
||||
|
||||
@action
|
||||
void removeSendItem(SendItem item) {
|
||||
sendItemList.remove(item);
|
||||
void removeOutput(Output output) {
|
||||
outputs.remove(output);
|
||||
}
|
||||
|
||||
@action
|
||||
void clearSendItemList() {
|
||||
sendItemList.clear();
|
||||
addSendItem();
|
||||
void clearOutputs() {
|
||||
outputs.clear();
|
||||
addOutput();
|
||||
}
|
||||
|
||||
@computed
|
||||
bool get isBatchSending => sendItemList.length > 1;
|
||||
bool get isBatchSending => outputs.length > 1;
|
||||
|
||||
@computed
|
||||
String get pendingTransactionFiatAmount {
|
||||
|
@ -150,14 +150,14 @@ abstract class SendViewModelBase with Store {
|
|||
|
||||
@action
|
||||
Future<void> commitTransaction() async {
|
||||
String address = sendItemList.fold('', (previousValue, item) {
|
||||
return previousValue + item.address + '\n';
|
||||
String address = outputs.fold('', (acc, value) {
|
||||
return acc + value.address + '\n';
|
||||
});
|
||||
|
||||
address = address.trim();
|
||||
|
||||
String note = sendItemList.fold('', (previousValue, item) {
|
||||
return previousValue + item.note + '\n';
|
||||
String note = outputs.fold('', (acc, value) {
|
||||
return acc + value.note + '\n';
|
||||
});
|
||||
|
||||
note = note.trim();
|
||||
|
@ -192,17 +192,17 @@ abstract class SendViewModelBase with Store {
|
|||
final priority = _settingsStore.priority[_wallet.type];
|
||||
|
||||
return BitcoinTransactionCredentials(
|
||||
sendItemList, priority as BitcoinTransactionPriority);
|
||||
outputs, priority as BitcoinTransactionPriority);
|
||||
case WalletType.litecoin:
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
|
||||
return BitcoinTransactionCredentials(
|
||||
sendItemList, priority as BitcoinTransactionPriority);
|
||||
outputs, priority as BitcoinTransactionPriority);
|
||||
case WalletType.monero:
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
|
||||
return MoneroTransactionCreationCredentials(
|
||||
sendItemList: sendItemList,
|
||||
outputs: outputs,
|
||||
priority: priority as MoneroTransactionPriority);
|
||||
default:
|
||||
return null;
|
||||
|
|
Loading…
Reference in a new issue