Merge pull request 'Initial MorphToken support' (#145) from tobtoht/feather:morphtoken into master

Reviewed-on: https://git.wownero.com/feather/feather/pulls/145
This commit is contained in:
tobtoht 2020-11-11 16:04:35 +00:00
commit b66aceccc8
13 changed files with 744 additions and 3 deletions

174
src/MorphTokenWidget.cpp Normal file
View file

@ -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 <QMessageBox>
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<int>::of(&QComboBox::currentIndexChanged), [this](int index){
ui->label_refundAddress->setText(QString("Refund address (%1):").arg(ui->combo_From->currentText()));
});
connect(ui->combo_To, QOverload<int>::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;
}

41
src/MorphTokenWidget.h Normal file
View file

@ -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 <QWidget>
#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

348
src/MorphTokenWidget.ui Normal file
View file

@ -0,0 +1,348 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MorphTokenWidget</class>
<widget class="QWidget" name="MorphTokenWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1036</width>
<height>614</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabCreateTrade">
<attribute name="title">
<string>Create trade</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>From:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combo_From">
<item>
<property name="text">
<string>XMR</string>
</property>
</item>
<item>
<property name="text">
<string>BTC</string>
</property>
</item>
<item>
<property name="text">
<string>ETH</string>
</property>
</item>
<item>
<property name="text">
<string>BCH</string>
</property>
</item>
<item>
<property name="text">
<string>LTC</string>
</property>
</item>
<item>
<property name="text">
<string>DASH</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>To:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combo_To">
<item>
<property name="text">
<string>XMR</string>
</property>
</item>
<item>
<property name="text">
<string>BTC</string>
</property>
</item>
<item>
<property name="text">
<string>ETH</string>
</property>
</item>
<item>
<property name="text">
<string>BCH</string>
</property>
</item>
<item>
<property name="text">
<string>LTC</string>
</property>
</item>
<item>
<property name="text">
<string>DASH</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_refundAddress">
<property name="text">
<string>Refund address (XMR):</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="line_refundAddress"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_destinationAddress">
<property name="text">
<string>Destination address (XMR):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="line_destinationAddress"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_4">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Powered by MorphToken.com</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCreateTrade">
<property name="text">
<string>Create Trade</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabLookupTrade">
<attribute name="title">
<string>Lookup trade</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Morph ID or MorphToken deposit address:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_Id"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="check_autorefresh">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Autorefresh</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_lookupTrade">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Lookup trade</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="trade">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Trade</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="label_status">
<property name="text">
<string>No trade loaded.</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabDebug">
<attribute name="title">
<string>Debug</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QPlainTextEdit" name="debugInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -181,7 +181,7 @@ void AppContext::initTor() {
this->tor = new Tor(this, this); this->tor = new Tor(this, this);
this->tor->start(); this->tor->start();
if (!(isTails || isWhonix)) { if (!(isWhonix)) {
auto networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort); auto networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
this->network->setProxy(*networkProxy); this->network->setProxy(*networkProxy);
if (m_wsUrl.host().endsWith(".onion")) if (m_wsUrl.host().endsWith(".onion"))

View file

@ -48,6 +48,7 @@
<file>assets/images/lock_icon.png</file> <file>assets/images/lock_icon.png</file>
<file>assets/images/lock.svg</file> <file>assets/images/lock.svg</file>
<file>assets/images/microphone.png</file> <file>assets/images/microphone.png</file>
<file>assets/images/morphtoken.png</file>
<file>assets/images/network.png</file> <file>assets/images/network.png</file>
<file>assets/images/offline_tx.png</file> <file>assets/images/offline_tx.png</file>
<file>assets/images/person.svg</file> <file>assets/images/person.svg</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -406,6 +406,10 @@ void MainWindow::initMenu() {
m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc); m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc"); 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) #if defined(XMRTO)
connect(ui->actionShow_xmr_to, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map)); 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); m_tabShowHideMapper["XMRto"] = new ToggleTab(ui->tabXmrTo, "XMRto", "XMR.to", ui->actionShow_xmr_to, Config::showTabXMRto);

View file

@ -76,6 +76,7 @@ public:
RECEIVE, RECEIVE,
COINS, COINS,
CALC, CALC,
MORPHTOKEN,
XMR_TO, XMR_TO,
XMRIG XMRIG
}; };

View file

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>894</width> <width>1156</width>
<height>496</height> <height>496</height>
</rect> </rect>
</property> </property>
@ -238,6 +238,20 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabMorphToken">
<attribute name="icon">
<iconset resource="assets.qrc">
<normaloff>:/assets/images/morphtoken.png</normaloff>:/assets/images/morphtoken.png</iconset>
</attribute>
<attribute name="title">
<string>MorphToken</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="MorphTokenWidget" name="morphtokenWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabXmrTo"> <widget class="QWidget" name="tabXmrTo">
<attribute name="icon"> <attribute name="icon">
<iconset resource="assets.qrc"> <iconset resource="assets.qrc">
@ -304,7 +318,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>894</width> <width>1156</width>
<height>30</height> <height>30</height>
</rect> </rect>
</property> </property>
@ -418,6 +432,7 @@
</property> </property>
<addaction name="actionShow_Coins"/> <addaction name="actionShow_Coins"/>
<addaction name="actionShow_calc"/> <addaction name="actionShow_calc"/>
<addaction name="actionShow_MorphToken"/>
<addaction name="actionShow_xmr_to"/> <addaction name="actionShow_xmr_to"/>
<addaction name="actionShow_XMRig"/> <addaction name="actionShow_XMRig"/>
</widget> </widget>
@ -670,6 +685,11 @@
<string>Import transaction</string> <string>Import transaction</string>
</property> </property>
</action> </action>
<action name="actionShow_MorphToken">
<property name="text">
<string>Show MorphToken</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>
@ -714,6 +734,12 @@
<header>calcwidget.h</header> <header>calcwidget.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>MorphTokenWidget</class>
<extends>QWidget</extends>
<header>MorphTokenWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="assets.qrc"/> <include location="assets.qrc"/>

View file

@ -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));
}

51
src/utils/MorphTokenApi.h Normal file
View file

@ -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 <QObject>
#include <utility>
#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

View file

@ -41,6 +41,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::nodeSource,{QS("nodeSource"), 0}}, {Config::nodeSource,{QS("nodeSource"), 0}},
{Config::useOnionNodes,{QS("useOnionNodes"), false}}, {Config::useOnionNodes,{QS("useOnionNodes"), false}},
{Config::showTabCoins,{QS("showTabCoins"), false}}, {Config::showTabCoins,{QS("showTabCoins"), false}},
{Config::showTabMorphToken, {QS("showTabMorphToken"), false}},
{Config::showTabXMRto,{QS("showTabXMRto"), true}}, {Config::showTabXMRto,{QS("showTabXMRto"), true}},
{Config::showTabXMRig,{QS("showTabXMRig"), false}}, {Config::showTabXMRig,{QS("showTabXMRig"), false}},
{Config::showTabCalc,{QS("showTabCalc"), true}}, {Config::showTabCalc,{QS("showTabCalc"), true}},

View file

@ -39,6 +39,7 @@ public:
nodeSource, nodeSource,
useOnionNodes, useOnionNodes,
showTabCoins, showTabCoins,
showTabMorphToken,
showTabXMRto, showTabXMRto,
showTabCalc, showTabCalc,
showTabXMRig, showTabXMRig,