From e9a4a828d6badd6157c51ca8803bc0cdc91bd991 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Wed, 11 Nov 2020 14:26:06 +0100 Subject: [PATCH] Initial MorphToken support --- src/MorphTokenWidget.cpp | 174 ++++++++++++++++ src/MorphTokenWidget.h | 41 ++++ src/MorphTokenWidget.ui | 348 +++++++++++++++++++++++++++++++ src/appcontext.cpp | 2 +- src/assets.qrc | 1 + src/assets/images/morphtoken.png | Bin 0 -> 3708 bytes src/mainwindow.cpp | 4 + src/mainwindow.h | 1 + src/mainwindow.ui | 30 ++- src/utils/MorphTokenApi.cpp | 93 +++++++++ src/utils/MorphTokenApi.h | 51 +++++ src/utils/config.cpp | 1 + src/utils/config.h | 1 + 13 files changed, 744 insertions(+), 3 deletions(-) create mode 100644 src/MorphTokenWidget.cpp create mode 100644 src/MorphTokenWidget.h create mode 100644 src/MorphTokenWidget.ui create mode 100644 src/assets/images/morphtoken.png create mode 100644 src/utils/MorphTokenApi.cpp create mode 100644 src/utils/MorphTokenApi.h diff --git a/src/MorphTokenWidget.cpp b/src/MorphTokenWidget.cpp new file mode 100644 index 0000000..e0a849d --- /dev/null +++ b/src/MorphTokenWidget.cpp @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#include "MorphTokenWidget.h" +#include "ui_MorphTokenWidget.h" +#include "mainwindow.h" + +#include + +MorphTokenWidget::MorphTokenWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::MorphTokenWidget) +{ + ui->setupUi(this); + m_ctx = MainWindow::getContext(); + + m_network = new UtilsNetworking(this->m_ctx->network); + m_api = new MorphTokenApi(this, m_network); + + connect(ui->btnCreateTrade, &QPushButton::clicked, this, &MorphTokenWidget::createTrade); + connect(ui->btn_lookupTrade, &QPushButton::clicked, this, &MorphTokenWidget::lookupTrade); + + connect(m_api, &MorphTokenApi::ApiResponse, this, &MorphTokenWidget::onApiResponse); + + connect(ui->combo_From, QOverload::of(&QComboBox::currentIndexChanged), [this](int index){ + ui->label_refundAddress->setText(QString("Refund address (%1):").arg(ui->combo_From->currentText())); + }); + connect(ui->combo_To, QOverload::of(&QComboBox::currentIndexChanged), [this](int index){ + ui->label_destinationAddress->setText(QString("Destination address (%1):").arg(ui->combo_To->currentText())); + }); + + connect(ui->check_autorefresh, &QCheckBox::toggled, [this](bool toggled){ + m_countdown = 30; + toggled ? m_countdownTimer.start(1000) : m_countdownTimer.stop(); + ui->check_autorefresh->setText("Autorefresh"); + }); + connect(&m_countdownTimer, &QTimer::timeout, this, &MorphTokenWidget::onCountdown); + + connect(ui->line_Id, &QLineEdit::textChanged, [this](const QString &text){ + ui->btn_lookupTrade->setEnabled(!text.isEmpty()); + ui->check_autorefresh->setEnabled(!text.isEmpty()); + }); + + // Default to BTC -> XMR + ui->combo_From->setCurrentIndex(1); + ui->combo_To->setCurrentIndex(0); + + ui->tabWidget->setTabVisible(2, false); +} + +void MorphTokenWidget::createTrade() { + QString inputAsset = ui->combo_From->currentText(); + QString outputAsset = ui->combo_To->currentText(); + QString refundAddress = ui->line_refundAddress->text(); + QString destinationAddress = ui->line_destinationAddress->text(); + + m_api->createTrade(inputAsset, outputAsset, refundAddress, destinationAddress); +} + +void MorphTokenWidget::lookupTrade() { + QString morphId = ui->line_Id->text(); + + if (!morphId.isEmpty()) + m_api->getTrade(morphId); +} + +void MorphTokenWidget::onApiResponse(const MorphTokenApi::MorphTokenResponse &resp) { + if (!resp.ok) { + ui->check_autorefresh->setChecked(false); + QMessageBox::warning(this, "MorphToken error", QString("Request failed:\n\n%1").arg(resp.message)); + return; + } + + ui->debugInfo->setPlainText(QJsonDocument(resp.obj).toJson(QJsonDocument::Indented)); + + if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE || resp.endpoint == MorphTokenApi::Endpoint::GET_TRADE) { + ui->tabWidget->setCurrentIndex(1); + ui->line_Id->setText(resp.obj.value("id").toString()); + + auto obj = resp.obj; + auto input = obj["input"].toObject(); + auto output = obj["output"].toArray()[0].toObject(); + QString state = obj.value("state").toString(); + QString statusText; + + ui->trade->setTitle(QString("Trade (%1)").arg(state)); + + statusText += QString("Morph ID: %1\n\n").arg(obj["id"].toString()); + + if (state == "PENDING") { + statusText += QString("Waiting for a deposit, send %1 to %2\n").arg(input["asset"].toString(), + input["deposit_address"].toString()); + statusText += QString("Rate: 1 %1 -> %2 %3\n\n").arg(input["asset"].toString(), + output["seen_rate"].toString(), + output["asset"].toString()); + statusText += "Limits:\n"; + statusText += QString(" Minimum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["min"].toDouble()), + input["asset"].toString()); + statusText += QString(" Maximum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["max"].toDouble()), + input["asset"].toString()); + statusText += QString("\nSend a single deposit. If the amount is outside the limits, a refund will happen."); + } else if (state == "PROCESSING" || state == "TRADING" || state == "CONFIRMING") { + if (state == "CONFIRMING") { + statusText += QString("Waiting for confirmations\n"); + } else if (state == "TRADING") { + statusText += QString("Your transaction has been received and is confirmed. MorphToken is now executing your trade.\n" + "Usually this step takes no longer than a minute, " + "but in rare cases it can take a couple hours.\n" + "Wait a bit before contacting support.\n"); + } + statusText += QString("Converting %1 to %2\n").arg(input["asset"].toString(), output["asset"].toString()); + statusText += QString("Sending to %1\n").arg(output["address"].toString()); + statusText += QString("Stuck? Contact support at contact@morphtoken.com"); + } else if (state == "COMPLETE") { + if (output["txid"].toString().isEmpty()) { + statusText += QString("MorphToken is sending your transaction.\n"); + statusText += QString("MorphToken will send %1 %2 to %2").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()), + output["asset"].toString(), + output["address"].toString()); + } else { + statusText += QString("Sent %1 %2 to %3\ntxid: {}").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()), + output["asset"].toString(), + output["address"].toString(), + output["txid"].toString()); + } + } else if (state == "PROCESSING_REFUND" || state == "COMPLETE_WITH_REFUND") { + statusText += QString("MorphToken will refund %1 %2\nReason: %3\n").arg(obj["final_amount"].toString(), + obj["asset"].toString(), + obj["reason"].toString()); + + if (obj.contains("txid")) { + statusText += QString("txid: %1").arg(obj["txid"].toString()); + } + } else if (state == "COMPLETE_WITHOUT_REFUND") { + statusText += "Deposit amount below network fee, too small to refund."; + } + + ui->label_status->setText(statusText); + } + + if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE) { + QMessageBox::information(this, "MorphToken", "Trade created!\n\nMake sure to save your Morph ID. You may need it in case something goes wrong."); + } +} + +void MorphTokenWidget::onCountdown() { + if (m_countdown > 0) { + m_countdown -= 1; + } else { + this->lookupTrade(); + m_countdown = 30; + } + ui->check_autorefresh->setText(QString("Autorefresh (%1)").arg(m_countdown)); +} + +QString MorphTokenWidget::formatAmount(const QString &asset, double amount) { + double displayAmount; + double div; + + if (asset == "ETH") + div = 1e18; + else if (asset == "XMR") + div = 1e12; + else + div = 1e8; + + displayAmount = amount / div; + + return QString::number(displayAmount, 'f', 8); +} + +MorphTokenWidget::~MorphTokenWidget() { + delete ui; +} diff --git a/src/MorphTokenWidget.h b/src/MorphTokenWidget.h new file mode 100644 index 0000000..99aa440 --- /dev/null +++ b/src/MorphTokenWidget.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#ifndef FEATHER_MORPHTOKENWIDGET_H +#define FEATHER_MORPHTOKENWIDGET_H + +#include +#include "appcontext.h" +#include "utils/MorphTokenApi.h" + +namespace Ui { + class MorphTokenWidget; +} + +class MorphTokenWidget : public QWidget +{ +Q_OBJECT + +public: + explicit MorphTokenWidget(QWidget *parent = nullptr); + ~MorphTokenWidget() override; + +private: + void createTrade(); + void lookupTrade(); + void onApiResponse(const MorphTokenApi::MorphTokenResponse &resp); + + void onCountdown(); + + QString formatAmount(const QString &asset, double amount); + + Ui::MorphTokenWidget *ui; + + AppContext *m_ctx; + MorphTokenApi *m_api; + UtilsNetworking *m_network; + QTimer m_countdownTimer; + int m_countdown = 30; +}; + +#endif //FEATHER_MORPHTOKENWIDGET_H diff --git a/src/MorphTokenWidget.ui b/src/MorphTokenWidget.ui new file mode 100644 index 0000000..585c9f3 --- /dev/null +++ b/src/MorphTokenWidget.ui @@ -0,0 +1,348 @@ + + + MorphTokenWidget + + + + 0 + 0 + 1036 + 614 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + Create trade + + + + + + + + + 0 + 0 + + + + From: + + + + + + + + XMR + + + + + BTC + + + + + ETH + + + + + BCH + + + + + LTC + + + + + DASH + + + + + + + + + 0 + 0 + + + + To: + + + + + + + + XMR + + + + + BTC + + + + + ETH + + + + + BCH + + + + + LTC + + + + + DASH + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Refund address (XMR): + + + + + + + + + + Destination address (XMR): + + + + + + + + + + + + + + false + + + Powered by MorphToken.com + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Create Trade + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Lookup trade + + + + + + Morph ID or MorphToken deposit address: + + + + + + + + + + + + false + + + Autorefresh + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Lookup trade + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + Trade + + + + + + No trade loaded. + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Debug + + + + + + + 0 + 0 + + + + true + + + + + + + + + + + + diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 25a66d3..8622d7a 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -181,7 +181,7 @@ void AppContext::initTor() { this->tor = new Tor(this, this); this->tor->start(); - if (!(isTails || isWhonix)) { + if (!(isWhonix)) { auto networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort); this->network->setProxy(*networkProxy); if (m_wsUrl.host().endsWith(".onion")) diff --git a/src/assets.qrc b/src/assets.qrc index e699e14..69689a0 100644 --- a/src/assets.qrc +++ b/src/assets.qrc @@ -48,6 +48,7 @@ assets/images/lock_icon.png assets/images/lock.svg assets/images/microphone.png + assets/images/morphtoken.png assets/images/network.png assets/images/offline_tx.png assets/images/person.svg diff --git a/src/assets/images/morphtoken.png b/src/assets/images/morphtoken.png new file mode 100644 index 0000000000000000000000000000000000000000..3d5db609c1afef97907713b1ae3f822286a8de81 GIT binary patch literal 3708 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 a)&Bu<+#$5U8$y5p0000tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc); m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc"); + connect(ui->actionShow_MorphToken, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map)); + m_tabShowHideMapper["MorphToken"] = new ToggleTab(ui->tabMorphToken, "MorphToken", "MorphToken", ui->actionShow_MorphToken, Config::showTabMorphToken); + m_tabShowHideSignalMapper->setMapping(ui->actionShow_MorphToken, "MorphToken"); + #if defined(XMRTO) connect(ui->actionShow_xmr_to, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map)); m_tabShowHideMapper["XMRto"] = new ToggleTab(ui->tabXmrTo, "XMRto", "XMR.to", ui->actionShow_xmr_to, Config::showTabXMRto); diff --git a/src/mainwindow.h b/src/mainwindow.h index c555bc2..774d588 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -76,6 +76,7 @@ public: RECEIVE, COINS, CALC, + MORPHTOKEN, XMR_TO, XMRIG }; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index f9e2fc8..02a60be 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -6,7 +6,7 @@ 0 0 - 894 + 1156 496 @@ -238,6 +238,20 @@ + + + + :/assets/images/morphtoken.png:/assets/images/morphtoken.png + + + MorphToken + + + + + + + @@ -304,7 +318,7 @@ 0 0 - 894 + 1156 30 @@ -418,6 +432,7 @@ + @@ -670,6 +685,11 @@ Import transaction + + + Show MorphToken + + @@ -714,6 +734,12 @@
calcwidget.h
1 + + MorphTokenWidget + QWidget +
MorphTokenWidget.h
+ 1 +
diff --git a/src/utils/MorphTokenApi.cpp b/src/utils/MorphTokenApi.cpp new file mode 100644 index 0000000..eae1380 --- /dev/null +++ b/src/utils/MorphTokenApi.cpp @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#include "MorphTokenApi.h" + +MorphTokenApi::MorphTokenApi(QObject *parent, UtilsNetworking *network, QString baseUrl) + : QObject(parent) + , m_network(network) + , m_baseUrl(std::move(baseUrl)) +{ +} + +void MorphTokenApi::createTrade(const QString &inputAsset, const QString &outputAsset, const QString &refundAddress, const QString &outputAddress) { + QJsonObject trade; + + QJsonObject input; + input["asset"] = inputAsset; + input["refund"] = refundAddress; + + QJsonArray output; + QJsonObject outputObj; + outputObj["asset"] = outputAsset; + outputObj["weight"] = 10000; + outputObj["address"] = outputAddress; + output.append(outputObj); + + trade["input"] = input; + trade["output"] = output; + + QString url = QString("%1/morph").arg(m_baseUrl); + QNetworkReply *reply = m_network->postJson(url, trade); + connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::CREATE_TRADE)); +} + +void MorphTokenApi::getTrade(const QString &morphId) { + QString url = QString("%1/morph/%2").arg(m_baseUrl, morphId); + QNetworkReply *reply = m_network->getJson(url); + connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::GET_TRADE)); +} + +void MorphTokenApi::getRates() { + QString url = QString("%1/rates").arg(m_baseUrl); + QNetworkReply *reply = m_network->getJson(url); + connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::GET_RATES)); +} + +void MorphTokenApi::getLimits(const QString &inputAsset, const QString &outputAsset) { + QJsonObject limits; + + QJsonObject input; + input["asset"] = inputAsset; + + QJsonArray output; + QJsonObject outputObj; + outputObj["asset"] = outputAsset; + outputObj["weight"] = 10000; + output.append(outputObj); + + limits["input"] = input; + limits["output"] = output; + + QString url = QString("%1/limits").arg(m_baseUrl); + QNetworkReply *reply = m_network->postJson(url, limits); + connect(reply, &QNetworkReply::finished, std::bind(&MorphTokenApi::onResponse, this, reply, Endpoint::GET_LIMITS)); +} + +void MorphTokenApi::onResponse(QNetworkReply *reply, Endpoint endpoint) { + const auto ok = reply->error() == QNetworkReply::NoError; + const auto err = reply->errorString(); + + QByteArray data = reply->readAll(); + QJsonObject obj; + if (!data.isEmpty() && Utils::validateJSON(data)) { + auto doc = QJsonDocument::fromJson(data); + obj = doc.object(); + } + else if (!ok) { + emit ApiResponse(MorphTokenResponse(false, endpoint, err, {})); + return; + } + else { + emit ApiResponse(MorphTokenResponse(false, endpoint, "Invalid response from MorphToken", {})); + return; + } + + if (obj.contains("success")) { + emit ApiResponse(MorphTokenResponse(false, endpoint, obj.value("description").toString(), obj)); + return; + } + + reply->deleteLater(); + emit ApiResponse(MorphTokenResponse(true, endpoint, "", obj)); +} \ No newline at end of file diff --git a/src/utils/MorphTokenApi.h b/src/utils/MorphTokenApi.h new file mode 100644 index 0000000..b96a96c --- /dev/null +++ b/src/utils/MorphTokenApi.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#ifndef FEATHER_MORPHTOKENAPI_H +#define FEATHER_MORPHTOKENAPI_H + +#include +#include +#include "utils/networking.h" + +class MorphTokenApi : public QObject { + Q_OBJECT + +public: + enum Endpoint { + CREATE_TRADE = 0, + GET_TRADE, + GET_RATES, + GET_LIMITS + }; + + struct MorphTokenResponse { + explicit MorphTokenResponse(bool ok, Endpoint endpoint, QString message, QJsonObject obj) + : ok(ok), endpoint(endpoint), message(std::move(message)), obj(std::move(obj)) {}; + + bool ok; + Endpoint endpoint; + QString message; + QJsonObject obj; + }; + + explicit MorphTokenApi(QObject *parent, UtilsNetworking *network, QString baseUrl = "https://api.morphtoken.com"); + + void createTrade(const QString &inputAsset, const QString &outputAsset, const QString &refundAddress, const QString &outputAddress); + void getTrade(const QString &morphId); + void getRates(); + void getLimits(const QString &inputAsset, const QString &outputAsset); + +signals: + void ApiResponse(MorphTokenResponse resp); + +private slots: + void onResponse(QNetworkReply *reply, Endpoint endpoint); + +private: + QString m_baseUrl; + UtilsNetworking *m_network; +}; + + +#endif //FEATHER_MORPHTOKENAPI_H diff --git a/src/utils/config.cpp b/src/utils/config.cpp index a98bb2b..33eb201 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -41,6 +41,7 @@ static const QHash configStrings = { {Config::nodeSource,{QS("nodeSource"), 0}}, {Config::useOnionNodes,{QS("useOnionNodes"), false}}, {Config::showTabCoins,{QS("showTabCoins"), false}}, + {Config::showTabMorphToken, {QS("showTabMorphToken"), false}}, {Config::showTabXMRto,{QS("showTabXMRto"), true}}, {Config::showTabXMRig,{QS("showTabXMRig"), false}}, {Config::showTabCalc,{QS("showTabCalc"), true}}, diff --git a/src/utils/config.h b/src/utils/config.h index 759ee30..4e2a218 100644 --- a/src/utils/config.h +++ b/src/utils/config.h @@ -39,6 +39,7 @@ public: nodeSource, useOnionNodes, showTabCoins, + showTabMorphToken, showTabXMRto, showTabCalc, showTabXMRig,