Clean up tx send logic for clarity
This commit is contained in:
parent
fdcd1c32ab
commit
e154f18c79
|
@ -7,6 +7,8 @@
|
||||||
class RPC;
|
class RPC;
|
||||||
class Settings;
|
class Settings;
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
// Struct used to hold destination info when sending a Tx.
|
// Struct used to hold destination info when sending a Tx.
|
||||||
struct ToFields {
|
struct ToFields {
|
||||||
QString addr;
|
QString addr;
|
||||||
|
@ -15,6 +17,11 @@ struct ToFields {
|
||||||
QString encodedMemo;
|
QString encodedMemo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Tx {
|
||||||
|
QString fromAddr;
|
||||||
|
QList<ToFields> toAddrs;
|
||||||
|
};
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +52,10 @@ private:
|
||||||
void removeExtraAddresses();
|
void removeExtraAddresses();
|
||||||
void setDefaultPayFrom();
|
void setDefaultPayFrom();
|
||||||
|
|
||||||
|
Tx createTxFromSendPage();
|
||||||
|
bool confirmTx(Tx tx, ToFields devFee);
|
||||||
|
void fillTxJsonParams(json& params, Tx tx);
|
||||||
|
|
||||||
void cancelButton();
|
void cancelButton();
|
||||||
void sendButton();
|
void sendButton();
|
||||||
void inputComboTextChanged(const QString& text);
|
void inputComboTextChanged(const QString& text);
|
||||||
|
@ -60,7 +71,7 @@ private:
|
||||||
void memoButtonClicked(int number);
|
void memoButtonClicked(int number);
|
||||||
void setMemoEnabled(int number, bool enabled);
|
void setMemoEnabled(int number, bool enabled);
|
||||||
|
|
||||||
QString doSendTxValidations(QString fromAddr, QList<ToFields> toAddrs);
|
QString doSendTxValidations(Tx tx);
|
||||||
|
|
||||||
void donate();
|
void donate();
|
||||||
void importPrivKeys();
|
void importPrivKeys();
|
||||||
|
|
161
src/sendtab.cpp
161
src/sendtab.cpp
|
@ -292,21 +292,12 @@ void MainWindow::maxAmountChecked(int checked) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a Tx from the current state of the send page.
|
||||||
void MainWindow::sendButton() {
|
Tx MainWindow::createTxFromSendPage() {
|
||||||
auto fnSplitAddressForWrap = [=] (const QString& a) -> QString {
|
Tx tx;
|
||||||
if (!a.startsWith("z")) return a;
|
|
||||||
|
|
||||||
auto half = a.length() / 2;
|
|
||||||
auto splitted = a.left(half) + "\n" + a.right(a.length() - half);
|
|
||||||
return splitted;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Gather the from / to addresses
|
// Gather the from / to addresses
|
||||||
QString fromAddr = ui->inputsCombo->currentText().split("(")[0].trimmed();
|
tx.fromAddr = ui->inputsCombo->currentText().split("(")[0].trimmed();
|
||||||
|
|
||||||
|
|
||||||
QList<ToFields> toAddrs;
|
|
||||||
// For each addr/amt in the sendTo tab
|
// For each addr/amt in the sendTo tab
|
||||||
int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that
|
int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that
|
||||||
for (int i=0; i < totalItems; i++) {
|
for (int i=0; i < totalItems; i++) {
|
||||||
|
@ -314,29 +305,21 @@ void MainWindow::sendButton() {
|
||||||
double amt = ui->sendToWidgets->findChild<QLineEdit*>(QString("Amount") % QString::number(i+1))->text().trimmed().toDouble();
|
double amt = ui->sendToWidgets->findChild<QLineEdit*>(QString("Amount") % QString::number(i+1))->text().trimmed().toDouble();
|
||||||
QString memo = ui->sendToWidgets->findChild<QLabel*>(QString("MemoTxt") % QString::number(i+1))->text().trimmed();
|
QString memo = ui->sendToWidgets->findChild<QLabel*>(QString("MemoTxt") % QString::number(i+1))->text().trimmed();
|
||||||
|
|
||||||
toAddrs.push_back( ToFields{addr, amt, memo, memo.toUtf8().toHex()} );
|
tx.toAddrs.push_back( ToFields{addr, amt, memo, memo.toUtf8().toHex()} );
|
||||||
}
|
}
|
||||||
|
|
||||||
QString error = doSendTxValidations(fromAddr, toAddrs);
|
return tx;
|
||||||
if (!error.isEmpty()) {
|
}
|
||||||
// Something went wrong, so show an error and exit
|
|
||||||
QMessageBox msg(
|
|
||||||
QMessageBox::Critical,
|
|
||||||
"Transaction Error",
|
|
||||||
error,
|
|
||||||
QMessageBox::Ok,
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
msg.exec();
|
bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
|
||||||
// abort the Tx
|
auto fnSplitAddressForWrap = [=] (const QString& a) -> QString {
|
||||||
return;
|
if (!a.startsWith("z")) return a;
|
||||||
}
|
|
||||||
|
|
||||||
auto devAddress = Utils::getDevAddr(fromAddr, toAddrs);
|
auto half = a.length() / 2;
|
||||||
|
auto splitted = a.left(half) + "\n" + a.right(a.length() - half);
|
||||||
|
return splitted;
|
||||||
|
};
|
||||||
|
|
||||||
// Get all the addresses and amounts
|
|
||||||
json allRecepients = json::array();
|
|
||||||
|
|
||||||
// Show a confirmation dialog
|
// Show a confirmation dialog
|
||||||
QDialog d(this);
|
QDialog d(this);
|
||||||
|
@ -367,17 +350,8 @@ void MainWindow::sendButton() {
|
||||||
delete confirm.sendToAddrs->findChild<QLabel*>("devFeeUSD");
|
delete confirm.sendToAddrs->findChild<QLabel*>("devFeeUSD");
|
||||||
|
|
||||||
// For each addr/amt/memo, construct the JSON and also build the confirm dialog box
|
// For each addr/amt/memo, construct the JSON and also build the confirm dialog box
|
||||||
for (int i=0; i < toAddrs.size(); i++) {
|
for (int i=0; i < tx.toAddrs.size(); i++) {
|
||||||
auto toAddr = toAddrs[i];
|
auto toAddr = tx.toAddrs[i];
|
||||||
|
|
||||||
// Construct the JSON params
|
|
||||||
json rec = json::object();
|
|
||||||
rec["address"] = toAddr.addr.toStdString();
|
|
||||||
rec["amount"] = toAddr.amount;
|
|
||||||
if (toAddr.addr.startsWith("z") && !toAddr.encodedMemo.trimmed().isEmpty())
|
|
||||||
rec["memo"] = toAddr.encodedMemo.toStdString();
|
|
||||||
|
|
||||||
allRecepients.push_back(rec);
|
|
||||||
|
|
||||||
// Add new Address widgets instead of the same one.
|
// Add new Address widgets instead of the same one.
|
||||||
{
|
{
|
||||||
|
@ -416,17 +390,9 @@ void MainWindow::sendButton() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the dev fee to the transaction
|
|
||||||
if (!devAddress.isEmpty() && Utils::getDevFee() > 0) {
|
|
||||||
json devFee = json::object();
|
|
||||||
devFee["address"] = devAddress.toStdString();
|
|
||||||
devFee["amount"] = Utils::getDevFee();
|
|
||||||
allRecepients.push_back(devFee);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add two rows for fees
|
// Add two rows for fees
|
||||||
{
|
{
|
||||||
auto i = toAddrs.size() * 2;
|
auto i = tx.toAddrs.size() * 2;
|
||||||
|
|
||||||
auto labelMinerFee = new QLabel(confirm.sendToAddrs);
|
auto labelMinerFee = new QLabel(confirm.sendToAddrs);
|
||||||
labelMinerFee->setObjectName(QStringLiteral("labelMinerFee"));
|
labelMinerFee->setObjectName(QStringLiteral("labelMinerFee"));
|
||||||
|
@ -445,17 +411,17 @@ void MainWindow::sendButton() {
|
||||||
confirm.gridLayout->addWidget(minerFeeUSD, i, 2, 1, 1);
|
confirm.gridLayout->addWidget(minerFeeUSD, i, 2, 1, 1);
|
||||||
minerFeeUSD->setText(Settings::getInstance()->getUSDFormat(Utils::getMinerFee()));
|
minerFeeUSD->setText(Settings::getInstance()->getUSDFormat(Utils::getMinerFee()));
|
||||||
|
|
||||||
if (!devAddress.isEmpty() && Utils::getDevFee() > 0) {
|
if (!devFee.addr.isEmpty()) {
|
||||||
auto labelDevFee = new QLabel(confirm.sendToAddrs);
|
auto labelDevFee = new QLabel(confirm.sendToAddrs);
|
||||||
labelDevFee->setObjectName(QStringLiteral("labelDevFee"));
|
labelDevFee->setObjectName(QStringLiteral("labelDevFee"));
|
||||||
confirm.gridLayout->addWidget(labelDevFee, i+1, 0, 1, 1);
|
confirm.gridLayout->addWidget(labelDevFee, i+1, 0, 1, 1);
|
||||||
labelDevFee ->setText("Dev Fee");
|
labelDevFee ->setText("Dev Fee");
|
||||||
|
|
||||||
auto devFee = new QLabel(confirm.sendToAddrs);
|
auto fee = new QLabel(confirm.sendToAddrs);
|
||||||
devFee->setObjectName(QStringLiteral("devFee"));
|
fee->setObjectName(QStringLiteral("devFee"));
|
||||||
devFee->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
|
fee->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
|
||||||
confirm.gridLayout->addWidget(devFee, i+1, 1, 1, 1);
|
confirm.gridLayout->addWidget(fee, i+1, 1, 1, 1);
|
||||||
devFee ->setText(Settings::getInstance()->getZECDisplayFormat(Utils::getDevFee()));
|
fee ->setText(Settings::getInstance()->getZECDisplayFormat(Utils::getDevFee()));
|
||||||
|
|
||||||
auto devFeeUSD = new QLabel(confirm.sendToAddrs);
|
auto devFeeUSD = new QLabel(confirm.sendToAddrs);
|
||||||
devFeeUSD->setObjectName(QStringLiteral("devFeeUSD"));
|
devFeeUSD->setObjectName(QStringLiteral("devFeeUSD"));
|
||||||
|
@ -465,17 +431,71 @@ void MainWindow::sendButton() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add sender
|
// And FromAddress in the confirm dialog
|
||||||
json params = json::array();
|
confirm.sendFrom->setText(fnSplitAddressForWrap(tx.fromAddr));
|
||||||
params.push_back(fromAddr.toStdString());
|
|
||||||
params.push_back(allRecepients);
|
|
||||||
|
|
||||||
// And show it in the confirm dialog
|
|
||||||
confirm.sendFrom->setText(fnSplitAddressForWrap(fromAddr));
|
|
||||||
|
|
||||||
// Show the dialog and submit it if the user confirms
|
// Show the dialog and submit it if the user confirms
|
||||||
if (d.exec() == QDialog::Accepted) {
|
if (d.exec() == QDialog::Accepted) {
|
||||||
|
// Then delete the additional fields from the sendTo tab
|
||||||
|
removeExtraAddresses();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the RPC JSON Parameters for this tx (with the dev fee included if applicable)
|
||||||
|
void MainWindow::fillTxJsonParams(json& params, Tx tx) {
|
||||||
|
// Get all the addresses and amounts
|
||||||
|
json allRecepients = json::array();
|
||||||
|
|
||||||
|
// For each addr/amt/memo, construct the JSON and also build the confirm dialog box
|
||||||
|
for (int i=0; i < tx.toAddrs.size(); i++) {
|
||||||
|
auto toAddr = tx.toAddrs[i];
|
||||||
|
|
||||||
|
// Construct the JSON params
|
||||||
|
json rec = json::object();
|
||||||
|
rec["address"] = toAddr.addr.toStdString();
|
||||||
|
rec["amount"] = toAddr.amount;
|
||||||
|
if (toAddr.addr.startsWith("z") && !toAddr.encodedMemo.trimmed().isEmpty())
|
||||||
|
rec["memo"] = toAddr.encodedMemo.toStdString();
|
||||||
|
|
||||||
|
allRecepients.push_back(rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sender
|
||||||
|
params.push_back(tx.fromAddr.toStdString());
|
||||||
|
params.push_back(allRecepients);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send button clicked
|
||||||
|
void MainWindow::sendButton() {
|
||||||
|
Tx tx = createTxFromSendPage();
|
||||||
|
|
||||||
|
QString error = doSendTxValidations(tx);
|
||||||
|
if (!error.isEmpty()) {
|
||||||
|
// Something went wrong, so show an error and exit
|
||||||
|
QMessageBox msg(QMessageBox::Critical, "Transaction Error", error,
|
||||||
|
QMessageBox::Ok, this);
|
||||||
|
|
||||||
|
msg.exec();
|
||||||
|
|
||||||
|
// abort the Tx
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToFields devFee{ Utils::getDevAddr(tx), Utils::getDevFee(), "", "" };
|
||||||
|
|
||||||
|
// Show a dialog to confirm the Tx
|
||||||
|
if (confirmTx(tx, devFee)) {
|
||||||
|
if (!devFee.addr.isEmpty())
|
||||||
|
tx.toAddrs.push_back(devFee);
|
||||||
|
|
||||||
|
json params = json::array();
|
||||||
|
fillTxJsonParams(params, tx);
|
||||||
std::cout << std::setw(2) << params << std::endl;
|
std::cout << std::setw(2) << params << std::endl;
|
||||||
|
|
||||||
|
// And send the Tx
|
||||||
rpc->sendZTransaction(params, [=](const json& reply) {
|
rpc->sendZTransaction(params, [=](const json& reply) {
|
||||||
QString opid = QString::fromStdString(reply.get<json::string_t>());
|
QString opid = QString::fromStdString(reply.get<json::string_t>());
|
||||||
ui->statusBar->showMessage("Computing Tx: " % opid);
|
ui->statusBar->showMessage("Computing Tx: " % opid);
|
||||||
|
@ -483,13 +503,10 @@ void MainWindow::sendButton() {
|
||||||
// And then start monitoring the transaction
|
// And then start monitoring the transaction
|
||||||
rpc->refreshTxStatus(opid);
|
rpc->refreshTxStatus(opid);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Then delete the additional fields from the sendTo tab
|
|
||||||
removeExtraAddresses();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MainWindow::doSendTxValidations(QString fromAddr, QList<ToFields> toAddrs) {
|
QString MainWindow::doSendTxValidations(Tx tx) {
|
||||||
// 1. Addresses are valid format.
|
// 1. Addresses are valid format.
|
||||||
QRegExp zcexp("^z[a-z0-9]{94}$", Qt::CaseInsensitive);
|
QRegExp zcexp("^z[a-z0-9]{94}$", Qt::CaseInsensitive);
|
||||||
QRegExp zsexp("^z[a-z0-9]{77}$", Qt::CaseInsensitive);
|
QRegExp zsexp("^z[a-z0-9]{77}$", Qt::CaseInsensitive);
|
||||||
|
@ -503,9 +520,9 @@ QString MainWindow::doSendTxValidations(QString fromAddr, QList<ToFields> toAddr
|
||||||
zsexp.exactMatch(addr);
|
zsexp.exactMatch(addr);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!matchesAnyAddr(fromAddr)) return QString("From Address is Invalid");
|
if (!matchesAnyAddr(tx.fromAddr)) return QString("From Address is Invalid");
|
||||||
|
|
||||||
for (auto toAddr : toAddrs) {
|
for (auto toAddr : tx.toAddrs) {
|
||||||
if (!matchesAnyAddr(toAddr.addr))
|
if (!matchesAnyAddr(toAddr.addr))
|
||||||
return QString("Recipient Address ") % toAddr.addr % " is Invalid";
|
return QString("Recipient Address ") % toAddr.addr % " is Invalid";
|
||||||
}
|
}
|
||||||
|
|
|
@ -656,7 +656,7 @@ public:
|
||||||
|
|
||||||
retranslateUi(MainWindow);
|
retranslateUi(MainWindow);
|
||||||
|
|
||||||
tabWidget->setCurrentIndex(2);
|
tabWidget->setCurrentIndex(1);
|
||||||
|
|
||||||
|
|
||||||
QMetaObject::connectSlotsByName(MainWindow);
|
QMetaObject::connectSlotsByName(MainWindow);
|
||||||
|
|
|
@ -13,7 +13,7 @@ const QString Utils::getTokenName() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the dev fee address based on the transaction
|
// Get the dev fee address based on the transaction
|
||||||
const QString Utils::getDevAddr(const QString& fromAddr, const QList<ToFields>& toAddrs) {
|
const QString Utils::getDevAddr(Tx tx) {
|
||||||
auto testnetAddrLookup = [=] (const QString& addr) -> QString {
|
auto testnetAddrLookup = [=] (const QString& addr) -> QString {
|
||||||
if (addr.startsWith("ztestsapling")) {
|
if (addr.startsWith("ztestsapling")) {
|
||||||
return "ztestsapling1kdp74adyfsmm9838jaupgfyx3npgw8ut63stjjx757pc248cuc0ymzphqeux60c64qe5qt68ygh";
|
return "ztestsapling1kdp74adyfsmm9838jaupgfyx3npgw8ut63stjjx757pc248cuc0ymzphqeux60c64qe5qt68ygh";
|
||||||
|
@ -25,13 +25,13 @@ const QString Utils::getDevAddr(const QString& fromAddr, const QList<ToFields>&
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Settings::getInstance()->isTestnet()) {
|
if (Settings::getInstance()->isTestnet()) {
|
||||||
auto devAddr = testnetAddrLookup(fromAddr);
|
auto devAddr = testnetAddrLookup(tx.fromAddr);
|
||||||
if (!devAddr.isEmpty()) {
|
if (!devAddr.isEmpty()) {
|
||||||
return devAddr;
|
return devAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// t-Addr, find if it is going to a sprout or sapling address
|
// t-Addr, find if it is going to a sprout or sapling address
|
||||||
for (const ToFields& to : toAddrs) {
|
for (const ToFields& to : tx.toAddrs) {
|
||||||
devAddr = testnetAddrLookup(to.addr);
|
devAddr = testnetAddrLookup(to.addr);
|
||||||
if (!devAddr.isEmpty()) {
|
if (!devAddr.isEmpty()) {
|
||||||
return devAddr;
|
return devAddr;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
struct ToFields;
|
struct ToFields;
|
||||||
|
struct Tx;
|
||||||
|
|
||||||
class Utils
|
class Utils
|
||||||
{
|
{
|
||||||
|
@ -11,7 +12,7 @@ public:
|
||||||
static const QString txidStatusMessage;
|
static const QString txidStatusMessage;
|
||||||
|
|
||||||
static const QString getTokenName();
|
static const QString getTokenName();
|
||||||
static const QString getDevAddr(const QString& fromAddr, const QList<ToFields>& toAddrs);
|
static const QString getDevAddr(Tx tx);
|
||||||
|
|
||||||
static double getMinerFee();
|
static double getMinerFee();
|
||||||
static double getDevFee();
|
static double getDevFee();
|
||||||
|
|
Loading…
Reference in New Issue