GUI: Support for sending assets

This commit is contained in:
Luke Dashjr 2019-01-08 14:18:10 +00:00 committed by Gregory Sanders
parent f4d5552f6c
commit 79e0cb74ff
7 changed files with 65 additions and 42 deletions

View file

@ -799,7 +799,9 @@ QString formatAssetAmount(const CAsset& asset, const CAmount& amount, const int
QString formatMultiAssetAmount(const CAmountMap& amountmap, const int bitcoin_unit, BitcoinUnits::SeparatorStyle separators, QString line_separator)
{
QStringList ret;
ret << formatAssetAmount(Params().GetConsensus().pegged_asset, amountmap.count(Params().GetConsensus().pegged_asset) ? amountmap.at(Params().GetConsensus().pegged_asset) : 0, bitcoin_unit, separators);
if (bitcoin_unit >= 0) {
ret << formatAssetAmount(Params().GetConsensus().pegged_asset, amountmap.count(Params().GetConsensus().pegged_asset) ? amountmap.at(Params().GetConsensus().pegged_asset) : 0, bitcoin_unit, separators);
}
for (const auto& assetamount : amountmap) {
if (assetamount.first == Params().GetConsensus().pegged_asset) {
// Already handled first

View file

@ -275,11 +275,12 @@ void SendCoinsDialog::on_sendButton_clicked()
CAmount txFee = currentTransaction.getTransactionFee();
// Format confirmation message
int bitcoin_unit = model->getOptionsModel()->getDisplayUnit();
QStringList formatted;
for (const SendAssetsRecipient &rcp : currentTransaction.getRecipients())
{
// generate bold amount string with wallet name in case of multiwallet
QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
QString amount = "<b>" + GUIUtil::formatAssetAmount(rcp.asset, rcp.asset_amount, bitcoin_unit, BitcoinUnits::separatorStandard, true);
if (model->isMultiwallet()) {
amount.append(" <u>"+tr("from wallet %1").arg(GUIUtil::HtmlEscape(model->getWalletName()))+"</u> ");
}
@ -347,17 +348,22 @@ void SendCoinsDialog::on_sendButton_clicked()
// add total amount in all subdivision units
questionString.append("<hr />");
CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
CAmountMap totalAmount = currentTransaction.getTotalTransactionAmount();
totalAmount[Params().GetConsensus().pegged_asset] += txFee;
QStringList alternativeUnits;
for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
{
if(u != model->getOptionsModel()->getDisplayUnit())
alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount[Params().GetConsensus().pegged_asset]));
}
questionString.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
.arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)));
.arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount[Params().GetConsensus().pegged_asset])));
questionString.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
.arg(alternativeUnits.join(" " + tr("or") + " ")));
totalAmount.erase(Params().GetConsensus().pegged_asset);
if (!!totalAmount) {
questionString.append(" " + tr("and") + "<br />" + GUIUtil::formatMultiAssetAmount(totalAmount, -1 /*bitcoin unit, hide*/, BitcoinUnits::separatorStandard, ";<br />"));
}
SendConfirmationDialog confirmationDialog(tr("Confirm send coins"),
questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this);
@ -623,7 +629,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
if (e && !e->isHidden() && e != entry) {
amount -= e->getValue().amount;
amount -= e->getValue().asset_amount;
}
}
@ -871,8 +877,10 @@ void SendCoinsDialog::coinControlUpdateLabels()
SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
if(entry && !entry->isHidden())
{
auto rcp = entry->getValue();
CoinControlDialog::payAmounts.append(rcp.amount);
SendAssetsRecipient rcp = entry->getValue();
if (rcp.asset == Params().GetConsensus().pegged_asset) {
CoinControlDialog::payAmounts.append(rcp.asset_amount);
}
if (rcp.fSubtractFeeFromAmount)
CoinControlDialog::fSubtractFeeFromAmount = true;
}

View file

@ -14,6 +14,8 @@
#include <QApplication>
#include <QClipboard>
#include <policy/policy.h>
SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *parent) :
QStackedWidget(parent),
ui(new Ui::SendCoinsEntry),
@ -152,14 +154,15 @@ bool SendCoinsEntry::validate(interfaces::Node& node)
}
// Sending a zero amount is invalid
if (ui->payAmount->value(0) <= 0)
const auto send_assets = ui->payAmount->fullValue();
if (send_assets.second <= 0)
{
ui->payAmount->setValid(false);
retval = false;
}
// Reject dust outputs:
if (retval && GUIUtil::isDust(node, ui->payTo->text(), ui->payAmount->value())) {
if (retval && send_assets.first == ::policyAsset && GUIUtil::isDust(node, ui->payTo->text(), ui->payAmount->value())) {
ui->payAmount->setValid(false);
retval = false;
}
@ -176,7 +179,7 @@ SendAssetsRecipient SendCoinsEntry::getValue()
// Normal payment
recipient.address = ui->payTo->text();
recipient.label = ui->addAsLabel->text();
recipient.amount = ui->payAmount->value();
std::tie(recipient.asset, recipient.asset_amount) = ui->payAmount->fullValue();
recipient.message = ui->messageTextLabel->text();
recipient.fSubtractFeeFromAmount = (ui->checkboxSubtractFeeFromAmount->checkState() == Qt::Checked);
@ -205,7 +208,7 @@ void SendCoinsEntry::setValue(const SendAssetsRecipient &value)
{
ui->payTo_is->setText(recipient.address);
ui->memoTextLabel_is->setText(recipient.message);
ui->payAmount_is->setValue(recipient.amount);
ui->payAmount_is->setFullValue(recipient.asset, recipient.asset_amount);
ui->payAmount_is->setReadOnly(true);
setCurrentWidget(ui->SendCoins_UnauthenticatedPaymentRequest);
}
@ -213,7 +216,7 @@ void SendCoinsEntry::setValue(const SendAssetsRecipient &value)
{
ui->payTo_s->setText(recipient.authenticatedMerchant);
ui->memoTextLabel_s->setText(recipient.message);
ui->payAmount_s->setValue(recipient.amount);
ui->payAmount_s->setFullValue(recipient.asset, recipient.asset_amount);
ui->payAmount_s->setReadOnly(true);
setCurrentWidget(ui->SendCoins_AuthenticatedPaymentRequest);
}
@ -229,7 +232,7 @@ void SendCoinsEntry::setValue(const SendAssetsRecipient &value)
ui->payTo->setText(recipient.address); // this may set a label from addressbook
if (!recipient.label.isEmpty()) // if a label had been set from the addressbook, don't overwrite with an empty label
ui->addAsLabel->setText(recipient.label);
ui->payAmount->setValue(recipient.amount);
ui->payAmount->setFullValue(recipient.asset, recipient.asset_amount);
}
}

View file

@ -20,6 +20,7 @@
#include <util.h> // for GetBoolArg
#include <wallet/coincontrol.h>
#include <wallet/wallet.h>
#include <rpc/util.h>
#include <stdint.h>
@ -31,7 +32,8 @@
SendAssetsRecipient::SendAssetsRecipient(SendCoinsRecipient r) :
address(r.address),
label(r.label),
amount(r.amount),
asset(Params().GetConsensus().pegged_asset),
asset_amount(r.amount),
message(r.message),
paymentRequest(r.paymentRequest),
authenticatedMerchant(r.authenticatedMerchant),
@ -39,6 +41,8 @@ SendAssetsRecipient::SendAssetsRecipient(SendCoinsRecipient r) :
{
}
#define SendCoinsRecipient SendAssetsRecipient
WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, interfaces::Node& node, const PlatformStyle *platformStyle, OptionsModel *_optionsModel, QObject *parent) :
QObject(parent), m_wallet(std::move(wallet)), m_node(node), optionsModel(_optionsModel), addressTableModel(0),
transactionTableModel(0),
@ -149,9 +153,9 @@ bool WalletModel::validateAddress(const QString &address)
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
{
CAmount total = 0;
CAmountMap total;
bool fSubtractFeeFromAmount = false;
auto recipients = transaction.getRecipients();
QList<SendCoinsRecipient> recipients = transaction.getRecipients();
std::vector<CRecipient> vecSend;
if(recipients.empty())
@ -163,7 +167,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
int nAddresses = 0;
// Pre-check input data for validity
for (const SendAssetsRecipient &rcp : recipients)
for (const SendCoinsRecipient &rcp : recipients)
{
if (rcp.fSubtractFeeFromAmount)
fSubtractFeeFromAmount = true;
@ -187,7 +191,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
{
return InvalidAmount;
}
total += subtotal;
total[Params().GetConsensus().pegged_asset] += subtotal;
}
else
{ // User-entered bitcoin address / amount:
@ -195,18 +199,20 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
{
return InvalidAddress;
}
if(rcp.amount <= 0)
if(rcp.asset_amount <= 0)
{
return InvalidAmount;
}
setAddress.insert(rcp.address);
++nAddresses;
CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString()));
CRecipient recipient = {scriptPubKey, rcp.amount, ::policyAsset, CPubKey(), rcp.fSubtractFeeFromAmount};
CTxDestination dest = DecodeDestination(rcp.address.toStdString());
CScript scriptPubKey = GetScriptForDestination(dest);
CPubKey confidentiality_pubkey = GetDestinationBlindingKey(dest);
CRecipient recipient = {scriptPubKey, rcp.asset_amount, rcp.asset, confidentiality_pubkey, rcp.fSubtractFeeFromAmount};
vecSend.push_back(recipient);
total += rcp.amount;
total[rcp.asset] += rcp.asset_amount;
}
}
if(setAddress.size() != nAddresses)
@ -214,7 +220,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
return DuplicateAddress;
}
CAmount nBalance = m_wallet->getAvailableBalance(coinControl)[::policyAsset];
CAmountMap nBalance = m_wallet->getAvailableBalance(coinControl);
if(total > nBalance)
{
@ -227,14 +233,16 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
std::string strFailReason;
auto& newTx = transaction.getWtx();
std::vector<CAmount> out_amounts;
newTx = m_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason);
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && newTx)
transaction.reassignAmounts(nChangePosRet);
transaction.reassignAmounts(out_amounts, nChangePosRet);
if(!newTx)
{
if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance)
total[Params().GetConsensus().pegged_asset] += nFeeRequired;
if(!fSubtractFeeFromAmount && total > nBalance)
{
return SendCoinsReturn(AmountWithFeeExceedsBalance);
}
@ -259,7 +267,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
{
std::vector<std::pair<std::string, std::string>> vOrderForm;
for (const SendAssetsRecipient &rcp : transaction.getRecipients())
for (const SendCoinsRecipient &rcp : transaction.getRecipients())
{
if (rcp.paymentRequest.IsInitialized())
{
@ -289,7 +297,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
// Add addresses / update labels that we've sent to the address book,
// and emit coinsSent signal for each recipient
for (const SendAssetsRecipient &rcp : transaction.getRecipients())
for (const SendCoinsRecipient &rcp : transaction.getRecipients())
{
// Don't touch the address book when we have a payment request
if (!rcp.paymentRequest.IsInitialized())

View file

@ -113,7 +113,8 @@ public:
public:
QString address;
QString label;
CAmount amount;
CAsset asset;
CAmount asset_amount;
QString message;
PaymentRequestPlus paymentRequest;

View file

@ -7,13 +7,15 @@
#include <interfaces/node.h>
#include <policy/policy.h>
WalletModelTransaction::WalletModelTransaction(const QList<SendAssetsRecipient> &_recipients) :
#define SendCoinsRecipient SendAssetsRecipient
WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &_recipients) :
recipients(_recipients),
fee(0)
{
}
QList<SendAssetsRecipient> WalletModelTransaction::getRecipients() const
QList<SendCoinsRecipient> WalletModelTransaction::getRecipients() const
{
return recipients;
}
@ -38,9 +40,8 @@ void WalletModelTransaction::setTransactionFee(const CAmount& newFee)
fee = newFee;
}
void WalletModelTransaction::reassignAmounts(int nChangePosRet)
void WalletModelTransaction::reassignAmounts(const std::vector<CAmount>& outAmounts, int nChangePosRet)
{
const CTransaction* walletTransaction = &wtx->get();
int i = 0;
for (auto it = recipients.begin(); it != recipients.end(); ++it)
{
@ -56,27 +57,27 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
if (out.amount() <= 0) continue;
if (i == nChangePosRet)
i++;
subtotal += walletTransaction->vout[i].nValue.GetAmount();
subtotal += outAmounts[i];
i++;
}
rcp.amount = subtotal;
rcp.asset_amount = subtotal;
}
else // normal recipient (no payment request)
{
if (i == nChangePosRet)
i++;
rcp.amount = walletTransaction->vout[i].nValue.GetAmount();
rcp.asset_amount = outAmounts[i];
i++;
}
}
}
CAmount WalletModelTransaction::getTotalTransactionAmount() const
CAmountMap WalletModelTransaction::getTotalTransactionAmount() const
{
CAmount totalTransactionAmount = 0;
for (const SendAssetsRecipient &rcp : recipients)
CAmountMap totalTransactionAmount;
for (const auto &rcp : recipients)
{
totalTransactionAmount += rcp.amount;
totalTransactionAmount[rcp.asset] += rcp.asset_amount;
}
return totalTransactionAmount;
}

View file

@ -32,9 +32,9 @@ public:
void setTransactionFee(const CAmount& newFee);
CAmount getTransactionFee() const;
CAmount getTotalTransactionAmount() const;
CAmountMap getTotalTransactionAmount() const;
void reassignAmounts(int nChangePosRet); // needed for the subtract-fee-from-amount feature
void reassignAmounts(const std::vector<CAmount>& out_amounts, int nChangePosRet); // needed for the subtract-fee-from-amount feature
private:
QList<SendAssetsRecipient> recipients;