diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 965657edb9..11a74f1155 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -238,6 +238,48 @@ + + + + 0 + + + + + + + + + + + 5 + + + 5 + + + 10 + + + 10 + + + margin-left:-30px;margin-right:-10px;margin-top:2px; + + + + + + + + + + color: #FFA800; margin-left:-10px; + + + + + diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index e9706fd9fc..fd3d658217 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -533,11 +533,16 @@ void SendCoinsDialog::on_sendButton_clicked() QString questionString = tr("Are you sure you want to send?"); questionString.append(warningMessage); questionString.append("

%1"); - questionString.append("\n\nMessage: "); - for (auto rec : recipients) - { - questionString.append(rec.message); - questionString.append(".\t"); + bool firstMessage = true; + for (const auto& rec : recipients) { + if (!rec.message.isEmpty()) { + if (firstMessage) { + questionString.append("
" + tr("Messages") + ":
"); + firstMessage = false; + } + QString sanitizedMsg = GUIUtil::HtmlEscape(rec.message, true); + questionString.append("• " + sanitizedMsg + "
"); + } } double txSize; diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index c762262041..4697423868 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -29,6 +29,7 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par QIcon icon_; icon_.addFile(QString::fromUtf8(":/icons/ic_warning"), QSize(), QIcon::Normal, QIcon::On); ui->iconWarning->setPixmap(icon_.pixmap(18, 18)); + ui->iconMessageWarning->setPixmap(icon_.pixmap(18, 18)); ui->addressBookButton->setIcon(platformStyle->SingleColorIcon(":/icons/address-book")); ui->pasteButton->setIcon(platformStyle->SingleColorIcon(":/icons/editpaste")); @@ -55,9 +56,11 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par connect(ui->deleteButton, &QToolButton::clicked, this, &SendCoinsEntry::deleteClicked); connect(ui->deleteButton_is, &QToolButton::clicked, this, &SendCoinsEntry::deleteClicked); connect(ui->deleteButton_s, &QToolButton::clicked, this, &SendCoinsEntry::deleteClicked); + connect(ui->messageTextLabel, &QLineEdit::textChanged, this, &SendCoinsEntry::on_MemoTextChanged); ui->messageLabel->setVisible(false); ui->messageTextLabel->setVisible(false); + ui->iconMessageWarning->setVisible(false); } SendCoinsEntry::~SendCoinsEntry() @@ -65,6 +68,24 @@ SendCoinsEntry::~SendCoinsEntry() delete ui; } +void SendCoinsEntry::on_MemoTextChanged(const QString &text) +{ + int maxLength = 256; + bool isOverLimit = text.length() > maxLength; + + if (isOverLimit) { + ui->messageWarning->setText("Message exceeds character 256 character limit"); + ui->messageWarning->setVisible(true); + ui->messageTextLabel->setStyleSheet("border: 1px solid red;"); + ui->iconMessageWarning->setVisible(true); + } else { + ui->messageWarning->clear(); + ui->messageWarning->setVisible(false); + ui->messageTextLabel->setStyleSheet(""); + ui->iconMessageWarning->setVisible(false); + } +} + void SendCoinsEntry::on_pasteButton_clicked() { // Paste text from clipboard into recipient field @@ -89,7 +110,10 @@ void SendCoinsEntry::on_payTo_textChanged(const QString &address) updateLabel(address); setWarning(fAnonymousMode); - bool isSparkAddress = model && model->validateSparkAddress(address); + bool isSparkAddress = false; + if (model) { + isSparkAddress = model->validateSparkAddress(address); + } ui->messageLabel->setVisible(isSparkAddress); ui->messageTextLabel->setVisible(isSparkAddress); } diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 2e89f0fd5e..4611bd0027 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -63,6 +63,7 @@ public Q_SLOTS: private Q_SLOTS: void deleteClicked(); void on_payTo_textChanged(const QString &address); + void on_MemoTextChanged(const QString &text); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); void updateDisplayUnit(); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 85d04babc4..deaabaf85f 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -311,12 +311,42 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "" + tr("Transaction total size") + ": " + QString::number(wtx.tx->GetTotalSize()) + " bytes
"; strHTML += "" + tr("Output index") + ": " + QString::number(rec->getOutputIndex()) + "
"; - uint256 selectedTxID = rec->hash; - std::unordered_map coins = wallet->sparkWallet->getMintMap(); + isminetype fAllFromMe = ISMINE_SPENDABLE; + bool foundSparkOutput = false; - for (const auto& [id, meta] : coins) { - if (meta.txid == selectedTxID && !meta.memo.empty()) { - strHTML += "" + tr("Message") + ": " + GUIUtil::HtmlEscape(meta.memo, true) + "
\n"; + for (const CTxIn& txin : wtx.tx->vin) { + isminetype mine = wallet->IsMine(txin, *wtx.tx); + fAllFromMe = std::min(fAllFromMe, mine); + } + + bool firstMessage = true; + if (fAllFromMe) { + for (const CTxOut& txout : wtx.tx->vout) { + if (wtx.IsChange(txout)) continue; + + CSparkOutputTx sparkOutput; + if (wallet->GetSparkOutputTx(txout.scriptPubKey, sparkOutput)) { + if (!sparkOutput.memo.empty()) { + foundSparkOutput = true; + if (firstMessage) { + strHTML += "
" + tr("Messages") + ":
"; + firstMessage = false; + } + strHTML += "• " + GUIUtil::HtmlEscape(sparkOutput.memo, true) + "
"; + } + } + } + } + + if (!foundSparkOutput && wallet->sparkWallet) { + for (const auto& [id, meta] : wallet->sparkWallet->getMintMap()) { + if (meta.txid == rec->hash && !meta.memo.empty()) { + if (firstMessage) { + strHTML += "
" + tr("Messages") + ":
"; + firstMessage = false; + } + strHTML += "• " + GUIUtil::HtmlEscape(meta.memo, true) + "
"; + } } } diff --git a/src/spark/primitives.h b/src/spark/primitives.h index 7929e73b80..09de4550eb 100644 --- a/src/spark/primitives.h +++ b/src/spark/primitives.h @@ -87,6 +87,7 @@ class CSparkOutputTx { public: std::string address; + std::string memo; int64_t amount; CSparkOutputTx() @@ -97,6 +98,7 @@ class CSparkOutputTx void SetNull() { address = ""; + memo = ""; amount = 0; } @@ -105,6 +107,7 @@ class CSparkOutputTx inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(address); READWRITE(amount); + READWRITE(memo); } }; diff --git a/src/spark/sparkwallet.cpp b/src/spark/sparkwallet.cpp index 382cb6c62d..d99a3f4810 100644 --- a/src/spark/sparkwallet.cpp +++ b/src/spark/sparkwallet.cpp @@ -742,7 +742,8 @@ std::vector CSparkWallet::CreateSparkMintRecipients( script.insert(script.end(), serializedCoins[i].begin(), serializedCoins[i].end()); unsigned char network = spark::GetNetworkType(); std::string addr = outputs[i].address.encode(network); - CRecipient recipient = {script, CAmount(outputs[i].v), false, addr}; + std::string memo = outputs[i].memo; + CRecipient recipient = {script, CAmount(outputs[i].v), false, addr, memo}; results.emplace_back(recipient); } @@ -1093,6 +1094,7 @@ bool CSparkWallet::CreateSparkMintTransactions( CSparkOutputTx output; output.address = recipient.address; output.amount = recipient.nAmount; + output.memo = recipient.memo; walletdb.WriteSparkOutputTx(recipient.scriptPubKey, output); break; } @@ -1530,6 +1532,7 @@ CWalletTx CSparkWallet::CreateSparkSpendTransaction( CSparkOutputTx output; output.address = privOutputs[i].address.encode(network); output.amount = privOutputs[i].v; + output.memo = privOutputs[i].memo; walletdb.WriteSparkOutputTx(script, output); tx.vout.push_back(CTxOut(0, script)); i++; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f9397f5150..f1f1d0c6a9 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -190,6 +190,7 @@ struct CRecipient CAmount nAmount; bool fSubtractFeeFromAmount; std::string address; + std::string memo; }; typedef std::map mapValue_t;