mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-23 12:09:50 +00:00
Merge pull request 'Exchanges: Remove XMR.to' (#316) from tobtoht/feather:rip_to into master
Reviewed-on: https://git.wownero.com/feather/feather/pulls/316
This commit is contained in:
commit
e3f6b73ab2
24 changed files with 2 additions and 1863 deletions
|
@ -10,7 +10,6 @@ set(VERSION_REVISION "0")
|
||||||
set(VERSION "beta-3")
|
set(VERSION "beta-3")
|
||||||
|
|
||||||
option(FETCH_DEPS "Download dependencies if they are not found" ON)
|
option(FETCH_DEPS "Download dependencies if they are not found" ON)
|
||||||
option(XMRTO "Include Xmr.To module" ON)
|
|
||||||
option(XMRIG "Include XMRig module" ON)
|
option(XMRIG "Include XMRig module" ON)
|
||||||
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
|
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,6 @@ via the `CMAKE_PREFIX_PATH` definition. For me this is:
|
||||||
|
|
||||||
There are some Monero/Feather related options/definitions that you may pass:
|
There are some Monero/Feather related options/definitions that you may pass:
|
||||||
|
|
||||||
- `-DXMRTO=OFF` - disable Xmr.To feature
|
|
||||||
- `-DXMRIG=OFF` - disable XMRig feature
|
- `-DXMRIG=OFF` - disable XMRig feature
|
||||||
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
|
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
|
||||||
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
|
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -30,7 +30,6 @@ CMAKEFLAGS = \
|
||||||
-DARCH=x86_64 \
|
-DARCH=x86_64 \
|
||||||
-DBUILD_64=On \
|
-DBUILD_64=On \
|
||||||
-DBUILD_TESTS=Off \
|
-DBUILD_TESTS=Off \
|
||||||
-DXMRTO=On \
|
|
||||||
-DXMRIG=On \
|
-DXMRIG=On \
|
||||||
-DTOR_BIN=Off \
|
-DTOR_BIN=Off \
|
||||||
-DCMAKE_CXX_STANDARD=11 \
|
-DCMAKE_CXX_STANDARD=11 \
|
||||||
|
|
|
@ -122,10 +122,6 @@ if(DONATE_BEG)
|
||||||
target_compile_definitions(feather PRIVATE DONATE_BEG=1)
|
target_compile_definitions(feather PRIVATE DONATE_BEG=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(XMRTO)
|
|
||||||
target_compile_definitions(feather PRIVATE HAS_XMRTO=1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TOR_BIN)
|
if(TOR_BIN)
|
||||||
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
|
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -132,11 +132,6 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||||
// fiat/crypto lookup
|
// fiat/crypto lookup
|
||||||
AppContext::prices = new Prices();
|
AppContext::prices = new Prices();
|
||||||
|
|
||||||
// xmr.to
|
|
||||||
#ifdef HAS_XMRTO
|
|
||||||
this->XMRTo = new XmrTo(this);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// XMRig
|
// XMRig
|
||||||
#ifdef HAS_XMRIG
|
#ifdef HAS_XMRIG
|
||||||
this->XMRig = new XmRig(this->configDirectory, this);
|
this->XMRig = new XmRig(this->configDirectory, this);
|
||||||
|
@ -171,7 +166,6 @@ void AppContext::initTor() {
|
||||||
if (m_wsUrl.host().endsWith(".onion"))
|
if (m_wsUrl.host().endsWith(".onion"))
|
||||||
this->ws->webSocket.setProxy(*networkProxy);
|
this->ws->webSocket.setProxy(*networkProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::initWS() {
|
void AppContext::initWS() {
|
||||||
|
@ -199,13 +193,6 @@ void AppContext::onSweepOutput(const QString &keyImage, QString address, bool ch
|
||||||
this->currentWallet->createTransactionSingleAsync(keyImage, address, outputs, this->tx_priority);
|
this->currentWallet->createTransactionSingleAsync(keyImage, address, outputs, this->tx_priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onCreateTransaction(XmrToOrder *order) {
|
|
||||||
// tx creation via xmr.to
|
|
||||||
const QString description = QString("XmrTo order %1").arg(order->uuid);
|
|
||||||
quint64 amount = WalletManager::amountFromDouble(order->incoming_amount_total);
|
|
||||||
this->onCreateTransaction(order->receiving_subaddress, amount, description, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppContext::onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all) {
|
void AppContext::onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all) {
|
||||||
// tx creation
|
// tx creation
|
||||||
this->tmpTxDescription = description;
|
this->tmpTxDescription = description;
|
||||||
|
@ -434,12 +421,6 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
|
||||||
QJsonObject fiat_rates = msg.value("data").toObject();
|
QJsonObject fiat_rates = msg.value("data").toObject();
|
||||||
AppContext::prices->fiatPricesReceived(fiat_rates);
|
AppContext::prices->fiatPricesReceived(fiat_rates);
|
||||||
}
|
}
|
||||||
#if defined(HAS_XMRTO)
|
|
||||||
else if(cmd == "xmrto_rates") {
|
|
||||||
auto xmr_rates = msg.value("data").toObject();
|
|
||||||
this->XMRTo->onRatesReceived(xmr_rates);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else if(cmd == "reddit") {
|
else if(cmd == "reddit") {
|
||||||
QJsonArray reddit_data = msg.value("data").toArray();
|
QJsonArray reddit_data = msg.value("data").toArray();
|
||||||
this->onWSReddit(reddit_data);
|
this->onWSReddit(reddit_data);
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "utils/prices.h"
|
#include "utils/prices.h"
|
||||||
#include "utils/networking.h"
|
#include "utils/networking.h"
|
||||||
#include "utils/tor.h"
|
#include "utils/tor.h"
|
||||||
#include "utils/xmrto.h"
|
|
||||||
#include "utils/xmrig.h"
|
#include "utils/xmrig.h"
|
||||||
#include "utils/wsclient.h"
|
#include "utils/wsclient.h"
|
||||||
#include "utils/txfiathistory.h"
|
#include "utils/txfiathistory.h"
|
||||||
|
@ -81,7 +80,6 @@ public:
|
||||||
|
|
||||||
Tor *tor{};
|
Tor *tor{};
|
||||||
WSClient *ws;
|
WSClient *ws;
|
||||||
XmrTo *XMRTo;
|
|
||||||
XmRig *XMRig;
|
XmRig *XMRig;
|
||||||
Nodes *nodes;
|
Nodes *nodes;
|
||||||
static Prices *prices;
|
static Prices *prices;
|
||||||
|
@ -114,7 +112,6 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void onOpenWallet(const QString& path, const QString &password);
|
void onOpenWallet(const QString& path, const QString &password);
|
||||||
void onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all);
|
void onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all);
|
||||||
void onCreateTransaction(XmrToOrder *order);
|
|
||||||
void onCreateTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
|
void onCreateTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
|
||||||
void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
|
void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
|
||||||
void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
|
void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
<file>assets/images/cutexmrfox.png</file>
|
<file>assets/images/cutexmrfox.png</file>
|
||||||
<file>assets/images/edit.png</file>
|
<file>assets/images/edit.png</file>
|
||||||
<file>assets/images/exchange.png</file>
|
<file>assets/images/exchange.png</file>
|
||||||
|
<file>assets/images/exchange_white.png</file>
|
||||||
<file>assets/images/expired.png</file>
|
<file>assets/images/expired.png</file>
|
||||||
<file>assets/images/expired_icon.png</file>
|
<file>assets/images/expired_icon.png</file>
|
||||||
<file>assets/images/eye1.png</file>
|
<file>assets/images/eye1.png</file>
|
||||||
|
@ -97,8 +98,6 @@
|
||||||
<file>assets/images/unpaid.png</file>
|
<file>assets/images/unpaid.png</file>
|
||||||
<file>assets/images/update.png</file>
|
<file>assets/images/update.png</file>
|
||||||
<file>assets/images/warning.png</file>
|
<file>assets/images/warning.png</file>
|
||||||
<file>assets/images/xmrto_big.png</file>
|
|
||||||
<file>assets/images/xmrto.png</file>
|
|
||||||
<file>assets/images/xmrig.ico</file>
|
<file>assets/images/xmrig.ico</file>
|
||||||
<file>assets/images/xmrig.svg</file>
|
<file>assets/images/xmrig.svg</file>
|
||||||
<file>assets/images/zoom.png</file>
|
<file>assets/images/zoom.png</file>
|
||||||
|
|
BIN
src/assets/images/exchange_white.png
Normal file
BIN
src/assets/images/exchange_white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1,000 B |
|
@ -1,35 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#include "xmrtoinfodialog.h"
|
|
||||||
#include "ui_xmrtoinfodialog.h"
|
|
||||||
|
|
||||||
XmrToInfoDialog::XmrToInfoDialog(XmrToOrder *oInfo, QWidget *parent)
|
|
||||||
: QDialog(parent)
|
|
||||||
, ui(new Ui::XmrToInfoDialog)
|
|
||||||
, m_oInfo(oInfo)
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
ui->status->setText(XmrTo::stateMap[(OrderState) oInfo->state]);
|
|
||||||
ui->xmrto_id->setText(!oInfo->uuid.isEmpty() ? oInfo->uuid : "");
|
|
||||||
|
|
||||||
ui->error_code->setText(!oInfo->errorCode.isEmpty() ? oInfo->errorCode : "");
|
|
||||||
ui->error_msg->setText(!oInfo->errorMsg.isEmpty() ? oInfo->errorMsg : "");
|
|
||||||
|
|
||||||
ui->xmr_amount->setText(QString::number(oInfo->incoming_amount_total));
|
|
||||||
ui->btc_amount->setText(QString::number(oInfo->btc_amount));
|
|
||||||
ui->rate->setText(oInfo->incoming_price_btc > 0 ? QString::number(oInfo->incoming_price_btc) : "");
|
|
||||||
|
|
||||||
ui->xmr_txid->setText(oInfo->xmr_txid);
|
|
||||||
ui->xmr_address->setText(oInfo->receiving_subaddress);
|
|
||||||
|
|
||||||
ui->btc_txid->setText(oInfo->btc_txid);
|
|
||||||
ui->btc_address->setText(oInfo->btc_dest_address);
|
|
||||||
|
|
||||||
this->adjustSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
XmrToInfoDialog::~XmrToInfoDialog() {
|
|
||||||
delete ui;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#ifndef FEATHER_XMRTOINFODIALOG_H
|
|
||||||
#define FEATHER_XMRTOINFODIALOG_H
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
#include "utils/xmrto.h"
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class XmrToInfoDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
class XmrToInfoDialog : public QDialog
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit XmrToInfoDialog(XmrToOrder *oInfo, QWidget *parent = nullptr);
|
|
||||||
~XmrToInfoDialog() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::XmrToInfoDialog *ui;
|
|
||||||
|
|
||||||
XmrToOrder *m_oInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //FEATHER_XMRTOINFODIALOG_H
|
|
|
@ -1,256 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>XmrToInfoDialog</class>
|
|
||||||
<widget class="QWidget" name="XmrToInfoDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>689</width>
|
|
||||||
<height>581</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Xmr.to Order</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label_10">
|
|
||||||
<property name="text">
|
|
||||||
<string>Status:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_16">
|
|
||||||
<property name="text">
|
|
||||||
<string>XMR.to ID:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_17">
|
|
||||||
<property name="text">
|
|
||||||
<string>Error code:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLineEdit" name="status">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLineEdit" name="xmrto_id">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLineEdit" name="error_code">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line_5">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QFormLayout" name="formLayout_3">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label_14">
|
|
||||||
<property name="text">
|
|
||||||
<string>XMR amount:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_20">
|
|
||||||
<property name="text">
|
|
||||||
<string>BTC amount:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_22">
|
|
||||||
<property name="text">
|
|
||||||
<string>Rate:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLineEdit" name="xmr_amount">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLineEdit" name="btc_amount">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLineEdit" name="rate">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Message:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="error_msg">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>XMR.to address:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="xmr_address">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>XMR txid:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="xmr_txid">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line_3">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_12">
|
|
||||||
<property name="text">
|
|
||||||
<string>BTC address:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="btc_address">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>BTC txid:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="btc_txid">
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
|
||||||
<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="pushButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Copy support template</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</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>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
|
@ -97,22 +97,6 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||||
connect(ui->actionShow_debug_info, &QAction::triggered, this, &MainWindow::showDebugInfo);
|
connect(ui->actionShow_debug_info, &QAction::triggered, this, &MainWindow::showDebugInfo);
|
||||||
connect(ui->actionOfficialWebsite, &QAction::triggered, [=] { Utils::externalLinkWarning(this, "https://featherwallet.org"); });
|
connect(ui->actionOfficialWebsite, &QAction::triggered, [=] { Utils::externalLinkWarning(this, "https://featherwallet.org"); });
|
||||||
|
|
||||||
#if defined(HAS_XMRTO)
|
|
||||||
// xmr.to connects/widget
|
|
||||||
connect(ui->xmrToWidget, &XMRToWidget::viewOrder, m_ctx->XMRTo, &XmrTo::onViewOrder);
|
|
||||||
connect(ui->xmrToWidget, &XMRToWidget::getRates, m_ctx->XMRTo, &XmrTo::onGetRates);
|
|
||||||
connect(ui->xmrToWidget, &XMRToWidget::createOrder, m_ctx->XMRTo, &XmrTo::createOrder);
|
|
||||||
connect(m_ctx->XMRTo, &XmrTo::ratesUpdated, ui->xmrToWidget, &XMRToWidget::onRatesUpdated);
|
|
||||||
connect(m_ctx->XMRTo, &XmrTo::connectionError, ui->xmrToWidget, &XMRToWidget::onConnectionError);
|
|
||||||
connect(m_ctx->XMRTo, &XmrTo::connectionSuccess, ui->xmrToWidget, &XMRToWidget::onConnectionSuccess);
|
|
||||||
connect(m_ctx, &AppContext::balanceUpdated, ui->xmrToWidget, &XMRToWidget::onBalanceUpdated);
|
|
||||||
connect(m_ctx->XMRTo, &XmrTo::openURL, this, [=](const QString &url){ Utils::externalLinkWarning(this, url); });
|
|
||||||
connect(m_ctx, &AppContext::walletClosed, ui->xmrToWidget, &XMRToWidget::onWalletClosed);
|
|
||||||
ui->xmrToWidget->setHistoryModel(m_ctx->XMRTo->tableModel);
|
|
||||||
#else
|
|
||||||
ui->tabExchanges->setTabVisible(0, false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX)
|
#if defined(Q_OS_LINUX)
|
||||||
// system tray
|
// system tray
|
||||||
m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/images/appicons/64x64.png"));
|
m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/images/appicons/64x64.png"));
|
||||||
|
@ -225,10 +209,6 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||||
connect(m_ctx, &AppContext::initiateTransaction, ui->sendWidget, &SendWidget::onInitiateTransaction);
|
connect(m_ctx, &AppContext::initiateTransaction, ui->sendWidget, &SendWidget::onInitiateTransaction);
|
||||||
connect(m_ctx, &AppContext::endTransaction, ui->sendWidget, &SendWidget::onEndTransaction);
|
connect(m_ctx, &AppContext::endTransaction, ui->sendWidget, &SendWidget::onEndTransaction);
|
||||||
|
|
||||||
// XMR.to
|
|
||||||
connect(m_ctx, &AppContext::initiateTransaction, ui->xmrToWidget, &XMRToWidget::onInitiateTransaction);
|
|
||||||
connect(m_ctx, &AppContext::endTransaction, ui->xmrToWidget, &XMRToWidget::onEndTransaction);
|
|
||||||
|
|
||||||
connect(m_ctx, &AppContext::initiateTransaction, [this]{
|
connect(m_ctx, &AppContext::initiateTransaction, [this]{
|
||||||
m_statusDots = 0;
|
m_statusDots = 0;
|
||||||
m_constructingTransaction = true;
|
m_constructingTransaction = true;
|
||||||
|
@ -421,13 +401,8 @@ 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");
|
||||||
|
|
||||||
#if defined(HAS_XMRTO)
|
|
||||||
connect(ui->actionShow_Exchange, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
|
||||||
m_tabShowHideMapper["Exchange"] = new ToggleTab(ui->tabExchange, "Exchange", "Exchange", ui->actionShow_Exchange, Config::showTabExchange);
|
|
||||||
m_tabShowHideSignalMapper->setMapping(ui->actionShow_Exchange, "Exchange");
|
|
||||||
#else
|
|
||||||
ui->actionShow_Exchange->setVisible(false);
|
ui->actionShow_Exchange->setVisible(false);
|
||||||
#endif
|
ui->tabWidget->setTabVisible(Tabs::EXCHANGES, false);
|
||||||
|
|
||||||
#if defined(HAS_XMRIG)
|
#if defined(HAS_XMRIG)
|
||||||
connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
||||||
|
|
|
@ -293,39 +293,6 @@
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<widget class="QTabWidget" name="tabExchanges">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="tabXMRto">
|
|
||||||
<attribute name="icon">
|
|
||||||
<iconset resource="assets.qrc">
|
|
||||||
<normaloff>:/assets/images/xmrto.png</normaloff>:/assets/images/xmrto.png</iconset>
|
|
||||||
</attribute>
|
|
||||||
<attribute name="title">
|
|
||||||
<string>XMR.to</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
|
||||||
<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="XMRToWidget" name="xmrToWidget" native="true"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tabXmrRig">
|
<widget class="QWidget" name="tabXmrRig">
|
||||||
|
@ -764,12 +731,6 @@
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>historywidget.h</header>
|
<header>historywidget.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
|
||||||
<class>XMRToWidget</class>
|
|
||||||
<extends>QWidget</extends>
|
|
||||||
<header>xmrtowidget.h</header>
|
|
||||||
<container>1</container>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>CalcWidget</class>
|
<class>CalcWidget</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#include "XmrToModel.h"
|
|
||||||
#include "model/ModelUtils.h"
|
|
||||||
#include "utils/xmrto.h"
|
|
||||||
#include "utils/ColorScheme.h"
|
|
||||||
|
|
||||||
XmrToModel::XmrToModel(QList<XmrToOrder*> *orders, QObject *parent)
|
|
||||||
: QAbstractTableModel(parent),
|
|
||||||
orders(orders)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToModel::update() {
|
|
||||||
beginResetModel();
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
int XmrToModel::rowCount(const QModelIndex &) const {
|
|
||||||
return this->orders->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
int XmrToModel::columnCount(const QModelIndex &) const {
|
|
||||||
return COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant XmrToModel::data(const QModelIndex &index, int role) const {
|
|
||||||
const int _row = index.row();
|
|
||||||
const int _col = index.column();
|
|
||||||
const auto order = this->orders->at(_row);
|
|
||||||
|
|
||||||
if (role == Qt::DisplayRole){
|
|
||||||
switch(index.column()){
|
|
||||||
case Status:
|
|
||||||
{
|
|
||||||
QString status = XmrTo::stateMap[(OrderState) order->state];
|
|
||||||
|
|
||||||
if (order->state == OrderState::Status_OrderUnpaid)
|
|
||||||
return QString("%1 (%2)").arg(status, QString::number(order->countdown));
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
case ID:
|
|
||||||
return !order->uuid.isEmpty() ? order->uuid.split("-")[1] : "-";
|
|
||||||
case Destination:
|
|
||||||
return ModelUtils::displayAddress(order->btc_dest_address, 1);
|
|
||||||
case Conversion:
|
|
||||||
if(order->state <= OrderState::Status_OrderToBeCreated)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
return QString("%1 XMR ⟶ %2 BTC").arg(QString::number(order->incoming_amount_total), QString::number(order->btc_amount));
|
|
||||||
case Rate:
|
|
||||||
return order->incoming_price_btc ? QString::number(order->incoming_price_btc, 'f', 6) : "";
|
|
||||||
case ErrorMsg:
|
|
||||||
if(order->errorMsg.isEmpty()) return "";
|
|
||||||
return order->errorMsg;
|
|
||||||
default: return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if(role == Qt::BackgroundRole) {
|
|
||||||
if (_col == 0) {
|
|
||||||
if (order->state == OrderState::Status_OrderPaid || order->state == OrderState::Status_OrderPaidUnconfirmed)
|
|
||||||
return QBrush(ColorScheme::GREEN.asColor(true));
|
|
||||||
else if (order->state == OrderState::Status_OrderCreating || order->state == OrderState::Status_OrderToBeCreated)
|
|
||||||
return QBrush(ColorScheme::YELLOW.asColor(true));
|
|
||||||
else if (order->state == OrderState::Status_OrderUnpaid)
|
|
||||||
return QBrush(ColorScheme::YELLOW.asColor(true));
|
|
||||||
else if (order->state == OrderState::Status_OrderBTCSent)
|
|
||||||
return QBrush(ColorScheme::GREEN.asColor(true));
|
|
||||||
else if (order->state == OrderState::Status_OrderFailed || order->state == OrderState::Status_OrderTimedOut)
|
|
||||||
return QBrush(ColorScheme::RED.asColor(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (role == Qt::FontRole) {
|
|
||||||
switch(index.column()) {
|
|
||||||
case ID:
|
|
||||||
case Destination:
|
|
||||||
return ModelUtils::getMonospaceFont();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant XmrToModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
|
||||||
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
|
|
||||||
switch (section) {
|
|
||||||
case Status:
|
|
||||||
return QString("Status");
|
|
||||||
case ID:
|
|
||||||
return QString("ID");
|
|
||||||
case Destination:
|
|
||||||
return QString("Address");
|
|
||||||
case Conversion:
|
|
||||||
return QString("Conversion");
|
|
||||||
case Rate:
|
|
||||||
return QString("Rate");
|
|
||||||
case ErrorMsg:
|
|
||||||
return QString("Message");
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#ifndef FEATHER_XMRTOMODEL_H
|
|
||||||
#define FEATHER_XMRTOMODEL_H
|
|
||||||
|
|
||||||
#include <QAbstractTableModel>
|
|
||||||
|
|
||||||
class XmrToOrder;
|
|
||||||
class XmrToModel : public QAbstractTableModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum ModelColumn
|
|
||||||
{
|
|
||||||
Status = 0,
|
|
||||||
ID,
|
|
||||||
Conversion,
|
|
||||||
Rate,
|
|
||||||
Destination,
|
|
||||||
ErrorMsg,
|
|
||||||
COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
XmrToModel(QList<XmrToOrder*> *orders, QObject *parent = nullptr);
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
|
||||||
QList<XmrToOrder*> *orders;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void update();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //FEATHER_XMRTOMODEL_H
|
|
|
@ -1,110 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#include "xmrto.h"
|
|
||||||
|
|
||||||
#include "appcontext.h"
|
|
||||||
|
|
||||||
QMap<OrderState, QString> XmrTo::stateMap;
|
|
||||||
|
|
||||||
XmrTo::XmrTo(AppContext *ctx, QObject *parent) :
|
|
||||||
QObject(parent),
|
|
||||||
m_ctx(ctx) {
|
|
||||||
|
|
||||||
m_baseUrl = m_ctx->networkType == NetworkType::Type::MAINNET ? "https://xmr.to" : "https://test.xmr.to";
|
|
||||||
|
|
||||||
m_netTor = new UtilsNetworking(this->m_ctx->network);
|
|
||||||
m_netClear = new UtilsNetworking(this->m_ctx->networkClearnet);
|
|
||||||
|
|
||||||
m_apiTor = new XmrToApi(this, m_netTor, m_baseUrl);
|
|
||||||
m_apiClear = new XmrToApi(this, m_netClear, m_baseUrl);
|
|
||||||
|
|
||||||
connect(m_apiTor, &XmrToApi::ApiResponse, this, &XmrTo::onApiResponse);
|
|
||||||
connect(m_apiClear, &XmrToApi::ApiResponse, this, &XmrTo::onApiResponse);
|
|
||||||
|
|
||||||
connect(this, &XmrTo::orderPaymentRequired, this->m_ctx, QOverload<XmrToOrder*>::of(&AppContext::onCreateTransaction));
|
|
||||||
|
|
||||||
XmrTo::stateMap[OrderState::Status_Idle] = "IDLE";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderCreating] = "CREATING";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderUnpaid] = "UNPAID";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderToBeCreated] = "TO_BE_CREATED";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderUnderPaid] = "UNDERPAID";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderPaidUnconfirmed] = "PAID_UNCONFIRMED";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderPaid] = "PAID";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderBTCSent] = "BTC_SENT";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderTimedOut] = "TIMED_OUT";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderFailed] = "FAILED";
|
|
||||||
XmrTo::stateMap[OrderState::Status_OrderXMRSent] = "XMR_SENT";
|
|
||||||
|
|
||||||
this->tableModel = new XmrToModel(&this->orders, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrTo::createOrder(double amount, const QString ¤cy, const QString &btcAddress) {
|
|
||||||
// ^[13][a-km-zA-HJ-NP-Z0-9]{26,33}$
|
|
||||||
|
|
||||||
XmrToOrder *order;
|
|
||||||
order = new XmrToOrder(this->m_ctx, m_netTor, m_baseUrl, false, &this->rates, this);
|
|
||||||
|
|
||||||
connect(order, &XmrToOrder::orderFailed, this, &XmrTo::orderFailed);
|
|
||||||
connect(order, &XmrToOrder::orderPaid, this, &XmrTo::orderPaid);
|
|
||||||
connect(order, &XmrToOrder::orderPaidUnconfirmed, this, &XmrTo::orderPaidUnconfirmed);
|
|
||||||
connect(order, &XmrToOrder::orderPaymentRequired, this, &XmrTo::orderPaymentRequired);
|
|
||||||
connect(order, &XmrToOrder::orderChanged, this->tableModel, &XmrToModel::update);
|
|
||||||
|
|
||||||
order->create(amount, currency, btcAddress);
|
|
||||||
this->orders.append(order);
|
|
||||||
tableModel->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void XmrTo::onApiResponse(const XmrToResponse &resp) {
|
|
||||||
if (!resp.ok) {
|
|
||||||
this->onApiFailure(resp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit connectionSuccess();
|
|
||||||
if (resp.endpoint == Endpoint::RATES) {
|
|
||||||
onRatesReceived(resp.obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrTo::onApiFailure(const XmrToResponse &resp) {
|
|
||||||
emit connectionError(resp.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrTo::onGetRates() {
|
|
||||||
m_apiTor->getRates();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrTo::onRatesReceived(const QJsonObject &doc) {
|
|
||||||
this->rates.price = doc.value("price").toString().toDouble();
|
|
||||||
this->rates.ln_lower_limit = doc.value("ln_lower_limit").toString().toDouble();
|
|
||||||
this->rates.ln_upper_limit = doc.value("ln_upper_limit").toString().toDouble();
|
|
||||||
this->rates.lower_limit = doc.value("lower_limit").toString().toDouble();
|
|
||||||
this->rates.upper_limit = doc.value("upper_limit").toString().toDouble();
|
|
||||||
this->rates.zero_conf_enabled = doc.value("zero_conf_enabled").toBool();
|
|
||||||
this->rates.zero_conf_max_amount = doc.value("zero_conf_enabled").toString().toDouble();
|
|
||||||
emit ratesUpdated(rates);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrTo::onNetworkChanged(bool clearnet) {
|
|
||||||
m_api = clearnet ? m_apiClear : m_apiTor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrTo::onWalletClosed() {
|
|
||||||
// @TODO: cleanup
|
|
||||||
for(const auto &order: this->orders)
|
|
||||||
order->deleteLater();
|
|
||||||
|
|
||||||
this->tableModel->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrTo::onWalletOpened() {
|
|
||||||
// @TODO: read past XMR.To orders, start pending ones
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrTo::onViewOrder(const QString &orderId) {
|
|
||||||
QString url = QString("%1/nojs/status/%2").arg(this->m_baseUrl).arg(orderId);
|
|
||||||
emit openURL(url);
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#ifndef FEATHER_XMRTOCONVERT_H
|
|
||||||
#define FEATHER_XMRTOCONVERT_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "model/XmrToModel.h"
|
|
||||||
#include "utils/xmrtoorder.h"
|
|
||||||
#include "utils/xmrtoapi.h"
|
|
||||||
|
|
||||||
class AppContext;
|
|
||||||
class XmrTo: public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit XmrTo(AppContext *ctx, QObject *parent = nullptr);
|
|
||||||
Q_ENUM(OrderState);
|
|
||||||
|
|
||||||
XmrToModel *tableModel;
|
|
||||||
static QMap<OrderState, QString> stateMap;
|
|
||||||
XmrToRates rates;
|
|
||||||
QList<XmrToOrder*> orders;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void createOrder(double amount, const QString ¤cy, const QString &btcAddress);
|
|
||||||
void onGetRates();
|
|
||||||
void onRatesReceived(const QJsonObject &doc);
|
|
||||||
void onViewOrder(const QString &orderId);
|
|
||||||
void onNetworkChanged(bool clearnet);
|
|
||||||
void onWalletOpened();
|
|
||||||
void onWalletClosed();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onApiResponse(const XmrToResponse &doc);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void orderPaymentRequired(XmrToOrder *order);
|
|
||||||
void orderPaidUnconfirmed(XmrToOrder *order);
|
|
||||||
void orderPaid(XmrToOrder *order);
|
|
||||||
void orderFailed(XmrToOrder *order);
|
|
||||||
void ratesUpdated(XmrToRates rates);
|
|
||||||
void openURL(const QString &url);
|
|
||||||
void connectionError(const QString &err);
|
|
||||||
void connectionSuccess();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onApiFailure(const XmrToResponse &doc);
|
|
||||||
|
|
||||||
QString m_baseUrl;
|
|
||||||
AppContext *m_ctx;
|
|
||||||
int m_orderTimeout = 900; // https://xmrto-api.readthedocs.io/en/latest/introduction.html#various-parameters
|
|
||||||
|
|
||||||
UtilsNetworking *m_netTor;
|
|
||||||
UtilsNetworking *m_netClear;
|
|
||||||
|
|
||||||
XmrToApi *m_api;
|
|
||||||
XmrToApi *m_apiTor;
|
|
||||||
XmrToApi *m_apiClear;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //FEATHER_XMRTOCONVERT_H
|
|
|
@ -1,76 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#include "xmrtoapi.h"
|
|
||||||
|
|
||||||
XmrToApi::XmrToApi(QObject *parent, UtilsNetworking *network, QString baseUrl)
|
|
||||||
: QObject(parent)
|
|
||||||
, m_network(network)
|
|
||||||
, m_baseUrl(std::move(baseUrl))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToApi::getRates() {
|
|
||||||
QString url = QString("%1/api/v3/xmr2btc/order_parameter_query/").arg(this->m_baseUrl);
|
|
||||||
QNetworkReply *reply = m_network->getJson(url);
|
|
||||||
connect(reply, &QNetworkReply::finished, std::bind(&XmrToApi::onResponse, this, reply, Endpoint::RATES));
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToApi::createOrder(double amount, const QString &amount_currency, const QString &dest_address) {
|
|
||||||
QJsonObject order;
|
|
||||||
order["amount"] = amount;
|
|
||||||
order["amount_currency"] = amount_currency;
|
|
||||||
order["btc_dest_address"] = dest_address;
|
|
||||||
|
|
||||||
QString url = QString("%1/api/v3/xmr2btc/order_create/").arg(m_baseUrl);
|
|
||||||
QNetworkReply *reply = m_network->postJson(url, order);
|
|
||||||
connect(reply, &QNetworkReply::finished, std::bind(&XmrToApi::onResponse, this, reply, Endpoint::ORDER_CREATE));
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToApi::getOrderStatus(const QString &uuid) {
|
|
||||||
QJsonObject order;
|
|
||||||
order["uuid"] = uuid;
|
|
||||||
|
|
||||||
QString url = QString("%1/api/v3/xmr2btc/order_status_query/").arg(m_baseUrl);
|
|
||||||
QNetworkReply *reply = m_network->postJson(url, order);
|
|
||||||
connect(reply, &QNetworkReply::finished, std::bind(&XmrToApi::onResponse, this, reply, Endpoint::ORDER_STATUS));
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToApi::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(XmrToResponse(false, endpoint, err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
emit ApiResponse(XmrToResponse(false, endpoint, "Invalid response from XMR.to"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
XmrToError xmrto_err = XmrToApi::getApiError(obj);
|
|
||||||
if (!xmrto_err.code.isEmpty()) {
|
|
||||||
emit ApiResponse(XmrToResponse(false, endpoint, m_errorMap.contains(xmrto_err.code) ? m_errorMap[xmrto_err.code] : "", xmrto_err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
emit ApiResponse(XmrToResponse(true, endpoint, "", obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
XmrToError XmrToApi::getApiError(const QJsonObject &obj) {
|
|
||||||
if (!obj.contains("error"))
|
|
||||||
return XmrToError();
|
|
||||||
|
|
||||||
QString code = obj.value("error").toString();
|
|
||||||
QString msg = obj.value("error_msg").toString();
|
|
||||||
|
|
||||||
return XmrToError(code, msg);
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#ifndef FEATHER_XMRTOAPI_H
|
|
||||||
#define FEATHER_XMRTOAPI_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "utils/networking.h"
|
|
||||||
|
|
||||||
enum Endpoint {
|
|
||||||
RATES = 0,
|
|
||||||
ORDER_CREATE,
|
|
||||||
ORDER_STATUS
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XmrToError {
|
|
||||||
explicit XmrToError(QString code = "", QString msg = "")
|
|
||||||
: code(std::move(code)), msg(std::move(msg)) {};
|
|
||||||
|
|
||||||
QString code;
|
|
||||||
QString msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XmrToResponse {
|
|
||||||
explicit XmrToResponse(bool ok, Endpoint endpoint, QString message, XmrToError error = XmrToError(), QJsonObject obj = {})
|
|
||||||
: ok(ok), endpoint(endpoint), message(std::move(message)), error(std::move(error)), obj(std::move(obj)) {};
|
|
||||||
|
|
||||||
explicit XmrToResponse(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;
|
|
||||||
XmrToError error = XmrToError();
|
|
||||||
QJsonObject obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
class XmrToApi : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit XmrToApi(QObject *parent, UtilsNetworking *network, QString baseUrl = "https://xmr.to");
|
|
||||||
|
|
||||||
void getRates();
|
|
||||||
void createOrder(double amount, const QString &amount_currency, const QString &dest_address);
|
|
||||||
void getOrderStatus(const QString &uuid);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void ApiResponse(XmrToResponse resp);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onResponse(QNetworkReply *reply, Endpoint endpoint);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static XmrToError getApiError(const QJsonObject &obj);
|
|
||||||
|
|
||||||
QString m_baseUrl;
|
|
||||||
UtilsNetworking *m_network;
|
|
||||||
|
|
||||||
// https://xmrto-api.readthedocs.io/en/latest/introduction.html#list-of-all-error-codes
|
|
||||||
const QMap<QString, QString> m_errorMap = {
|
|
||||||
{"XMRTO-ERROR-001", "internal services not available, try again later."},
|
|
||||||
{"XMRTO-ERROR-002", "malformed bitcoin address, check address validity."},
|
|
||||||
{"XMRTO-ERROR-003", "invalid bitcoin amount, check amount data type."},
|
|
||||||
{"XMRTO-ERROR-004", "bitcoin amount out of bounds, check min and max amount."},
|
|
||||||
{"XMRTO-ERROR-005", "unexpected validation error, contact support."},
|
|
||||||
{"XMRTO-ERROR-006", "requested order not found, check order UUID."},
|
|
||||||
{"XMRTO-ERROR-007", "third party service not available, try again later."},
|
|
||||||
{"XMRTO-ERROR-008", "insufficient funds available, try again later."},
|
|
||||||
{"XMRTO-ERROR-009", "invalid request, check request parameters."},
|
|
||||||
{"XMRTO-ERROR-010", "payment protocol failed, invalid or outdated data served by URL."},
|
|
||||||
{"XMRTO-ERROR-011", "malformed payment protocol url, URL is malformed or cannot be contacted."},
|
|
||||||
{"XMRTO-ERROR-012", "too many requests, try less often."},
|
|
||||||
{"XMRTO-ERROR-013", "access forbidden."},
|
|
||||||
{"XMRTO-ERROR-014", "service is not available in your region."},
|
|
||||||
{"XMRTO-ERROR-015", "invalid monero amount, check amount data type."},
|
|
||||||
{"XMRTO-ERROR-016", "invalid currency, check available currency options."},
|
|
||||||
{"XMRTO-ERROR-017", "malformed lightning network invoice, provide a correct invoice for the main network."},
|
|
||||||
{"XMRTO-ERROR-018", "lightning payment unlikely to succeed, check first if xmr.to has routes available."},
|
|
||||||
{"XMRTO-ERROR-019", "lightning invoice preimage already known, don’t use the same invoice more than once."}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //FEATHER_XMRTOAPI_H
|
|
|
@ -1,274 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#include "xmrtoorder.h"
|
|
||||||
|
|
||||||
#include "libwalletqt/Wallet.h"
|
|
||||||
#include "appcontext.h"
|
|
||||||
#include "globals.h"
|
|
||||||
|
|
||||||
XmrToOrder::XmrToOrder(AppContext *ctx, UtilsNetworking *network, QString baseUrl, bool clearnet, XmrToRates *rates, QObject *parent) :
|
|
||||||
QObject(parent),
|
|
||||||
m_ctx(ctx),
|
|
||||||
m_network(network),
|
|
||||||
m_baseUrl(std::move(baseUrl)),
|
|
||||||
m_rates(rates),
|
|
||||||
m_clearnet(clearnet) {
|
|
||||||
this->state = OrderState::Status_Idle;
|
|
||||||
|
|
||||||
m_baseUrl = m_ctx->networkType == NetworkType::Type::MAINNET ? "https://xmr.to" : "https://test.xmr.to";
|
|
||||||
m_api = new XmrToApi(this, network, m_baseUrl);
|
|
||||||
|
|
||||||
connect(m_api, &XmrToApi::ApiResponse, this, &XmrToOrder::onApiResponse);
|
|
||||||
connect(m_ctx, &AppContext::transactionCommitted, this, &XmrToOrder::onTransactionCommitted);
|
|
||||||
connect(m_ctx, &AppContext::createTransactionCancelled, this, &XmrToOrder::onTransactionCancelled);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::onTransactionCancelled(const QVector<QString> &address, double amount) {
|
|
||||||
// listener for all cancelled transactions - will try to match the exact amount to this order.
|
|
||||||
if(this->incoming_amount_total != amount || this->receiving_subaddress != address[0]) return;
|
|
||||||
|
|
||||||
this->errorMsg = "TX cancelled by user";
|
|
||||||
this->changeState(OrderState::Status_OrderFailed);
|
|
||||||
this->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid) {
|
|
||||||
// listener for all outgoing transactions - will try to match the exact amount to this order.
|
|
||||||
if(this->state == OrderState::Status_OrderUnpaid){
|
|
||||||
if(tx->amount() / globals::cdiv == this->incoming_amount_total) {
|
|
||||||
if(!status) {
|
|
||||||
this->errorMsg = "TX failed to commit";
|
|
||||||
this->changeState(OrderState::Status_OrderFailed);
|
|
||||||
this->stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->xmr_txid = txid.at(0);
|
|
||||||
this->m_paymentSent = true;
|
|
||||||
this->changeState(OrderState::Status_OrderXMRSent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::onApiFailure(const XmrToResponse &resp) {
|
|
||||||
this->errorCode = resp.error.code;
|
|
||||||
this->errorMsg = resp.message;
|
|
||||||
|
|
||||||
switch (resp.endpoint) {
|
|
||||||
case ORDER_CREATE:
|
|
||||||
this->onCreatedError();
|
|
||||||
break;
|
|
||||||
case ORDER_STATUS:
|
|
||||||
this->onCheckedError(resp.error);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::onApiResponse(const XmrToResponse& resp) {
|
|
||||||
if (!resp.ok) {
|
|
||||||
this->onApiFailure(resp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (resp.endpoint) {
|
|
||||||
case ORDER_CREATE:
|
|
||||||
this->onCreated(resp.obj);
|
|
||||||
break;
|
|
||||||
case ORDER_STATUS:
|
|
||||||
this->onChecked(resp.obj);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::create(double amount, const QString ¤cy, const QString &btcAddress) {
|
|
||||||
if(this->m_ctx->currentWallet == nullptr) {
|
|
||||||
this->errorMsg = "No wallet opened";
|
|
||||||
this->changeState(OrderState::Status_OrderFailed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_api->createOrder(amount, currency, btcAddress);
|
|
||||||
this->changeState(OrderState::Status_OrderCreating);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::onCreatedError() {
|
|
||||||
this->changeState(OrderState::Status_OrderFailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::onCreated(const QJsonObject &object) {
|
|
||||||
if(!object.contains("state"))
|
|
||||||
this->errorMsg = "Could not parse 'state' from JSON response";
|
|
||||||
if(object.value("state").toString() != "TO_BE_CREATED")
|
|
||||||
this->errorMsg = "unknown state from response, should be \"TO_BE_CREATED\"";
|
|
||||||
|
|
||||||
if(!this->errorMsg.isEmpty()) {
|
|
||||||
this->changeState(OrderState::Status_OrderFailed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m_created) return;
|
|
||||||
m_created = true;
|
|
||||||
this->btc_amount = object.value("btc_amount").toDouble();
|
|
||||||
this->btc_dest_address = object.value("btc_dest_address").toString();
|
|
||||||
this->uses_lightning = object.value("uses_lightning").toBool();
|
|
||||||
this->uuid = object.value("uuid").toString();
|
|
||||||
m_checkTimer.start(1000*5);
|
|
||||||
m_countdownTimer.start(1000);
|
|
||||||
connect(&m_checkTimer, &QTimer::timeout, this, &XmrToOrder::check);
|
|
||||||
connect(&m_countdownTimer, &QTimer::timeout, this, &XmrToOrder::onCountdown);
|
|
||||||
|
|
||||||
this->changeState(OrderState::Status_OrderToBeCreated);
|
|
||||||
this->check();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::check() {
|
|
||||||
if(this->m_ctx->currentWallet == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_api->getOrderStatus(this->uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::onCheckedError(const XmrToError& err) {
|
|
||||||
if (!err.code.isEmpty())
|
|
||||||
this->changeState(OrderState::Status_OrderFailed);
|
|
||||||
|
|
||||||
m_checkFailures += 1;
|
|
||||||
if(m_checkFailures > 15){
|
|
||||||
this->errorMsg = "Too many failed attempts";
|
|
||||||
this->changeState(OrderState::Status_OrderFailed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::onChecked(const QJsonObject &object) {
|
|
||||||
if(object.contains("btc_amount"))
|
|
||||||
this->btc_amount = object.value("btc_amount").toString().toDouble();
|
|
||||||
if(object.contains("btc_dest_address"))
|
|
||||||
this->btc_dest_address = object.value("btc_dest_address").toString();
|
|
||||||
if(object.contains("seconds_till_timeout")) {
|
|
||||||
this->seconds_till_timeout = object.value("seconds_till_timeout").toInt();
|
|
||||||
this->countdown = this->seconds_till_timeout;
|
|
||||||
}
|
|
||||||
if(object.contains("created_at"))
|
|
||||||
this->created_at = object.value("created_at").toString();
|
|
||||||
if(object.contains("expires_at"))
|
|
||||||
this->expires_at = object.value("expires_at").toString();
|
|
||||||
if(object.contains("incoming_amount_total"))
|
|
||||||
this->incoming_amount_total = object.value("incoming_amount_total").toString().toDouble();
|
|
||||||
if(object.contains("remaining_amount_incoming"))
|
|
||||||
this->remaining_amount_incoming = object.value("remaining_amount_incoming").toString().toDouble();
|
|
||||||
if(object.contains("incoming_price_btc"))
|
|
||||||
{
|
|
||||||
qDebug() << object.value("incoming_price_btc").toString();
|
|
||||||
this->incoming_price_btc = object.value("incoming_price_btc").toString().toDouble();
|
|
||||||
}
|
|
||||||
if(object.contains("receiving_subaddress"))
|
|
||||||
this->receiving_subaddress = object.value("receiving_subaddress").toString();
|
|
||||||
|
|
||||||
if(object.contains("payments")) {
|
|
||||||
// detect btc txid, xmr.to api can output several - we'll just grab the first #yolo
|
|
||||||
auto payments = object.value("payments").toArray();
|
|
||||||
for(const auto &payment: payments){
|
|
||||||
auto obj = payment.toObject();
|
|
||||||
if(obj.contains("tx_id")) {
|
|
||||||
this->btc_txid = obj.value("tx_id").toString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->changeState(object.value("state").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::changeState(const QString &_state) {
|
|
||||||
for(const auto &key: XmrTo::stateMap.keys()) {
|
|
||||||
const auto &val = XmrTo::stateMap[key];
|
|
||||||
if(_state == val){
|
|
||||||
this->changeState(key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::changeState(OrderState _state) {
|
|
||||||
if(this->m_ctx->currentWallet == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(_state == OrderState::Status_OrderUnderPaid && m_paymentSent) {
|
|
||||||
this->state = OrderState::Status_OrderXMRSent;
|
|
||||||
emit orderChanged();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_state == this->state) return;
|
|
||||||
switch(_state){
|
|
||||||
case OrderState::Status_Idle:
|
|
||||||
break;
|
|
||||||
case OrderState::Status_OrderCreating:
|
|
||||||
break;
|
|
||||||
case OrderState::Status_OrderToBeCreated:
|
|
||||||
break;
|
|
||||||
case OrderState::Status_OrderUnderPaid:
|
|
||||||
emit orderFailed(this);
|
|
||||||
this->stop();
|
|
||||||
break;
|
|
||||||
case OrderState::Status_OrderUnpaid:
|
|
||||||
// need to send Monero
|
|
||||||
if(!m_paymentRequested) {
|
|
||||||
auto unlocked_balance = m_ctx->currentWallet->unlockedBalance() / globals::cdiv;
|
|
||||||
if (this->incoming_amount_total >= unlocked_balance) {
|
|
||||||
this->state = OrderState::Status_OrderFailed;
|
|
||||||
emit orderFailed(this);
|
|
||||||
this->stop();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
m_paymentRequested = true;
|
|
||||||
emit orderPaymentRequired(this);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case OrderState::Status_OrderFailed:
|
|
||||||
emit orderFailed(this);
|
|
||||||
this->stop();
|
|
||||||
break;
|
|
||||||
case OrderState::Status_OrderPaidUnconfirmed:
|
|
||||||
emit orderPaidUnconfirmed(this);
|
|
||||||
break;
|
|
||||||
case OrderState::Status_OrderPaid:
|
|
||||||
emit orderPaid(this);
|
|
||||||
break;
|
|
||||||
case OrderState::Status_OrderTimedOut:
|
|
||||||
emit orderFailed(this);
|
|
||||||
this->stop();
|
|
||||||
break;
|
|
||||||
case OrderState::Status_OrderBTCSent:
|
|
||||||
emit orderPaid(this);
|
|
||||||
this->stop();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->state = _state;
|
|
||||||
emit orderChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::onCountdown() {
|
|
||||||
if(this->countdown <= 0) return;
|
|
||||||
this->countdown -= 1;
|
|
||||||
emit orderChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XmrToOrder::stop(){
|
|
||||||
this->m_checkTimer.stop();
|
|
||||||
this->m_countdownTimer.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
XmrToOrder::~XmrToOrder(){
|
|
||||||
this->stop();
|
|
||||||
this->disconnect();
|
|
||||||
this->m_network->deleteLater();
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#ifndef FEATHER_XMRTOORDER_H
|
|
||||||
#define FEATHER_XMRTOORDER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "utils/networking.h"
|
|
||||||
#include "PendingTransaction.h"
|
|
||||||
#include "utils/xmrtoapi.h"
|
|
||||||
|
|
||||||
enum OrderState {
|
|
||||||
Status_Idle,
|
|
||||||
Status_OrderCreating,
|
|
||||||
Status_OrderToBeCreated,
|
|
||||||
Status_OrderUnpaid,
|
|
||||||
Status_OrderXMRSent,
|
|
||||||
Status_OrderUnderPaid,
|
|
||||||
Status_OrderPaidUnconfirmed,
|
|
||||||
Status_OrderPaid,
|
|
||||||
Status_OrderBTCSent,
|
|
||||||
Status_OrderTimedOut,
|
|
||||||
Status_OrderFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XmrToRates {
|
|
||||||
double price;
|
|
||||||
double upper_limit;
|
|
||||||
double lower_limit;
|
|
||||||
double ln_upper_limit;
|
|
||||||
double ln_lower_limit;
|
|
||||||
double zero_conf_max_amount;
|
|
||||||
bool zero_conf_enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
class XmrToOrder : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit XmrToOrder(AppContext *ctx, UtilsNetworking *network, QString baseUrl, bool clearnet, XmrToRates *rates, QObject *parent = nullptr);
|
|
||||||
void create(double btcAmount, const QString ¤cy, const QString &btcAddress);
|
|
||||||
void changeState(OrderState state);
|
|
||||||
void changeState(const QString &state);
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
int state;
|
|
||||||
int countdown = -1; // seconds remaining calculated from `seconds_till_timeout`
|
|
||||||
QString uuid;
|
|
||||||
QString errorMsg;
|
|
||||||
QString errorCode;
|
|
||||||
|
|
||||||
double btc_amount = 0;
|
|
||||||
QString btc_dest_address;
|
|
||||||
QString btc_txid;
|
|
||||||
QString xmr_txid;
|
|
||||||
bool uses_lightning = false;
|
|
||||||
|
|
||||||
QString receiving_subaddress;
|
|
||||||
QString created_at;
|
|
||||||
QString expires_at;
|
|
||||||
int seconds_till_timeout = -1;
|
|
||||||
double incoming_amount_total = 0; // amount_in_incoming_currency_for_this_order_as_string
|
|
||||||
double remaining_amount_incoming; // amount_in_incoming_currency_that_the_user_must_still_send_as_string
|
|
||||||
double incoming_price_btc = 0; // price_of_1_incoming_in_btc_currency_as_offered_by_service
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void onCountdown();
|
|
||||||
void onTransactionCancelled(const QVector<QString> &address, double amount);
|
|
||||||
void onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid);
|
|
||||||
|
|
||||||
void onCreatedError();
|
|
||||||
void onChecked(const QJsonObject &object);
|
|
||||||
void onCheckedError(const XmrToError& err);
|
|
||||||
void check();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_created = false;
|
|
||||||
QString m_baseUrl;
|
|
||||||
QTimer m_checkTimer;
|
|
||||||
QTimer m_countdownTimer;
|
|
||||||
int m_checkFailures = 0;
|
|
||||||
bool m_clearnet;
|
|
||||||
bool m_paymentSent = false;
|
|
||||||
bool m_paymentRequested = false;
|
|
||||||
UtilsNetworking *m_network;
|
|
||||||
AppContext *m_ctx;
|
|
||||||
XmrToRates *m_rates;
|
|
||||||
XmrToApi *m_api;
|
|
||||||
|
|
||||||
~XmrToOrder();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void orderChanged();
|
|
||||||
void orderPaymentRequired(XmrToOrder *order);
|
|
||||||
void orderPaid(XmrToOrder *order);
|
|
||||||
void orderPaidUnconfirmed(XmrToOrder *order);
|
|
||||||
void orderFailed(XmrToOrder *order);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onCreated(const QJsonObject &object);
|
|
||||||
void onApiFailure(const XmrToResponse &resp);
|
|
||||||
void onApiResponse(const XmrToResponse &resp);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //FEATHER_XMRTOORDER_H
|
|
|
@ -1,193 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#include "xmrtowidget.h"
|
|
||||||
#include "ui_xmrtowidget.h"
|
|
||||||
#include "dialog/xmrtoinfodialog.h"
|
|
||||||
#include "mainwindow.h"
|
|
||||||
#include "globals.h"
|
|
||||||
|
|
||||||
#include <QMessageBox>
|
|
||||||
|
|
||||||
XMRToWidget::XMRToWidget(QWidget *parent) :
|
|
||||||
QWidget(parent),
|
|
||||||
ui(new Ui::XMRToWidget)
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
m_ctx = MainWindow::getContext();
|
|
||||||
|
|
||||||
QString amount_rx = R"(^\d*\.\d*$)";
|
|
||||||
QRegExp rx;
|
|
||||||
rx.setPattern(amount_rx);
|
|
||||||
QValidator *validator = new QRegExpValidator(rx, this);
|
|
||||||
ui->lineAmount->setValidator(validator);
|
|
||||||
|
|
||||||
// xmrto logo (c) binaryFate et. al. :-D
|
|
||||||
QPixmap p(":assets/images/xmrto_big.png");
|
|
||||||
ui->logo->setPixmap(p.scaled(112, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
|
||||||
|
|
||||||
ui->ratesLayout->hide();
|
|
||||||
|
|
||||||
// context menu
|
|
||||||
m_contextMenu = new QMenu();
|
|
||||||
m_showDetailsAction = m_contextMenu->addAction("Details");
|
|
||||||
m_viewOnXmrToAction = m_contextMenu->addAction("View order on XMR.to");
|
|
||||||
m_viewOnXmrToAction->setIcon(QIcon(":/assets/images/xmrto.png"));
|
|
||||||
connect(m_showDetailsAction, &QAction::triggered, this, &XMRToWidget::showInfoDialog);
|
|
||||||
connect(m_viewOnXmrToAction, &QAction::triggered, [&](){
|
|
||||||
QModelIndex index = ui->historyTable->currentIndex();
|
|
||||||
XmrToOrder *order = this->tableModel->orders->at(index.row());
|
|
||||||
emit viewOrder(order->uuid);
|
|
||||||
});
|
|
||||||
|
|
||||||
// connects
|
|
||||||
connect(ui->btnGetRates, &QPushButton::pressed, this, &XMRToWidget::onGetRates);
|
|
||||||
connect(ui->lineAmount, &QLineEdit::textChanged, this, &XMRToWidget::updateConversionLabel);
|
|
||||||
connect(ui->comboBox_currency, &QComboBox::currentTextChanged, this, &XMRToWidget::updateConversionLabel);
|
|
||||||
connect(ui->torCheckBox, &QCheckBox::stateChanged, this, &XMRToWidget::onTorCheckBoxToggled);
|
|
||||||
connect(ui->btnCreate, &QPushButton::clicked, this, &XMRToWidget::onCreateOrder);
|
|
||||||
|
|
||||||
ui->historyTable->header()->setStretchLastSection(true);
|
|
||||||
ui->historyTable->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
||||||
ui->historyTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
||||||
ui->historyTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
connect(ui->historyTable, &QTreeView::customContextMenuRequested, [&](const QPoint & point){
|
|
||||||
QModelIndex index = ui->historyTable->indexAt(point);
|
|
||||||
if (index.isValid()) {
|
|
||||||
m_contextMenu->popup(ui->historyTable->viewport()->mapToGlobal(point));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (m_ctx->isTails || m_ctx->isWhonix) {
|
|
||||||
ui->torCheckBox->setDisabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(ui->historyTable, &QTreeView::doubleClicked, this, &XMRToWidget::showInfoDialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::setHistoryModel(XmrToModel *model) {
|
|
||||||
this->tableModel = model;
|
|
||||||
this->ui->historyTable->setModel(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onBalanceUpdated(quint64 balance, quint64 spendable) {
|
|
||||||
this->m_unlockedBalance = spendable / globals::cdiv;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onWalletClosed() {
|
|
||||||
ui->lineAddress->clear();
|
|
||||||
ui->lineAmount->clear();
|
|
||||||
ui->xmrLabelEstimate->setText("0.00 XMR");
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onCreateOrder() {
|
|
||||||
// @TODO: regex verify
|
|
||||||
|
|
||||||
auto amount = ui->lineAmount->text();
|
|
||||||
if(amount.isEmpty()) {
|
|
||||||
QMessageBox::warning(this, "Cannot create XMR.To order", "Invalid amount");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double amount_num = amount.toDouble();
|
|
||||||
QString amount_cur = (ui->comboBox_currency->currentIndex() == curr::BTC) ? "BTC" : "XMR";
|
|
||||||
double amount_xmr = amount_num;
|
|
||||||
if (ui->comboBox_currency->currentIndex() == curr::BTC) {
|
|
||||||
amount_xmr = AppContext::prices->convert("BTC", "XMR", amount_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto available = m_unlockedBalance;
|
|
||||||
if(amount_xmr > available){
|
|
||||||
QMessageBox::warning(this, "Cannot create XMR.To order", "Not enough Monero to create order.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->btnGetRates->setEnabled(false);
|
|
||||||
ui->btnCreate->setEnabled(false);
|
|
||||||
|
|
||||||
auto btc_address = ui->lineAddress->text();
|
|
||||||
emit createOrder(amount_num, amount_cur, btc_address);
|
|
||||||
|
|
||||||
QTimer::singleShot(2000, [=] {
|
|
||||||
ui->lineAmount->clear();
|
|
||||||
ui->lineAddress->clear();
|
|
||||||
ui->btnGetRates->setEnabled(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onTorCheckBoxToggled(int state) {
|
|
||||||
ui->btnGetRates->setEnabled(true);
|
|
||||||
ui->btnCreate->setEnabled(false);
|
|
||||||
emit networkChanged(!state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::updateConversionLabel() {
|
|
||||||
QString amount = ui->lineAmount->text();
|
|
||||||
|
|
||||||
int curIndex = ui->comboBox_currency->currentIndex();
|
|
||||||
QString symbolFrom = (curIndex == curr::XMR) ? "XMR" : "BTC";
|
|
||||||
QString symbolTo = (curIndex == curr::XMR) ? "BTC" : "XMR";
|
|
||||||
|
|
||||||
if(amount.isEmpty()) {
|
|
||||||
ui->xmrLabelEstimate->setText(QString("0.00 %1").arg(symbolTo));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto amount_num = amount.toDouble();
|
|
||||||
auto amount_converted = AppContext::prices->convert(symbolFrom, symbolTo, amount_num);
|
|
||||||
auto amount_converted_str = QString::number(amount_converted, 'f', 2);
|
|
||||||
|
|
||||||
auto fiat_cur = config()->get(Config::preferredFiatCurrency).toString();
|
|
||||||
auto amount_fiat = AppContext::prices->convert(symbolFrom, fiat_cur, amount_num);
|
|
||||||
auto amount_fiat_str = QString::number(amount_fiat, 'f', 2);
|
|
||||||
|
|
||||||
ui->xmrLabelEstimate->setText(QString("%1 %2, %3 %4").arg(amount_converted_str, symbolTo, amount_fiat_str, fiat_cur));
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onGetRates() {
|
|
||||||
ui->btnGetRates->setEnabled(false);
|
|
||||||
ui->btnCreate->setEnabled(false);
|
|
||||||
emit getRates();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onConnectionError(QString msg) {
|
|
||||||
ui->btnGetRates->setEnabled(true);
|
|
||||||
ui->btnCreate->setEnabled(false);
|
|
||||||
msg = QString("%1\n\n%2").arg(msg).arg(m_regionBlockMessage);
|
|
||||||
QMessageBox::warning(this, "XMR.To Connection Error", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onConnectionSuccess() {
|
|
||||||
ui->btnGetRates->setEnabled(true);
|
|
||||||
ui->btnCreate->setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onRatesUpdated(XmrToRates rates) {
|
|
||||||
ui->label_rate->setText(QString("%1 BTC").arg(QString::number(rates.price)));
|
|
||||||
ui->label_minimum->setText(QString("%1 BTC").arg(QString::number(rates.lower_limit)));
|
|
||||||
ui->label_maximum->setText(QString("%1 BTC").arg(QString::number(rates.upper_limit)));
|
|
||||||
|
|
||||||
if(!m_ratesDisplayed) {
|
|
||||||
ui->ratesLayout->setVisible(true);
|
|
||||||
m_ratesDisplayed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onInitiateTransaction() {
|
|
||||||
ui->btnCreate->setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::onEndTransaction() {
|
|
||||||
ui->btnCreate->setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMRToWidget::showInfoDialog() {
|
|
||||||
QModelIndex index = ui->historyTable->currentIndex();
|
|
||||||
XmrToOrder *order = this->tableModel->orders->at(index.row());
|
|
||||||
auto *dialog = new XmrToInfoDialog(order, this);
|
|
||||||
dialog->exec();
|
|
||||||
dialog->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
XMRToWidget::~XMRToWidget() {
|
|
||||||
delete ui;
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#ifndef XMRTOWIDGET_H
|
|
||||||
#define XMRTOWIDGET_H
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
#include "widgets/tickerwidget.h"
|
|
||||||
#include "utils/xmrto.h"
|
|
||||||
#include "appcontext.h"
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class XMRToWidget;
|
|
||||||
}
|
|
||||||
|
|
||||||
class XMRToWidget : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit XMRToWidget(QWidget *parent = nullptr);
|
|
||||||
void setHistoryModel(XmrToModel *model);
|
|
||||||
~XMRToWidget() override;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void onWalletClosed();
|
|
||||||
void onGetRates();
|
|
||||||
void onConnectionError(QString msg);
|
|
||||||
void onConnectionSuccess();
|
|
||||||
void onRatesUpdated(XmrToRates rates);
|
|
||||||
void onTorCheckBoxToggled(int state);
|
|
||||||
void onCreateOrder();
|
|
||||||
void onBalanceUpdated(quint64 balance, quint64 spendable);
|
|
||||||
void updateConversionLabel();
|
|
||||||
|
|
||||||
void onInitiateTransaction();
|
|
||||||
void onEndTransaction();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void getRates();
|
|
||||||
void networkChanged(bool clearnet);
|
|
||||||
void createOrder(double btnAmount, QString currency, QString btnAddress);
|
|
||||||
void viewOrder(const QString &orderId);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void showInfoDialog();
|
|
||||||
|
|
||||||
QMap<QString, TickerWidget*> m_tickerWidgets;
|
|
||||||
QMenu *m_contextMenu;
|
|
||||||
QAction *m_viewOnXmrToAction;
|
|
||||||
QAction *m_showDetailsAction;
|
|
||||||
|
|
||||||
Ui::XMRToWidget *ui;
|
|
||||||
AppContext *m_ctx;
|
|
||||||
bool m_ratesDisplayed = false;
|
|
||||||
const QString m_regionBlockMessage = "Beware that XMR.To region blocks certain IPs, which can be problematic in combination with Tor. "
|
|
||||||
"Wait a few minutes for the circuit to switch, or disable the option to relay over Tor if the problem persists.";
|
|
||||||
double m_unlockedBalance = 0;
|
|
||||||
XmrToModel *tableModel;
|
|
||||||
|
|
||||||
enum curr {
|
|
||||||
BTC = 0,
|
|
||||||
XMR
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,327 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>XMRToWidget</class>
|
|
||||||
<widget class="QWidget" name="XMRToWidget">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>896</width>
|
|
||||||
<height>472</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
|
||||||
<item row="3" column="1">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="torCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Relay over Tor</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_4">
|
|
||||||
<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="btnGetRates">
|
|
||||||
<property name="text">
|
|
||||||
<string>Get Rates</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnCreate">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Create Order</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>Amount</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="lineAmount">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="placeholderText">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="comboBox_currency">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>BTC</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>XMR</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="xmrLabelEstimate">
|
|
||||||
<property name="text">
|
|
||||||
<string>0.00 XMR</string>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>false</bool>
|
|
||||||
</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>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="lineAddress">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="placeholderText">
|
|
||||||
<string>BTC address</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeType">
|
|
||||||
<enum>QSizePolicy::Minimum</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="logo">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>169</width>
|
|
||||||
<height>48</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>-</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Pay to</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Sunken</enum>
|
|
||||||
</property>
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="ratesLayout" native="true">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="tickerLayout">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_3">
|
|
||||||
<property name="title">
|
|
||||||
<string>Rate (1 XMR)</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>4</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_rate">
|
|
||||||
<property name="text">
|
|
||||||
<string>Rate</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
|
||||||
<property name="title">
|
|
||||||
<string>Minimum</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>4</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_minimum">
|
|
||||||
<property name="text">
|
|
||||||
<string>Minimum</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox">
|
|
||||||
<property name="title">
|
|
||||||
<string>Maximum</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>4</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_maximum">
|
|
||||||
<property name="text">
|
|
||||||
<string>Maximum</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QTreeView" name="historyTable">
|
|
||||||
<property name="rootIsDecorated">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
Loading…
Reference in a new issue