Files
wowlet/src/dialog/TxConfAdvDialog.cpp
T

261 lines
9.0 KiB
C++

// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: The Monero Project
#include "TxConfAdvDialog.h"
#include "ui_TxConfAdvDialog.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QTreeWidgetItem>
#include "constants.h"
#include "dialog/QrCodeDialog.h"
#include "libwalletqt/rows/Input.h"
#include "libwalletqt/rows/Output.h"
#include "libwalletqt/WalletManager.h"
#include "qrcode/QrCode.h"
#include "utils/AppData.h"
#include "utils/ColorScheme.h"
#include "utils/config.h"
#include "utils/Utils.h"
TxConfAdvDialog::TxConfAdvDialog(Wallet *wallet, const QString &description, QWidget *parent, bool offline)
: WindowModalDialog(parent)
, ui(new Ui::TxConfAdvDialog)
, m_wallet(wallet)
, m_exportSignedMenu(new QMenu(this))
, m_exportTxKeyMenu(new QMenu(this))
, m_offline(offline)
, m_description(description)
{
ui->setupUi(this);
m_exportSignedMenu->addAction("Copy to clipboard", this, &TxConfAdvDialog::signedCopy);
m_exportSignedMenu->addAction("Save to file", this, &TxConfAdvDialog::signedSaveFile);
ui->btn_exportSigned->setMenu(m_exportSignedMenu);
m_exportTxKeyMenu->addAction("Copy to clipboard", this, &TxConfAdvDialog::txKeyCopy);
ui->btn_exportTxKey->setMenu(m_exportTxKeyMenu);
connect(ui->btn_sign, &QPushButton::clicked, this, &TxConfAdvDialog::signTransaction);
connect(ui->btn_send, &QPushButton::clicked, this, &TxConfAdvDialog::broadcastTransaction);
connect(ui->btn_close, &QPushButton::clicked, this, &TxConfAdvDialog::closeDialog);
ui->amount->setFont(Utils::getMonospaceFont());
ui->fee->setFont(Utils::getMonospaceFont());
ui->total->setFont(Utils::getMonospaceFont());
if (m_offline) {
ui->txid->hide();
ui->label_txid->hide();
}
ui->treeInputs->setContextMenuPolicy(Qt::CustomContextMenu);
ui->treeOutputs->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->treeInputs, &QTreeView::customContextMenuRequested, [this](const QPoint &point){
this->setupContextMenu(point, ui->treeInputs);
});
connect(ui->treeOutputs, &QTreeView::customContextMenuRequested, [this](const QPoint &point){
this->setupContextMenu(point, ui->treeOutputs);
});
this->adjustSize();
}
void TxConfAdvDialog::setTransaction(PendingTransaction *tx, bool isSigned) {
ui->btn_sign->hide();
if (!isSigned) {
ui->btn_exportSigned->hide();
ui->btn_send->hide();
}
m_tx = tx;
m_tx->refresh();
const PendingTransactionInfo& ptx = m_tx->transaction(0); //Todo: support split transactions
// TODO: implement hasTxKey()
if (!m_wallet->isHwBacked() && m_tx->transaction(0).txKey == "0100000000000000000000000000000000000000000000000000000000000000") {
ui->btn_exportTxKey->hide();
}
m_txid = tx->txid().first();
ui->txid->setText(m_txid);
this->setAmounts(tx->amount(), tx->fee());
this->setupConstructionData(ptx);
}
void TxConfAdvDialog::setUnsignedTransaction(UnsignedTransaction *utx) {
m_utx = utx;
m_utx->refresh();
ui->btn_exportSigned->hide();
ui->btn_exportTxKey->hide();
ui->btn_sign->show();
ui->btn_send->hide();
ui->txid->setText("n/a");
this->setAmounts(utx->amount(0), utx->fee(0));
const ConstructionInfo& ci = m_utx->constructionInfo(0);
this->setupConstructionData(ci);
}
void TxConfAdvDialog::setAmounts(quint64 amount, quint64 fee) {
QString preferredCur = conf()->get(Config::preferredFiatCurrency).toString();
auto convert = [preferredCur](double amount){
return QString::number(appData()->prices.convert("WOW", preferredCur, amount), 'f', 2);
};
QString amount_str = WalletManager::displayAmount(amount);
QString fee_str = WalletManager::displayAmount(fee);
QString total = WalletManager::displayAmount(amount + fee);
QVector<QString> amounts = {amount_str, fee_str, total};
int maxLength = Utils::maxLength(amounts);
std::for_each(amounts.begin(), amounts.end(), [maxLength](QString& amount){amount = amount.rightJustified(maxLength, ' ');});
QString amount_fiat = convert(amount / constants::cdiv);
QString fee_fiat = convert(fee / constants::cdiv);
QString total_fiat = convert((amount + fee) / constants::cdiv);
QVector<QString> amounts_fiat = {amount_fiat, fee_fiat, total_fiat};
int maxLengthFiat = Utils::maxLength(amounts_fiat);
std::for_each(amounts_fiat.begin(), amounts_fiat.end(), [maxLengthFiat](QString& amount){amount = amount.rightJustified(maxLengthFiat, ' ');});
if (m_offline) {
ui->amount->setText(amount_str);
ui->fee->setText(fee_str);
ui->total->setText(total);
} else {
ui->amount->setText(QString("%1 (%2 %3)").arg(amounts[0], amounts_fiat[0], preferredCur));
ui->fee->setText(QString("%1 (%2 %3)").arg(amounts[1], amounts_fiat[1], preferredCur));
ui->total->setText(QString("%1 (%2 %3)").arg(amounts[2], amounts_fiat[2], preferredCur));
}
}
void TxConfAdvDialog::setupConstructionData(const ConstructionInfo& ci) {
for (const auto &in: ci.inputs) {
auto *item = new QTreeWidgetItem(ui->treeInputs);
item->setText(0, in.pubKey);
item->setFont(0, Utils::getMonospaceFont());
item->setText(1, WalletManager::displayAmount(in.amount));
}
ui->treeInputs->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->treeInputs->resizeColumnToContents(1);
ui->treeInputs->header()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->label_inputs->setText(QString("Inputs (%1)").arg(QString::number(ci.inputs.size())));
for (const auto &out: ci.outputs) {
auto *item = new QTreeWidgetItem(ui->treeOutputs);
item->setText(0, out.address);
item->setText(1, WalletManager::displayAmount(out.amount));
item->setFont(0, Utils::getMonospaceFont());
auto index = m_wallet->subaddressIndex(out.address);
QBrush brush;
if (index.isChange()) {
brush = QBrush(ColorScheme::YELLOW.asColor(true));
item->setToolTip(0, "Wallet change/primary address");
// item->setHidden(true);
}
else if (index.isValid()) {
brush = QBrush(ColorScheme::GREEN.asColor(true));
item->setToolTip(0, "Wallet receive address");
}
else if (out.amount == 0) {
brush = QBrush(ColorScheme::GRAY.asColor(true));
item->setToolTip(0, "Dummy output (Min. 2 outs consensus rule)");
}
item->setBackground(0, brush);
}
ui->treeOutputs->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->treeOutputs->resizeColumnToContents(1);
ui->treeOutputs->header()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->label_outputs->setText(QString("Outputs (%1)").arg(QString::number(ci.outputs.size())));
this->adjustSize();
}
void TxConfAdvDialog::signTransaction() {
this->accept();
}
void TxConfAdvDialog::signedSaveFile() {
QString defaultName = QString("%1_signed_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
QString fn = QFileDialog::getSaveFileName(this, "Save transaction to file", QDir::home().filePath(defaultName), "Transaction (*signed_monero_tx)");
if (fn.isEmpty()) {
return;
}
bool success = m_tx->saveToFile(fn);
if (success) {
Utils::showInfo(this, "Transaction saved successfully");
} else {
Utils::showError(this, "Failed to save transaction to file");
}
}
void TxConfAdvDialog::signedCopy() {
Utils::copyToClipboard(m_tx->signedTxToHex(0));
}
void TxConfAdvDialog::txKeyCopy() {
if (m_wallet->isHwBacked()) {
Utils::showError(this, "Unable to copy transaction private key", "Function not supported for hardware wallets");
return;
}
Utils::copyToClipboard(m_tx->transaction(0).txKey);
}
void TxConfAdvDialog::broadcastTransaction() {
if (m_tx == nullptr) return;
m_wallet->commitTransaction(m_tx, m_description);
QDialog::accept();
}
void TxConfAdvDialog::closeDialog() {
if (m_tx != nullptr)
m_wallet->disposeTransaction(m_tx);
if (m_utx != nullptr)
m_wallet->disposeTransaction(m_utx);
QDialog::reject();
}
void TxConfAdvDialog::setupContextMenu(const QPoint &point, QTreeWidget *tree) {
if (!tree) {
return;
}
QTreeWidgetItem *header = tree->headerItem();
if (!header) {
return;
}
auto* menu = new QMenu(this);
for (int column = 0; column < tree->columnCount(); column++)
{
menu->addAction(QString("Copy %1").arg(header->text(column)), this, [this, column, point, tree]{
this->copyFromTree(point, column, tree);
});
}
menu->popup(tree->viewport()->mapToGlobal(point));
}
void TxConfAdvDialog::copyFromTree(const QPoint &point, int column, QTreeWidget *tree) {
QModelIndex index = tree->indexAt(point);
if (!index.isValid()) {
return;
}
QModelIndex dataIndex = index.sibling(index.row(), column);
Utils::copyToClipboard(dataIndex.data().toString());
}
TxConfAdvDialog::~TxConfAdvDialog() = default;