mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
GUI: Support for sending assets
This commit is contained in:
parent
f4d5552f6c
commit
79e0cb74ff
7 changed files with 65 additions and 42 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@ public:
|
|||
public:
|
||||
QString address;
|
||||
QString label;
|
||||
CAmount amount;
|
||||
CAsset asset;
|
||||
CAmount asset_amount;
|
||||
QString message;
|
||||
|
||||
PaymentRequestPlus paymentRequest;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue