From 2f57c6663a6889135139eb8dc23233fbe4ee101e Mon Sep 17 00:00:00 2001 From: syl Date: Fri, 29 Jul 2022 13:45:28 +0200 Subject: [PATCH 01/17] Remove frontend lock when selecting an asset --- .../Dex/Wallet/SidebarItemDelegate.qml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml b/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml index 408a48545d..78b270b146 100644 --- a/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml +++ b/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml @@ -24,23 +24,16 @@ GradientRectangle { hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { - if(!can_change_ticker) return - + onClicked: + { if (mouse.button === Qt.RightButton) context_menu.popup() else api_wallet_page.ticker = ticker } - onPressAndHold: { - if(!can_change_ticker) return - - if (mouse.source === Qt.MouseEventNotSynthesized) context_menu.popup() - } + onPressAndHold: if (mouse.source === Qt.MouseEventNotSynthesized) context_menu.popup() } // Right click menu - CoinMenu { - id: context_menu - } + CoinMenu { id: context_menu } readonly property double side_margin: 16 From ff493ee65914b21bc4e5a06fe3993dd6c2b78c0c Mon Sep 17 00:00:00 2001 From: syl Date: Mon, 1 Aug 2022 12:26:55 +0200 Subject: [PATCH 02/17] Clean qml code Remove useless logs Coding style --- atomic_defi_design/Dex/Wallet/Main.qml | 34 ++++++++---------- .../Dex/Wallet/Transactions.qml | 36 +++++++++++-------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/atomic_defi_design/Dex/Wallet/Main.qml b/atomic_defi_design/Dex/Wallet/Main.qml index 21d8c7bce4..da8f8169da 100644 --- a/atomic_defi_design/Dex/Wallet/Main.qml +++ b/atomic_defi_design/Dex/Wallet/Main.qml @@ -20,16 +20,18 @@ Item { id: root property alias send_modal: send_modal - readonly property int layout_margin: 20 - readonly property string headerTitleColor: Style.colorText2 - readonly property string headerTitleFont: Style.textSizeMid1 - readonly property string headerTextColor: Dex.CurrentTheme.foregroundColor - readonly property string headerTextFont: Style.textSize - readonly property string headerSmallTitleFont: Style.textSizeSmall4 - readonly property string headerSmallFont: Style.textSizeSmall2 - readonly property string addressURL: General.getAddressExplorerURL(api_wallet_page.ticker, current_ticker_infos.address) - - function loadingPercentage(remaining) { + + readonly property int layout_margin: 20 + readonly property string headerTitleColor: Style.colorText2 + readonly property string headerTitleFont: Style.textSizeMid1 + readonly property string headerTextColor: Dex.CurrentTheme.foregroundColor + readonly property string headerTextFont: Style.textSize + readonly property string headerSmallTitleFont: Style.textSizeSmall4 + readonly property string headerSmallFont: Style.textSizeSmall2 + readonly property string addressURL: General.getAddressExplorerURL(api_wallet_page.ticker, current_ticker_infos.address) + + function loadingPercentage(remaining) + { return General.formatPercent((100 * (1 - parseFloat(remaining)/parseFloat(current_ticker_infos.current_block))).toFixed(3), false) } @@ -894,7 +896,6 @@ Item { width: parent.width height: parent.height - model: transactions_mdl.proxy_mdl } ColumnLayout @@ -929,11 +930,7 @@ Item id: explorerLink Layout.topMargin: 24 Layout.alignment: Qt.AlignHCenter - visible: - { - if (addressURL) console.log("addressURL: " + addressURL) - return api_wallet_page.tx_fetching_busy ? false : addressURL == "" ? false : api_wallet_page.tx_fetching_failed - } + visible: api_wallet_page.tx_fetching_busy ? false : root.addressURL == "" ? false : api_wallet_page.tx_fetching_failed text_value: qsTr("Click to view your address on %1 (%2) block explorer").arg(current_ticker_infos.name).arg(api_wallet_page.ticker) font.pixelSize: Style.textSize color: explorer_mouseArea.containsMouse ? Dex.CurrentTheme.textSelectionColor : Dex.CurrentTheme.foregroundColor @@ -944,10 +941,7 @@ Item cursorShape: Qt.PointingHandCursor anchors.fill: parent hoverEnabled: true - onClicked: { - console.log(addressURL) - Qt.openUrlExternally(addressURL) - } + onClicked: Qt.openUrlExternally(addressURL) } } diff --git a/atomic_defi_design/Dex/Wallet/Transactions.qml b/atomic_defi_design/Dex/Wallet/Transactions.qml index a195fc610b..30e94fa2b8 100644 --- a/atomic_defi_design/Dex/Wallet/Transactions.qml +++ b/atomic_defi_design/Dex/Wallet/Transactions.qml @@ -5,8 +5,9 @@ import QtQuick.Controls 2.15 import "../Components" import "../Constants" import Dex.Themes 1.0 as Dex +import Dex.Components 1.0 as Dex -DefaultListView +Dex.ListView { id: list @@ -18,14 +19,10 @@ DefaultListView property real _feeColumnWidth: 225 property real _dateColumnWidth: 170 - ModalLoader - { - id: tx_details_modal - sourceComponent: TransactionDetailsModal {} - } + model: transactions_mdl.proxy_mdl // Row - delegate: DexRectangle + delegate: Dex.Rectangle { id: rectangle width: list.width @@ -35,7 +32,7 @@ DefaultListView colorAnimation: false color: mouse_area.containsMouse ? Dex.CurrentTheme.buttonColorHovered : 'transparent' - DexMouseArea + Dex.MouseArea { id: mouse_area anchors.fill: parent @@ -47,14 +44,17 @@ DefaultListView } } - RowLayout { + RowLayout + { id: tx_row anchors.fill: parent anchors.margins: 15 - RowLayout { + RowLayout + { spacing: 3 Layout.preferredWidth: _categoryColumnWidth + Circle { id: note_tag @@ -70,7 +70,7 @@ DefaultListView } // Description - DexLabel + Dex.Text { id: description horizontalAlignment: Qt.AlignLeft @@ -80,7 +80,7 @@ DefaultListView } // Crypto - DexLabel + Dex.Text { id: crypto_amount Layout.preferredWidth: _cryptoColumnWidth @@ -98,7 +98,7 @@ DefaultListView } // Fiat - DexLabel + Dex.Text { Layout.preferredWidth: _fiatColumnWidth horizontalAlignment: Text.AlignRight @@ -109,7 +109,7 @@ DefaultListView } // Fee - DexLabel + Dex.Text { Layout.preferredWidth: _feeColumnWidth horizontalAlignment: Text.AlignRight @@ -120,7 +120,7 @@ DefaultListView } // Date - DexLabel + Dex.Text { Layout.preferredWidth: _dateColumnWidth horizontalAlignment: Text.AlignRight @@ -130,4 +130,10 @@ DefaultListView } } } + + ModalLoader + { + id: tx_details_modal + sourceComponent: TransactionDetailsModal {} + } } From ce29123a7ccd7ddc444391f7ceca7221ab6a0acf Mon Sep 17 00:00:00 2001 From: syl Date: Mon, 1 Aug 2022 12:33:09 +0200 Subject: [PATCH 03/17] Lock transactions model refresh --- src/core/atomicdex/pages/qt.wallet.page.cpp | 1 + src/core/atomicdex/pages/qt.wallet.page.hpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/core/atomicdex/pages/qt.wallet.page.cpp b/src/core/atomicdex/pages/qt.wallet.page.cpp index c40047d4b0..f1ea32bc8d 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.cpp +++ b/src/core/atomicdex/pages/qt.wallet.page.cpp @@ -793,6 +793,7 @@ namespace atomic_dex void wallet_page::on_tx_fetch_finished(const tx_fetch_finished& evt) { + std::unique_lock{m_update_tx_mutex}; if (!evt.with_error) { std::error_code ec; diff --git a/src/core/atomicdex/pages/qt.wallet.page.hpp b/src/core/atomicdex/pages/qt.wallet.page.hpp index 50e4e50c55..40a4a1bc78 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.hpp +++ b/src/core/atomicdex/pages/qt.wallet.page.hpp @@ -161,6 +161,8 @@ namespace atomic_dex bool m_current_ticker_fees_coin_enabled{true}; // Tells if the current ticker's fees coin is enabled. std::chrono::high_resolution_clock::time_point m_update_clock; // Clock used to time the `update()` loop of this ecs system. bool m_page_open{false}; + + mutable std::shared_mutex m_update_tx_mutex; }; } // namespace atomic_dex From 65d8a84759d9dfb5e261ba282acea579946f6605 Mon Sep 17 00:00:00 2001 From: syl Date: Mon, 1 Aug 2022 12:34:29 +0200 Subject: [PATCH 04/17] Cleanup Coding style --- src/app/app.cpp | 2 +- src/core/atomicdex/api/mm2/rpc.hpp | 2 - src/core/atomicdex/api/mm2/rpc.tx.history.hpp | 2 - .../atomicdex/api/mm2/transaction.data.cpp | 21 +- src/core/atomicdex/events/events.hpp | 10 +- ...tions.model.cpp => transactions_model.cpp} | 55 +- ...tions.model.hpp => transactions_model.hpp} | 23 +- ...model.cpp => transactions_proxy_model.cpp} | 10 +- ...model.hpp => transactions_proxy_model.hpp} | 15 +- src/core/atomicdex/pages/qt.wallet.page.cpp | 1557 ++++++++--------- src/core/atomicdex/pages/qt.wallet.page.hpp | 49 +- .../atomicdex/services/mm2/mm2.service.cpp | 8 +- .../atomicdex/services/mm2/mm2.service.hpp | 48 +- src/core/atomicdex/utilities/qt.utilities.hpp | 12 +- 14 files changed, 833 insertions(+), 981 deletions(-) rename src/core/atomicdex/models/{qt.wallet.transactions.model.cpp => transactions_model.cpp} (88%) rename src/core/atomicdex/models/{qt.wallet.transactions.model.hpp => transactions_model.hpp} (90%) rename src/core/atomicdex/models/{qt.wallet.transactions.proxy.filter.model.cpp => transactions_proxy_model.cpp} (88%) rename src/core/atomicdex/models/{qt.wallet.transactions.proxy.filter.model.hpp => transactions_proxy_model.hpp} (85%) diff --git a/src/app/app.cpp b/src/app/app.cpp index 07922b0fcc..10c85774eb 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -242,7 +242,7 @@ namespace atomic_dex if (std::find(to_init.begin(), to_init.end(), g_primary_dex_coin) != to_init.end()) { get_wallet_page()->get_transactions_mdl()->reset(); - this->dispatcher_.trigger(); + this->dispatcher_.trigger(); } get_wallet_page()->refresh_ticker_infos(); system_manager_.get_system().set_status("complete"); diff --git a/src/core/atomicdex/api/mm2/rpc.hpp b/src/core/atomicdex/api/mm2/rpc.hpp index 517420762b..1e519ae65d 100644 --- a/src/core/atomicdex/api/mm2/rpc.hpp +++ b/src/core/atomicdex/api/mm2/rpc.hpp @@ -16,10 +16,8 @@ #pragma once -// Std Headers #include -// Project Headers #include "../api.call.hpp" namespace mm2::api diff --git a/src/core/atomicdex/api/mm2/rpc.tx.history.hpp b/src/core/atomicdex/api/mm2/rpc.tx.history.hpp index b81f23b624..1ad4a60361 100644 --- a/src/core/atomicdex/api/mm2/rpc.tx.history.hpp +++ b/src/core/atomicdex/api/mm2/rpc.tx.history.hpp @@ -3,10 +3,8 @@ #include #include -//! Deps #include -//! Our Headers #include "transaction.data.hpp" namespace mm2::api diff --git a/src/core/atomicdex/api/mm2/transaction.data.cpp b/src/core/atomicdex/api/mm2/transaction.data.cpp index 4a67af8c21..0dccc35591 100644 --- a/src/core/atomicdex/api/mm2/transaction.data.cpp +++ b/src/core/atomicdex/api/mm2/transaction.data.cpp @@ -1,24 +1,16 @@ -// -// Created by Sztergbaum Roman on 08/06/2021. -// - -//! Deps #include -//! Our Headers #include "atomicdex/api/mm2/transaction.data.hpp" #include "atomicdex/utilities/global.utilities.hpp" namespace mm2::api { - void - from_json(const nlohmann::json& j, fee_regular_coin& cfg) + void from_json(const nlohmann::json& j, fee_regular_coin& cfg) { j.at("amount").get_to(cfg.amount); } - void - from_json(const nlohmann::json& j, fee_erc_coin& cfg) + void from_json(const nlohmann::json& j, fee_erc_coin& cfg) { j.at("coin").get_to(cfg.coin); j.at("gas").get_to(cfg.gas); @@ -26,8 +18,7 @@ namespace mm2::api j.at("total_fee").get_to(cfg.total_fee); } - void - from_json(const nlohmann::json& j, fee_qrc_coin& cfg) + void from_json(const nlohmann::json& j, fee_qrc_coin& cfg) { j.at("coin").get_to(cfg.coin); j.at("gas_limit").get_to(cfg.gas_limit); @@ -36,8 +27,7 @@ namespace mm2::api j.at("total_gas_fee").get_to(cfg.total_gas_fee); } - void - from_json(const nlohmann::json& j, fees_data& cfg) + void from_json(const nlohmann::json& j, fees_data& cfg) { if (j.count("amount") == 1) { @@ -56,8 +46,7 @@ namespace mm2::api } } - void - from_json(const nlohmann::json& j, transaction_data& cfg) + void from_json(const nlohmann::json& j, transaction_data& cfg) { j.at("block_height").get_to(cfg.block_height); j.at("coin").get_to(cfg.coin); diff --git a/src/core/atomicdex/events/events.hpp b/src/core/atomicdex/events/events.hpp index 7e3bd704ce..7efe5ec70f 100644 --- a/src/core/atomicdex/events/events.hpp +++ b/src/core/atomicdex/events/events.hpp @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2013-2021 The Komodo Platform Developers. * + * Copyright © 2013-2022 The Komodo Platform Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -20,7 +20,6 @@ #include #include -//! Project Headers #include namespace atomic_dex @@ -30,19 +29,12 @@ namespace atomic_dex using gui_enter_trading = entt::tag<"gui_enter_trading"_hs>; using gui_leave_trading = entt::tag<"gui_leave_trading"_hs>; using mm2_initialized = entt::tag<"mm2_running_and_enabling"_hs>; - //using tx_fetch_finished = entt::tag<"gui_tx_fetch_finished"_hs>; using default_coins_enabled = entt::tag<"default_coins_enabled"_hs>; - // using process_swaps_and_orders_finished = entt::tag<"process_swaps_and_orders_finished"_hs>; using band_oracle_refreshed = entt::tag<"band_oracle_refreshed"_hs>; using current_currency_changed = entt::tag<"update_orders_and_swap_values"_hs>; using force_update_providers = entt::tag<"force_update_providers"_hs>; using download_release_finished = entt::tag<"download_release_finished"_hs>; - struct tx_fetch_finished - { - bool with_error{false}; - }; - struct process_swaps_and_orders_finished { bool after_manual_reset{false}; diff --git a/src/core/atomicdex/models/qt.wallet.transactions.model.cpp b/src/core/atomicdex/models/transactions_model.cpp similarity index 88% rename from src/core/atomicdex/models/qt.wallet.transactions.model.cpp rename to src/core/atomicdex/models/transactions_model.cpp index 8b464b7229..26245134a0 100644 --- a/src/core/atomicdex/models/qt.wallet.transactions.model.cpp +++ b/src/core/atomicdex/models/transactions_model.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2013-2021 The Komodo Platform Developers. * + * Copyright © 2013-2022 The Komodo Platform Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -14,8 +14,7 @@ * * ******************************************************************************/ -//! Project Headers -#include "atomicdex/models/qt.wallet.transactions.model.hpp" +#include "transactions_model.hpp" #include "atomicdex/managers/qt.wallet.manager.hpp" #include "atomicdex/pages/qt.settings.page.hpp" #include "atomicdex/services/price/global.provider.hpp" @@ -38,10 +37,10 @@ namespace atomic_dex this->m_model_proxy->sort(0); } - QHash - transactions_model::roleNames() const + QHash transactions_model::roleNames() const { - return { + return + { {AmountRole, "amount"}, {AmISenderRole, "am_i_sender"}, {DateRole, "date"}, @@ -55,18 +54,16 @@ namespace atomic_dex {BlockheightRole, "blockheight"}, {ConfirmationsRole, "confirmations"}, {UnconfirmedRole, "unconfirmed"}, - {TransactionNoteRole, "transaction_note"}}; + {TransactionNoteRole, "transaction_note"} + }; } - int - transactions_model::rowCount([[maybe_unused]] const QModelIndex& parent) const + int transactions_model::rowCount([[maybe_unused]] const QModelIndex& parent) const { - // return m_model_data.size(); return static_cast(m_file_count); } - bool - atomic_dex::transactions_model::setData(const QModelIndex& index, const QVariant& value, int role) + bool atomic_dex::transactions_model::setData(const QModelIndex& index, const QVariant& value, int role) { if (!hasIndex(index.row(), index.column(), index.parent()) || !value.isValid()) { @@ -119,8 +116,7 @@ namespace atomic_dex return true; } - QVariant - transactions_model::data(const QModelIndex& index, int role) const + QVariant transactions_model::data(const QModelIndex& index, int role) const { if (!hasIndex(index.row(), index.column(), index.parent())) { @@ -182,8 +178,7 @@ namespace atomic_dex return {}; } - void - atomic_dex::transactions_model::reset() + void atomic_dex::transactions_model::reset() { this->m_file_count = 0; this->beginResetModel(); @@ -192,13 +187,11 @@ namespace atomic_dex emit lengthChanged(); } - void - transactions_model::init_transactions(const t_transactions& transactions) + void transactions_model::init_transactions(const t_transactions& transactions) { if (m_model_data.size() == 0) { SPDLOG_DEBUG("first time initialization, inserting {} transactions", transactions.size()); - //! First time insertion beginResetModel(); m_model_data = transactions; m_file_count = transactions.size() < g_file_count_limit ? transactions.size() : g_file_count_limit; @@ -228,13 +221,13 @@ namespace atomic_dex emit lengthChanged(); } - void - atomic_dex::transactions_model::update_transaction(const tx_infos& tx) + void atomic_dex::transactions_model::update_transaction(const tx_infos& tx) { if (const auto res = this->match(this->index(0, 0), TxHashRole, QString::fromStdString(tx.tx_hash)); not res.isEmpty()) { - const QModelIndex& idx = res.at(0); - quint64 timestamp = tx.timestamp; + const QModelIndex& idx = res.at(0); + quint64 timestamp = tx.timestamp; + update_value(TimestampRole, timestamp, idx, *this); update_value(DateRole, QString::fromStdString(tx.date), idx, *this); update_value(ConfirmationsRole, static_cast(tx.confirmations), idx, *this); @@ -242,8 +235,7 @@ namespace atomic_dex } } - void - atomic_dex::transactions_model::update_or_insert_transactions(const t_transactions& transactions) + void atomic_dex::transactions_model::update_or_insert_transactions(const t_transactions& transactions) { if (m_model_data.size() > transactions.size()) { @@ -285,20 +277,17 @@ namespace atomic_dex } } - int - transactions_model::get_length() const + int transactions_model::get_length() const { return rowCount(); } - transactions_proxy_model* - transactions_model::get_transactions_proxy() const + transactions_proxy_model* transactions_model::get_transactions_proxy() const { return m_model_proxy; } - void - atomic_dex::transactions_model::fetchMore(const QModelIndex& parent) + void atomic_dex::transactions_model::fetchMore(const QModelIndex& parent) { if (parent.isValid()) { @@ -317,10 +306,8 @@ namespace atomic_dex emit lengthChanged(); } - bool - atomic_dex::transactions_model::canFetchMore([[maybe_unused]] const QModelIndex& parent) const + bool atomic_dex::transactions_model::canFetchMore([[maybe_unused]] const QModelIndex& parent) const { return (m_file_count < m_model_data.size()); } - } // namespace atomic_dex diff --git a/src/core/atomicdex/models/qt.wallet.transactions.model.hpp b/src/core/atomicdex/models/transactions_model.hpp similarity index 90% rename from src/core/atomicdex/models/qt.wallet.transactions.model.hpp rename to src/core/atomicdex/models/transactions_model.hpp index ef9c453851..b76b85a8fd 100644 --- a/src/core/atomicdex/models/qt.wallet.transactions.model.hpp +++ b/src/core/atomicdex/models/transactions_model.hpp @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2013-2021 The Komodo Platform Developers. * + * Copyright © 2013-2022 The Komodo Platform Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -16,12 +16,10 @@ #pragma once -//! QT Headers #include #include -//! Project Headers -#include "atomicdex/models/qt.wallet.transactions.proxy.filter.model.hpp" +#include "transactions_proxy_model.hpp" #include "atomicdex/services/mm2/mm2.service.hpp" namespace atomic_dex @@ -29,6 +27,7 @@ namespace atomic_dex class transactions_model final : public QAbstractListModel { Q_OBJECT + Q_PROPERTY(int length READ get_length NOTIFY lengthChanged); Q_PROPERTY(transactions_proxy_model* proxy_mdl READ get_transactions_proxy NOTIFY transactionsProxyMdlChanged) @@ -37,7 +36,7 @@ namespace atomic_dex t_transactions m_model_data; std::size_t m_file_count{0}; - public: + public: enum TransactionsRoles { AmountRole = Qt::UserRole + 1, @@ -56,15 +55,15 @@ namespace atomic_dex TransactionNoteRole }; - transactions_model(ag::ecs::system_manager& system_manager, QObject* parent = nullptr) ; - ~transactions_model() final = default; + transactions_model(ag::ecs::system_manager& system_manager, QObject* parent = nullptr); + ~transactions_model() final = default; void reset(); void init_transactions(const t_transactions& transactions); void update_or_insert_transactions(const t_transactions& transactions); void update_transaction(const tx_infos& tx); - //! Override + // Override [[nodiscard]] QHash roleNames() const final; [[nodiscard]] QVariant data(const QModelIndex& index, int role) const final; [[nodiscard]] int rowCount(const QModelIndex& parent = QModelIndex()) const final; @@ -72,11 +71,11 @@ namespace atomic_dex void fetchMore(const QModelIndex& parent) final; bool canFetchMore(const QModelIndex& parent) const final; - //! Props - [[nodiscard]] int get_length() const ; - [[nodiscard]] transactions_proxy_model* get_transactions_proxy() const ; + // Getters + [[nodiscard]] int get_length() const; + [[nodiscard]] transactions_proxy_model* get_transactions_proxy() const; - signals: + signals: void lengthChanged(); void transactionsProxyMdlChanged(); }; diff --git a/src/core/atomicdex/models/qt.wallet.transactions.proxy.filter.model.cpp b/src/core/atomicdex/models/transactions_proxy_model.cpp similarity index 88% rename from src/core/atomicdex/models/qt.wallet.transactions.proxy.filter.model.cpp rename to src/core/atomicdex/models/transactions_proxy_model.cpp index d58b6da5e0..fcbc68ecbc 100644 --- a/src/core/atomicdex/models/qt.wallet.transactions.proxy.filter.model.cpp +++ b/src/core/atomicdex/models/transactions_proxy_model.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2013-2021 The Komodo Platform Developers. * + * Copyright © 2013-2022 The Komodo Platform Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -14,9 +14,8 @@ * * ******************************************************************************/ -//! Project Headers -#include "atomicdex/models/qt.wallet.transactions.proxy.filter.model.hpp" -#include "atomicdex/models/qt.wallet.transactions.model.hpp" +#include "transactions_proxy_model.hpp" +#include "transactions_model.hpp" namespace atomic_dex { @@ -24,8 +23,7 @@ namespace atomic_dex { } - bool - transactions_proxy_model::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const + bool transactions_proxy_model::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const { int role = this->sortRole(); QVariant left_data = sourceModel()->data(source_left, role); diff --git a/src/core/atomicdex/models/qt.wallet.transactions.proxy.filter.model.hpp b/src/core/atomicdex/models/transactions_proxy_model.hpp similarity index 85% rename from src/core/atomicdex/models/qt.wallet.transactions.proxy.filter.model.hpp rename to src/core/atomicdex/models/transactions_proxy_model.hpp index a27cf8705f..ff6cdf9a54 100644 --- a/src/core/atomicdex/models/qt.wallet.transactions.proxy.filter.model.hpp +++ b/src/core/atomicdex/models/transactions_proxy_model.hpp @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2013-2021 The Komodo Platform Developers. * + * Copyright © 2013-2022 The Komodo Platform Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -22,17 +22,14 @@ namespace atomic_dex { class transactions_proxy_model final : public QSortFilterProxyModel { - Q_OBJECT + Q_OBJECT - public: - //! Constructor + public: transactions_proxy_model(QObject* parent); + ~transactions_proxy_model() final = default; - //! Destructor - ~transactions_proxy_model() final = default; - - protected: - //! Override member functions + protected: + // Override [[nodiscard]] bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const final; }; } diff --git a/src/core/atomicdex/pages/qt.wallet.page.cpp b/src/core/atomicdex/pages/qt.wallet.page.cpp index f1ea32bc8d..ef9eab3a6e 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.cpp +++ b/src/core/atomicdex/pages/qt.wallet.page.cpp @@ -3,13 +3,11 @@ #include #include #include - -//! Deps #include + #include #include -//! Project Headers #include "atomicdex/api/faucet/faucet.hpp" #include "atomicdex/api/mm2/rpc.convertaddress.hpp" #include "atomicdex/api/mm2/rpc.electrum.hpp" @@ -28,12 +26,11 @@ namespace atomic_dex wallet_page::wallet_page(entt::registry& registry, ag::ecs::system_manager& system_manager, QObject* parent) : QObject(parent), system(registry), m_system_manager(system_manager), m_transactions_mdl(new transactions_model(system_manager, this)) { - this->dispatcher_.sink().connect<&wallet_page::on_tx_fetch_finished>(*this); + this->dispatcher_.sink().connect<&wallet_page::on_mm2_tx_fetch_finished>(*this); this->dispatcher_.sink().connect<&wallet_page::on_ticker_balance_updated>(*this); } - void - wallet_page::update() + void wallet_page::update() { if (!m_page_open) { @@ -49,13 +46,8 @@ namespace atomic_dex m_update_clock = std::chrono::high_resolution_clock::now(); } } -} // namespace atomic_dex -//! Private API -namespace atomic_dex -{ - void - wallet_page::check_send_availability() + void wallet_page::check_send_availability() { auto& mm2 = m_system_manager.get_system(); auto global_coins_cfg = m_system_manager.get_system().get_global_cfg(); @@ -94,955 +86,884 @@ namespace atomic_dex emit sendAvailabilityStateChanged(); emit currentTickerFeesCoinEnabledChanged(); } -} // namespace atomic_dex - -//! Getters/Setters -namespace atomic_dex -{ - QString - wallet_page::get_current_ticker() const - { - const auto& mm2_system = m_system_manager.get_system(); - return QString::fromStdString(mm2_system.get_current_ticker()); - } - - void - wallet_page::set_current_ticker(const QString& ticker) - { - auto& mm2_system = m_system_manager.get_system(); - if (mm2_system.set_current_ticker(ticker.toStdString())) - { - SPDLOG_INFO("new ticker: {}", ticker.toStdString()); - this->set_tx_fetching_busy(true); - m_transactions_mdl->reset(); - mm2_system.fetch_infos_thread(true, true); - emit currentTickerChanged(); - refresh_ticker_infos(); - check_send_availability(); - } - } - bool - wallet_page::is_rpc_claiming_busy() const + void wallet_page::refresh_ticker_infos() { - return m_is_claiming_busy.load(); + emit tickerInfosChanged(); } - void - wallet_page::set_claiming_is_busy(bool status) + void wallet_page::send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data) { - if (m_is_claiming_busy != status) + //! Preparation + this->set_send_busy(true); + nlohmann::json batch = nlohmann::json::array(); + auto& mm2_system = m_system_manager.get_system(); + const auto& ticker = mm2_system.get_current_ticker(); + t_withdraw_request withdraw_req{.coin = ticker, .to = address.toStdString(), .amount = max ? "0" : amount.toStdString(), .max = max}; + auto coin_info = mm2_system.get_coin_info(ticker); + if (with_fees) { - m_is_claiming_busy = status; - emit rpcClaimingStatusChanged(); + qDebug() << fees_data; + auto json_fees = nlohmann::json::parse(QString(QJsonDocument(QVariant(fees_data).toJsonObject()).toJson()).toStdString()); + withdraw_req.fees = t_withdraw_fees{ + .type = "UtxoFixed", + .amount = json_fees.at("fees_amount").get(), + .gas_price = json_fees.at("gas_price").get(), + .gas_limit = json_fees.at("gas_limit").get()}; + if (coin_info.coin_type == CoinType::ERC20) + { + withdraw_req.fees->type = "EthGas"; + } + else if (coin_info.coin_type == CoinType::QRC20) + { + withdraw_req.fees->type = "Qrc20Gas"; + } + else if (coin_info.has_parent_fees_ticker) + { + withdraw_req.fees->type = "otherGas"; + } } - } - - bool - wallet_page::is_claiming_faucet_busy() const - { - return m_is_claiming_faucet_busy.load(); - } - - void - wallet_page::set_claiming_faucet_is_busy(bool status) - { - if (m_is_claiming_faucet_busy != status) + nlohmann::json json_data = ::mm2::api::template_request("withdraw", true); + ::mm2::api::to_json(json_data, withdraw_req); + batch.push_back(json_data); + std::string amount_std = amount.toStdString(); + if (max) { - m_is_claiming_faucet_busy = status; - emit claimingFaucetStatusChanged(); + std::error_code ec; + amount_std = mm2_system.my_balance(ticker, ec); } - } - void - wallet_page::set_send_busy(bool status) - { - if (m_is_send_busy != status) + //! Answer + auto answer_functor = [this, coin_info, ticker, amount_std](web::http::http_response resp) { - m_is_send_busy = status; - emit sendStatusChanged(); - } - } + const auto& settings_system = m_system_manager.get_system(); + const auto& global_price_system = m_system_manager.get_system(); + const auto& current_fiat = settings_system.get_current_fiat().toStdString(); + std::string body = TO_STD_STR(resp.extract_string(true).get()); + SPDLOG_DEBUG("resp: {}", body); + if (resp.status_code() == 200 && body.find("error") == std::string::npos) + { + auto answers = nlohmann::json::parse(body); + auto withdraw_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "withdraw"); + nlohmann::json j_out = nlohmann::json::object(); + j_out["withdraw_answer"] = answers[0]["result"]; + j_out.at("withdraw_answer")["date"] = withdraw_answer.result.value().timestamp_as_date; - bool - wallet_page::is_send_busy() const - { - return m_is_send_busy.load(); - } + // Add total amount in fiat currency. + if (coin_info.coinpaprika_id == "test-coin") + { + j_out["withdraw_answer"]["total_amount_fiat"] = "0"; + } + else + { + j_out["withdraw_answer"]["total_amount_fiat"] = global_price_system.get_price_as_currency_from_amount(current_fiat, ticker, amount_std); + } - bool - wallet_page::is_broadcast_busy() const - { - return m_is_broadcast_busy.load(); - } + // Add fees amount. + if (j_out.at("withdraw_answer").at("fee_details").contains("total_fee") && !j_out.at("withdraw_answer").at("fee_details").contains("amount")) + { + j_out["withdraw_answer"]["fee_details"]["amount"] = j_out["withdraw_answer"]["fee_details"]["total_fee"]; + } + if (j_out.at("withdraw_answer").at("fee_details").contains("miner_fee") && !j_out.at("withdraw_answer").at("fee_details").contains("amount")) + { + j_out["withdraw_answer"]["fee_details"]["amount"] = j_out["withdraw_answer"]["fee_details"]["miner_fee"]; + } - void - wallet_page::set_broadcast_busy(bool status) - { - if (m_is_broadcast_busy != status) - { - m_is_broadcast_busy = status; - emit broadCastStatusChanged(); - } - } + // Add fees amount in fiat currency. + auto fee = j_out["withdraw_answer"]["fee_details"]["amount"].get(); + if (coin_info.coinpaprika_id == "test-coin") + { + j_out["withdraw_answer"]["fee_details"]["amount_fiat"] = "0"; + } + else + { + j_out["withdraw_answer"]["fee_details"]["amount_fiat"] = + global_price_system.get_price_as_currency_from_amount(current_fiat, coin_info.fees_ticker, fee); + } - bool - wallet_page::is_convert_address_busy() const - { - return m_convert_address_busy.load(); - } + this->set_rpc_send_data(nlohmann_json_object_to_qt_json_object(j_out)); + } + else + { + auto error_json = QJsonObject({{"error_code", resp.status_code()}, {"error_message", QString::fromStdString(body)}}); + this->set_rpc_send_data(error_json); + } + this->set_send_busy(false); + }; - void - wallet_page::set_convert_address_busy(bool status) - { - if (m_convert_address_busy != status) + auto error_functor = [this](pplx::task previous_task) { - m_convert_address_busy = status; - emit convertAddressBusyChanged(); - } - } + try + { + previous_task.wait(); + } + catch (const std::exception& e) + { + SPDLOG_ERROR("error caught in send: {}", e.what()); + auto error_json = QJsonObject({{"error_code", 500}, {"error_message", QString::fromStdString(e.what())}}); + this->set_rpc_send_data(error_json); + this->set_send_busy(false); + } + }; - bool - wallet_page::is_validate_address_busy() const - { - return m_validate_address_busy.load(); + //! Process + mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(error_functor); } - void - wallet_page::set_validate_address_busy(bool status) + void wallet_page::broadcast(const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount) { - if (m_validate_address_busy != status) +#if defined(__APPLE__) || defined(WIN32) || defined(_WIN32) + QSettings& settings = this->entity_registry_.ctx(); + if (settings.value("2FA").toBool()) { - m_validate_address_busy = status; - emit validateAddressBusyChanged(); + antara::gaming::core::evaluate_authentication( + "Password to send funds is required", [=](bool is_auth) { broadcast_on_auth_finished(is_auth, tx_hex, is_claiming, is_max, amount); }); } - } - - bool - atomic_dex::wallet_page::is_tx_fetching_busy() const - { - return m_tx_fetching_busy; - } - - void - atomic_dex::wallet_page::set_tx_fetching_busy(bool status) - { - if (m_tx_fetching_busy != status) + else { - m_tx_fetching_busy = status; - emit txFetchingStatusChanged(); + broadcast_on_auth_finished(true, tx_hex, is_claiming, is_max, amount); } +#else + broadcast_on_auth_finished(true, tx_hex, is_claiming, is_max, amount); +#endif } - bool - atomic_dex::wallet_page::is_tx_fetching_failed() const - { - return m_tx_fetching_failed; - } - - void - atomic_dex::wallet_page::set_tx_fetching_failed(bool status) + void wallet_page::broadcast_on_auth_finished(bool is_auth, const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount) { - if (m_tx_fetching_failed != status) + if (!is_auth) { - m_tx_fetching_failed = status; - emit txFetchingOutcomeChanged(); + m_auth_succeeded = false; + emit auth_succeededChanged(); + return; } - } - - + m_auth_succeeded = true; + emit auth_succeededChanged(); + this->set_rpc_broadcast_data(""); + this->set_broadcast_busy(true); + auto& mm2_system = m_system_manager.get_system(); + const auto& ticker = mm2_system.get_current_ticker(); + nlohmann::json batch = nlohmann::json::array(); + t_broadcast_request broadcast_request{.tx_hex = tx_hex.toStdString(), .coin = ticker}; + nlohmann::json json_data = ::mm2::api::template_request("send_raw_transaction"); + ::mm2::api::to_json(json_data, broadcast_request); + batch.push_back(json_data); - QVariant - wallet_page::get_ticker_infos() const - { - // SPDLOG_DEBUG("get_ticker_infos"); - QJsonObject obj{ - {"balance", "0"}, - {"name", "Komodo"}, - {"type", "SmartChain"}, - {"is_claimable", true}, - {"address", "foo"}, - {"minimal_balance_asking_rewards", "10.00"}, - {"explorer_url", "foo"}, - {"current_currency_ticker_price", "0.00"}, - {"change_24h", "0"}, - {"tx_state", "InProgress"}, - {"fiat_amount", "0.00"}, - {"trend_7d", QJsonArray()}, - {"fee_ticker", DEX_PRIMARY_COIN}, - {"blocks_left", 1}, - {"transactions_left", 0}, - {"current_block", 1}, - {"is_smartchain_test_coin", false}, - {"qrcode_address", ""}, - {"segwit_supported", false}, - {"is_segwit_on", false}}; - std::error_code ec; - auto& mm2_system = m_system_manager.get_system(); - if (mm2_system.is_mm2_running()) + //! Answer + auto answer_functor = [this, is_claiming, is_max, amount](web::http::http_response resp) { - auto& price_service = m_system_manager.get_system(); - const auto& settings_system = m_system_manager.get_system(); - const auto& provider = m_system_manager.get_system(); - const auto& ticker = mm2_system.get_current_ticker(); - const auto& coin_info = mm2_system.get_coin_info(ticker); - const auto& config = settings_system.get_cfg(); - obj["balance"] = QString::fromStdString(mm2_system.my_balance(ticker, ec)); - obj["name"] = QString::fromStdString(coin_info.name); - obj["type"] = QString::fromStdString(coin_info.type); - obj["segwit_supported"] = coin_info.segwit; - obj["is_segwit_on"] = coin_info.is_segwit_on; - obj["has_parent_fees_ticker"] = coin_info.has_parent_fees_ticker; - obj["fees_ticker"] = QString::fromStdString(coin_info.fees_ticker); - obj["is_claimable"] = coin_info.is_claimable; - obj["address"] = QString::fromStdString(mm2_system.address(ticker, ec)); - obj["minimal_balance_for_asking_rewards"] = QString::fromStdString(coin_info.minimal_claim_amount); - obj["explorer_url"] = QString::fromStdString(coin_info.explorer_url[0]); - obj["current_currency_ticker_price"] = QString::fromStdString(price_service.get_rate_conversion(config.current_currency, ticker, true)); - obj["change_24h"] = retrieve_change_24h(provider, coin_info, config, m_system_manager); - const auto& tx_state = mm2_system.get_tx_state(ec); - obj["tx_state"] = QString::fromStdString(tx_state.state); - obj["fiat_amount"] = QString::fromStdString(price_service.get_price_in_fiat(config.current_currency, ticker, ec)); - obj["trend_7d"] = nlohmann_json_array_to_qt_json_array(provider.get_ticker_historical(ticker)); - // SPDLOG_INFO("fee_ticker of ticker :{} is {}", ticker, coin_info.fees_ticker); - obj["fee_ticker"] = QString::fromStdString(coin_info.fees_ticker); - obj["blocks_left"] = static_cast(tx_state.blocks_left); - obj["transactions_left"] = static_cast(tx_state.transactions_left); - obj["current_block"] = static_cast(tx_state.current_block); - obj["is_smartchain_test_coin"] = coin_info.ticker == "RICK" || coin_info.ticker == "MORTY"; - std::error_code ec; - qrcodegen::QrCode qr0 = qrcodegen::QrCode::encodeText(mm2_system.address(ticker, ec).c_str(), qrcodegen::QrCode::Ecc::MEDIUM); - std::string svg = qr0.toSvgString(2); - obj["qrcode_address"] = QString::fromStdString("data:image/svg+xml;base64,") + QString::fromStdString(svg).toLocal8Bit().toBase64(); - // SPDLOG_DEBUG("is_segwit_on {} segwit: {}", coin_info.is_segwit_on, coin_info.segwit); - } - return obj; - } - - QVariant - wallet_page::get_validate_address_data() const - { - return m_validate_address_result.get(); - } - - void - wallet_page::set_validate_address_data(QVariant rpc_data) - { - auto json_result = rpc_data.toJsonObject(); - if (json_result.contains("reason")) - { - auto reason = json_result["reason"].toString(); - if (!reason.isEmpty()) + std::string body = TO_STD_STR(resp.extract_string(true).get()); + if (resp.status_code() == 200) { - if (reason.contains("Checksum verification failed")) - { - reason = tr("Checksum verification failed for %1.").arg(get_current_ticker()); - json_result["convertible"] = false; - } - else if (reason.contains("Invalid address checksum")) - { - reason = - tr("Invalid checksum for %1. Click the button to convert to mixed case address.").arg(json_result["ticker"].toString()); - json_result["convertible"] = true; - json_result["to_address_format"] = QJsonObject{{"format", "mixedcase"}}; - } - else if (reason.contains("Cashaddress address format activated for BCH, but legacy format used instead. Try to call 'convertaddress'")) - { - reason = - tr("Legacy address used for %1. Click the button to convert to a Cashaddress.").arg(json_result["ticker"].toString()); - json_result["to_address_format"] = QJsonObject{{"format", "cashaddress"}, {"network", "bitcoincash"}}; - json_result["convertible"] = true; - } - else if (reason.contains("Address must be prefixed with 0x")) - { - reason = tr("%1 address must be prefixed with 0x").arg(json_result["ticker"].toString()); - json_result["convertible"] = false; - } - else if (reason.contains("Invalid input length")) - { - reason = tr("%1 address length is invalid, please use a valid address.").arg(json_result["ticker"].toString()); - json_result["convertible"] = false; - } - else if (reason.toLower().contains("invalid address")) - { - reason = tr("%1 address is invalid.").arg(json_result["ticker"].toString()); - json_result["convertible"] = false; - } - else if (reason.contains("Invalid Checksum")) - { - reason = tr("Invalid checksum."); - json_result["convertible"] = false; - } - else if (reason.contains("has invalid prefixes")) + auto& mm2_system = m_system_manager.get_system(); + const auto& ticker = mm2_system.get_current_ticker(); + auto answers = nlohmann::json::parse(body); + // SPDLOG_INFO("broadcast answer: {}", answers.dump(4)); + if (answers[0].contains("tx_hash")) { - reason = tr("%1 address has invalid prefixes.").arg(json_result["ticker"].toString()); + this->set_rpc_broadcast_data(QString::fromStdString(answers[0].at("tx_hash").get())); + if (mm2_system.is_pin_cfg_enabled() && (not is_claiming && is_max)) + { + mm2_system.reset_fake_balance_to_zero(ticker); + } + else if (mm2_system.is_pin_cfg_enabled() && (not is_claiming && not is_max)) + { + mm2_system.decrease_fake_balance(ticker, amount.toStdString()); + } + mm2_system.fetch_infos_thread(); } else { - reason = tr("Backend error: %1").arg(reason); - json_result["convertible"] = false; + this->set_rpc_broadcast_data(QString::fromStdString(body)); } - json_result["reason"] = reason; } - } - m_validate_address_result = json_result; - emit validateAddressDataChanged(); - } - - QVariant - wallet_page::get_rpc_claiming_data() const - { - return m_claiming_rpc_result.get(); - } - - void - wallet_page::set_rpc_claiming_data(QVariant rpc_data) - { - m_claiming_rpc_result = rpc_data.toJsonObject(); - emit claimingRpcDataChanged(); - } + else + { + this->set_rpc_broadcast_data(QString::fromStdString(body)); + } + this->set_broadcast_busy(false); + }; + auto error_functor = [this](pplx::task previous_task) + { + try + { + previous_task.wait(); + } + catch (const std::exception& e) + { + SPDLOG_ERROR("error caught in broadcast finished: {}", e.what()); + this->set_rpc_broadcast_data(QString::fromStdString(e.what())); + this->set_broadcast_busy(false); + } + }; - QVariant - wallet_page::get_rpc_claiming_faucet_data() const - { - return m_claiming_rpc_faucet_result.get(); + mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(error_functor); } - void - wallet_page::set_rpc_claiming_faucet_data(QVariant rpc_data) + void wallet_page::claim_rewards() { - m_claiming_rpc_faucet_result = rpc_data.toJsonObject(); - emit claimingFaucetRpcDataChanged(); - } + this->set_claiming_is_busy(true); + nlohmann::json batch = nlohmann::json::array(); + auto& mm2_system = m_system_manager.get_system(); + std::error_code ec; + t_withdraw_request withdraw_req{.coin = "KMD", .to = mm2_system.address("KMD", ec), .amount = "0", .max = true}; + nlohmann::json json_data = ::mm2::api::template_request("withdraw", true); + ::mm2::api::to_json(json_data, withdraw_req); + batch.push_back(json_data); + json_data = ::mm2::api::template_request("kmd_rewards_info"); + batch.push_back(json_data); - QString - wallet_page::get_rpc_broadcast_data() const - { - return m_broadcast_rpc_result.get(); - } + auto answer_functor = [this](web::http::http_response resp) + { + std::string body = TO_STD_STR(resp.extract_string(true).get()); + if (resp.status_code() == static_cast(antara::app::http_code::ok) && body.find("error") == std::string::npos) + { + auto answers = nlohmann::json::parse(body); + auto withdraw_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "withdraw"); + nlohmann::json j_out = nlohmann::json::object(); + j_out["withdraw_answer"] = answers[0]["result"]; + j_out.at("withdraw_answer")["date"] = withdraw_answer.result.value().timestamp_as_date; + auto kmd_rewards_answer = ::mm2::api::process_kmd_rewards_answer(answers[1]); + j_out["kmd_rewards_info"] = kmd_rewards_answer.result; + this->set_rpc_claiming_data(nlohmann_json_object_to_qt_json_object(j_out)); + } + else + { + auto error_json = QJsonObject({{"error_code", resp.status_code()}, {"error_message", QString::fromStdString(body)}}); + this->set_rpc_claiming_data(error_json); + } + this->set_claiming_is_busy(false); + }; - void - wallet_page::set_rpc_broadcast_data(QString rpc_data) - { - m_broadcast_rpc_result = rpc_data; - emit broadcastDataChanged(); - } + auto error_functor = [this](pplx::task previous_task) + { + try + { + previous_task.wait(); + } + catch (const std::exception& e) + { + SPDLOG_ERROR("error caught in claim_rewards: {}", e.what()); + auto error_json = QJsonObject({{"error_code", 500}, {"error_message", QString::fromStdString(e.what())}}); + this->set_rpc_claiming_data(error_json); + this->set_claiming_is_busy(false); + } + }; - QVariant - wallet_page::get_rpc_send_data() const - { - return m_send_rpc_result.get(); + mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(error_functor); } - void - wallet_page::set_rpc_send_data(QVariant rpc_data) + void wallet_page::claim_faucet() { - m_send_rpc_result = rpc_data.toJsonObject(); - emit sendDataChanged(); - } + const auto& mm2_system = m_system_manager.get_system(); + const auto& ticker = mm2_system.get_current_ticker(); + const auto& coin_info = mm2_system.get_coin_info(ticker); + std::error_code ec; + faucet::api::claim_request claim_request{.coin_name = coin_info.ticker, .wallet_address = mm2_system.address(ticker, ec)}; - bool - wallet_page::has_auth_succeeded() const - { - return m_auth_succeeded; + this->set_claiming_faucet_is_busy(true); + faucet::api::claim(claim_request) + .then( + [this](web::http::http_response resp) + { + auto claim_result = faucet::api::get_claim_result(resp); + this->set_rpc_claiming_faucet_data( + QJsonObject({{"message", QString::fromStdString(claim_result.message)}, {"status", QString::fromStdString(claim_result.status)}})); + this->set_claiming_faucet_is_busy(false); + }) + .then(&handle_exception_pplx_task); } - bool - wallet_page::is_send_available() + transactions_model* wallet_page::get_transactions_mdl() const { - return m_send_available; + return m_transactions_mdl; } - QString - wallet_page::get_send_availability_state() + void wallet_page::on_ticker_balance_updated(const ticker_balance_updated&) { - return m_send_availability_state; + refresh_ticker_infos(); } - bool - wallet_page::is_current_ticker_fees_coin_enabled() + void wallet_page::on_mm2_tx_fetch_finished(const mm2_service::tx_fetch_finished& evt) { - return m_current_ticker_fees_coin_enabled; + std::unique_lock{m_update_tx_mutex}; + if (!evt.with_error) + { + std::error_code ec; + t_transactions transactions = m_system_manager.get_system().get_tx_history(ec); + if (m_transactions_mdl->rowCount() == 0) + { + //! insert all transactions + m_transactions_mdl->init_transactions(transactions); + } + else + { + //! Update tx (only unconfirmed) or insert (new tx) + m_transactions_mdl->update_or_insert_transactions(transactions); + } + if (ec) + { + this->set_tx_fetching_failed(true); + } + else + { + this->set_tx_fetching_failed(false); + } + } + else + { + this->m_transactions_mdl->reset(); + } + this->set_tx_fetching_busy(false); } - bool - wallet_page::is_page_open() const + void wallet_page::validate_address(QString address) { - return m_page_open; + auto& mm2_system = m_system_manager.get_system(); + if (mm2_system.is_mm2_running()) + { + const auto& ticker = mm2_system.get_current_ticker(); + validate_address(address, QString::fromStdString(ticker)); + } } - void - wallet_page::set_page_open(bool value) + void wallet_page::validate_address(QString address, QString ticker) { - m_page_open = value; - emit isPageOpenChanged(); - } -} // namespace atomic_dex - -//! Public api -namespace atomic_dex -{ - void - wallet_page::refresh_ticker_infos() - { - emit tickerInfosChanged(); - } - - void - wallet_page::send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data) - { - //! Preparation - this->set_send_busy(true); - nlohmann::json batch = nlohmann::json::array(); - auto& mm2_system = m_system_manager.get_system(); - const auto& ticker = mm2_system.get_current_ticker(); - t_withdraw_request withdraw_req{.coin = ticker, .to = address.toStdString(), .amount = max ? "0" : amount.toStdString(), .max = max}; - auto coin_info = mm2_system.get_coin_info(ticker); - if (with_fees) - { - qDebug() << fees_data; - auto json_fees = nlohmann::json::parse(QString(QJsonDocument(QVariant(fees_data).toJsonObject()).toJson()).toStdString()); - withdraw_req.fees = t_withdraw_fees{ - .type = "UtxoFixed", - .amount = json_fees.at("fees_amount").get(), - .gas_price = json_fees.at("gas_price").get(), - .gas_limit = json_fees.at("gas_limit").get()}; - if (coin_info.coin_type == CoinType::ERC20) - { - withdraw_req.fees->type = "EthGas"; - } - else if (coin_info.coin_type == CoinType::QRC20) - { - withdraw_req.fees->type = "Qrc20Gas"; - } - else if (coin_info.has_parent_fees_ticker) - { - withdraw_req.fees->type = "otherGas"; - } - } - nlohmann::json json_data = ::mm2::api::template_request("withdraw", true); - ::mm2::api::to_json(json_data, withdraw_req); - // SPDLOG_DEBUG("final json: {}", json_data.dump(4)); - batch.push_back(json_data); - std::string amount_std = amount.toStdString(); - if (max) - { - std::error_code ec; - amount_std = mm2_system.my_balance(ticker, ec); - } - - //! Answer - auto answer_functor = [this, coin_info, ticker, amount_std](web::http::http_response resp) + auto& mm2_system = m_system_manager.get_system(); + if (mm2_system.is_mm2_running()) { - const auto& settings_system = m_system_manager.get_system(); - const auto& global_price_system = m_system_manager.get_system(); - const auto& current_fiat = settings_system.get_current_fiat().toStdString(); - std::string body = TO_STD_STR(resp.extract_string(true).get()); - SPDLOG_DEBUG("resp: {}", body); - if (resp.status_code() == 200 && body.find("error") == std::string::npos) + std::error_code ec; + t_validate_address_request req{.coin = ticker.toStdString(), .address = address.toStdString()}; + this->set_validate_address_busy(true); + nlohmann::json batch = nlohmann::json::array(); + nlohmann::json json_data = ::mm2::api::template_request("validateaddress"); + ::mm2::api::to_json(json_data, req); + batch.push_back(json_data); + auto answer_functor = [this, ticker](web::http::http_response resp) { - auto answers = nlohmann::json::parse(body); - auto withdraw_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "withdraw"); - nlohmann::json j_out = nlohmann::json::object(); - j_out["withdraw_answer"] = answers[0]["result"]; - j_out.at("withdraw_answer")["date"] = withdraw_answer.result.value().timestamp_as_date; - - // Add total amount in fiat currency. - if (coin_info.coinpaprika_id == "test-coin") - { - j_out["withdraw_answer"]["total_amount_fiat"] = "0"; - } - else - { - j_out["withdraw_answer"]["total_amount_fiat"] = global_price_system.get_price_as_currency_from_amount(current_fiat, ticker, amount_std); - } - - // Add fees amount. - if (j_out.at("withdraw_answer").at("fee_details").contains("total_fee") && !j_out.at("withdraw_answer").at("fee_details").contains("amount")) - { - j_out["withdraw_answer"]["fee_details"]["amount"] = j_out["withdraw_answer"]["fee_details"]["total_fee"]; - } - if (j_out.at("withdraw_answer").at("fee_details").contains("miner_fee") && !j_out.at("withdraw_answer").at("fee_details").contains("amount")) - { - j_out["withdraw_answer"]["fee_details"]["amount"] = j_out["withdraw_answer"]["fee_details"]["miner_fee"]; - } - - // Add fees amount in fiat currency. - auto fee = j_out["withdraw_answer"]["fee_details"]["amount"].get(); - if (coin_info.coinpaprika_id == "test-coin") + std::string body = TO_STD_STR(resp.extract_string(true).get()); + SPDLOG_DEBUG("resp validateaddress: {}", body); + nlohmann::json j_out = nlohmann::json::object(); + j_out["ticker"] = ticker.toStdString(); + if (resp.status_code() == static_cast(antara::app::http_code::ok)) { - j_out["withdraw_answer"]["fee_details"]["amount_fiat"] = "0"; + auto answers = nlohmann::json::parse(body); + auto validate_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "validateaddress"); + if (validate_answer.result.has_value()) + { + auto res = validate_answer.result.value(); + j_out["is_valid"] = res.is_valid; + j_out["reason"] = res.reason.value_or(""); + } + else + { + j_out["is_valid"] = false; + j_out["reason"] = "valideaddress unknown error"; + } } else { - j_out["withdraw_answer"]["fee_details"]["amount_fiat"] = - global_price_system.get_price_as_currency_from_amount(current_fiat, coin_info.fees_ticker, fee); + j_out["is_valid"] = false; + j_out["reason"] = "valideaddress unknown error"; } - - this->set_rpc_send_data(nlohmann_json_object_to_qt_json_object(j_out)); - } - else - { - auto error_json = QJsonObject({{"error_code", resp.status_code()}, {"error_message", QString::fromStdString(body)}}); - this->set_rpc_send_data(error_json); - } - this->set_send_busy(false); - }; - - auto error_functor = [this](pplx::task previous_task) - { - try - { - previous_task.wait(); - } - catch (const std::exception& e) - { - SPDLOG_ERROR("error caught in send: {}", e.what()); - auto error_json = QJsonObject({{"error_code", 500}, {"error_message", QString::fromStdString(e.what())}}); - this->set_rpc_send_data(error_json); - this->set_send_busy(false); - } - }; - - //! Process - mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(error_functor); + this->set_validate_address_data(nlohmann_json_object_to_qt_json_object(j_out)); + this->set_validate_address_busy(false); + }; + mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(&handle_exception_pplx_task); + } } - void - wallet_page::broadcast(const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount) + void wallet_page::convert_address(QString from, QVariant to_address_format) { -#if defined(__APPLE__) || defined(WIN32) || defined(_WIN32) - QSettings& settings = this->entity_registry_.ctx(); - if (settings.value("2FA").toBool()) - { - antara::gaming::core::evaluate_authentication( - "Password to send funds is required", [=](bool is_auth) { broadcast_on_auth_finished(is_auth, tx_hex, is_claiming, is_max, amount); }); - } - else + auto& mm2_system = m_system_manager.get_system(); + if (mm2_system.is_mm2_running()) { - broadcast_on_auth_finished(true, tx_hex, is_claiming, is_max, amount); + const auto& ticker = mm2_system.get_current_ticker(); + convert_address(from, QString::fromStdString(ticker), to_address_format); } -#else - broadcast_on_auth_finished(true, tx_hex, is_claiming, is_max, amount); -#endif } - void - wallet_page::broadcast_on_auth_finished(bool is_auth, const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount) + void wallet_page::convert_address(QString from, QString ticker, QVariant to_address_format) { - if (!is_auth) + auto& mm2_system = m_system_manager.get_system(); + if (mm2_system.is_mm2_running()) { - m_auth_succeeded = false; - emit auth_succeededChanged(); - return; + QVariantMap out = to_address_format.value(); + auto address_fmt = nlohmann::json::parse(QJsonDocument::fromVariant(out).toJson().toStdString()); + t_convert_address_request req{.coin = ticker.toStdString(), .from = from.toStdString(), .to_address_format = address_fmt}; + this->set_convert_address_busy(true); + nlohmann::json batch = nlohmann::json::array(); + nlohmann::json json_data = ::mm2::api::template_request("convertaddress"); + ::mm2::api::to_json(json_data, req); + batch.push_back(json_data); + auto answer_functor = [this](web::http::http_response resp) + { + std::string body = TO_STD_STR(resp.extract_string(true).get()); + SPDLOG_DEBUG("resp convertaddress: {}", body); + if (resp.status_code() == static_cast(antara::app::http_code::ok)) + { + auto answers = nlohmann::json::parse(body); + auto convert_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "convertaddress"); + if (convert_answer.result.has_value()) + { + auto res = QString::fromStdString(convert_answer.result.value().address); + this->set_converted_address(res); + } + } + this->set_convert_address_busy(false); + }; + mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(&handle_exception_pplx_task); } - m_auth_succeeded = true; - emit auth_succeededChanged(); - this->set_rpc_broadcast_data(""); - this->set_broadcast_busy(true); - auto& mm2_system = m_system_manager.get_system(); - const auto& ticker = mm2_system.get_current_ticker(); - nlohmann::json batch = nlohmann::json::array(); - t_broadcast_request broadcast_request{.tx_hex = tx_hex.toStdString(), .coin = ticker}; - nlohmann::json json_data = ::mm2::api::template_request("send_raw_transaction"); - ::mm2::api::to_json(json_data, broadcast_request); - batch.push_back(json_data); + } - //! Answer - auto answer_functor = [this, is_claiming, is_max, amount](web::http::http_response resp) + QString wallet_page::switch_address_mode(bool checked) + { + auto& mm2_system = m_system_manager.get_system(); + std::string address = ""; + if (mm2_system.is_mm2_running()) { - std::string body = TO_STD_STR(resp.extract_string(true).get()); - if (resp.status_code() == 200) + const auto ticker = get_current_ticker().toStdString(); + const auto coin_cfg = mm2_system.get_coin_info(ticker); + if (coin_cfg.segwit) { - auto& mm2_system = m_system_manager.get_system(); - const auto& ticker = mm2_system.get_current_ticker(); - auto answers = nlohmann::json::parse(body); - // SPDLOG_INFO("broadcast answer: {}", answers.dump(4)); - if (answers[0].contains("tx_hash")) + nlohmann::json address_format = nlohmann::json::object(); + address_format = {{"format", "segwit"}}; + if (!checked) { - this->set_rpc_broadcast_data(QString::fromStdString(answers[0].at("tx_hash").get())); - if (mm2_system.is_pin_cfg_enabled() && (not is_claiming && is_max)) + //! We go from segwit to legacy + if (coin_cfg.ticker != "BCH") { - mm2_system.reset_fake_balance_to_zero(ticker); + address_format = {{"format", "standard"}}; } - else if (mm2_system.is_pin_cfg_enabled() && (not is_claiming && not is_max)) + else { - mm2_system.decrease_fake_balance(ticker, amount.toStdString()); + address_format = {{"format", "bch"}}; } - mm2_system.fetch_infos_thread(); - } - else - { - this->set_rpc_broadcast_data(QString::fromStdString(body)); } - } - else - { - this->set_rpc_broadcast_data(QString::fromStdString(body)); - } - this->set_broadcast_busy(false); - }; - auto error_functor = [this](pplx::task previous_task) - { - try - { - previous_task.wait(); + + std::error_code ec; + address = mm2_system.address(ticker, ec); + t_convert_address_request req{.coin = ticker, .from = address, .to_address_format = address_format}; + nlohmann::json batch = nlohmann::json::array(); + nlohmann::json json_data = ::mm2::api::template_request("convertaddress"); + ::mm2::api::to_json(json_data, req); + batch.push_back(json_data); + json_data["userpass"] = "******"; + web::http::http_response resp = mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).get(); + std::string body = TO_STD_STR(resp.extract_string(true).get()); + SPDLOG_DEBUG("resp convertaddress: {}", body); + if (resp.status_code() == static_cast(antara::app::http_code::ok)) + { + auto answers = nlohmann::json::parse(body); + auto convert_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "convertaddress"); + if (convert_answer.result.has_value()) + { + return QString::fromStdString(convert_answer.result.value().address); + } + } } - catch (const std::exception& e) + } + return QString::fromStdString(address); + } + + void wallet_page::post_switch_address_mode(bool is_segwit) + { + auto& mm2_system = m_system_manager.get_system(); + if (mm2_system.is_mm2_running()) + { + //! Need disable + enable + refresh balance + refresh current coin info (address) + change segwit in cfg + const auto ticker = get_current_ticker().toStdString(); + nlohmann::json batch = nlohmann::json::array(); + nlohmann::json json_data = ::mm2::api::template_request("disable_coin"); + t_disable_coin_request req{.coin = ticker}; + ::mm2::api::to_json(json_data, req); + batch.push_back(json_data); + //! Disable is in the batch + + //! electrum + auto coin_info = mm2_system.get_coin_info(ticker); + t_electrum_request electrum_req{ + .coin_name = coin_info.ticker, .servers = coin_info.electrum_urls.value(), .coin_type = coin_info.coin_type, .with_tx_history = true}; + if (is_segwit) { - SPDLOG_ERROR("error caught in broadcast finished: {}", e.what()); - this->set_rpc_broadcast_data(QString::fromStdString(e.what())); - this->set_broadcast_busy(false); + electrum_req.address_format = nlohmann::json::object(); + electrum_req.address_format.value()["format"] = "segwit"; } - }; + nlohmann::json electrum_data = ::mm2::api::template_request("electrum"); + ::mm2::api::to_json(electrum_data, electrum_req); + batch.push_back(electrum_data); + electrum_data["userpass"] = "*******"; - mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(error_functor); + //! Answer functor + auto answer_functor = [this, ticker, is_segwit](web::http::http_response resp) + { + std::string body = TO_STD_STR(resp.extract_string(true).get()); + SPDLOG_DEBUG("resp disable/enable: {}", body); + if (resp.status_code() == static_cast(antara::app::http_code::ok)) + { + auto& mm2_system = m_system_manager.get_system(); + mm2_system.change_segwit_status(ticker, is_segwit); + mm2_system.fetch_infos_thread(true, false); + } + }; + + //! Rpc processing + mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(&handle_exception_pplx_task); + } } +} // namespace atomic_dex - void - wallet_page::claim_rewards() +// Getters/Setters +namespace atomic_dex +{ + QString wallet_page::get_current_ticker() const { - this->set_claiming_is_busy(true); - nlohmann::json batch = nlohmann::json::array(); - auto& mm2_system = m_system_manager.get_system(); - std::error_code ec; - t_withdraw_request withdraw_req{.coin = "KMD", .to = mm2_system.address("KMD", ec), .amount = "0", .max = true}; - nlohmann::json json_data = ::mm2::api::template_request("withdraw", true); - ::mm2::api::to_json(json_data, withdraw_req); - batch.push_back(json_data); - json_data = ::mm2::api::template_request("kmd_rewards_info"); - batch.push_back(json_data); + const auto& mm2_system = m_system_manager.get_system(); + return QString::fromStdString(mm2_system.get_current_ticker()); + } - auto answer_functor = [this](web::http::http_response resp) + void wallet_page::set_current_ticker(const QString& ticker) + { + auto& mm2_system = m_system_manager.get_system(); + if (mm2_system.set_current_ticker(ticker.toStdString())) { - std::string body = TO_STD_STR(resp.extract_string(true).get()); - // SPDLOG_DEBUG("resp claiming: {}", body); - if (resp.status_code() == static_cast(antara::app::http_code::ok) && body.find("error") == std::string::npos) - { - auto answers = nlohmann::json::parse(body); - auto withdraw_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "withdraw"); - nlohmann::json j_out = nlohmann::json::object(); - j_out["withdraw_answer"] = answers[0]["result"]; - j_out.at("withdraw_answer")["date"] = withdraw_answer.result.value().timestamp_as_date; - auto kmd_rewards_answer = ::mm2::api::process_kmd_rewards_answer(answers[1]); - j_out["kmd_rewards_info"] = kmd_rewards_answer.result; - this->set_rpc_claiming_data(nlohmann_json_object_to_qt_json_object(j_out)); - } - else - { - auto error_json = QJsonObject({{"error_code", resp.status_code()}, {"error_message", QString::fromStdString(body)}}); - this->set_rpc_claiming_data(error_json); - } - this->set_claiming_is_busy(false); - }; + SPDLOG_INFO("new ticker: {}", ticker.toStdString()); + this->set_tx_fetching_busy(true); + m_transactions_mdl->reset(); + mm2_system.fetch_infos_thread(true, true); + emit currentTickerChanged(); + refresh_ticker_infos(); + check_send_availability(); + } + } - auto error_functor = [this](pplx::task previous_task) + bool wallet_page::is_rpc_claiming_busy() const + { + return m_is_claiming_busy.load(); + } + + void wallet_page::set_claiming_is_busy(bool status) + { + if (m_is_claiming_busy != status) { - try - { - previous_task.wait(); - } - catch (const std::exception& e) + m_is_claiming_busy = status; + emit rpcClaimingStatusChanged(); + } + } + + bool wallet_page::is_claiming_faucet_busy() const + { + return m_is_claiming_faucet_busy.load(); + } + + void wallet_page::set_claiming_faucet_is_busy(bool status) + { + if (m_is_claiming_faucet_busy != status) + { + m_is_claiming_faucet_busy = status; + emit claimingFaucetStatusChanged(); + } + } + + void wallet_page::set_send_busy(bool status) + { + if (m_is_send_busy != status) + { + m_is_send_busy = status; + emit sendStatusChanged(); + } + } + + bool wallet_page::is_send_busy() const + { + return m_is_send_busy.load(); + } + + bool wallet_page::is_broadcast_busy() const + { + return m_is_broadcast_busy.load(); + } + + void wallet_page::set_broadcast_busy(bool status) + { + if (m_is_broadcast_busy != status) + { + m_is_broadcast_busy = status; + emit broadCastStatusChanged(); + } + } + + bool wallet_page::is_convert_address_busy() const + { + return m_convert_address_busy.load(); + } + + void wallet_page::set_convert_address_busy(bool status) + { + if (m_convert_address_busy != status) + { + m_convert_address_busy = status; + emit convertAddressBusyChanged(); + } + } + + bool wallet_page::is_validate_address_busy() const + { + return m_validate_address_busy.load(); + } + + void wallet_page::set_validate_address_busy(bool status) + { + if (m_validate_address_busy != status) + { + m_validate_address_busy = status; + emit validateAddressBusyChanged(); + } + } + + QString wallet_page::get_converted_address() const + { + return m_converted_address.get(); + } + + void wallet_page::set_converted_address(QString converted_address) + { + m_converted_address = converted_address; + emit convertedAddressChanged(); + } + + bool atomic_dex::wallet_page::is_tx_fetching_busy() const + { + return m_tx_fetching_busy; + } + + void atomic_dex::wallet_page::set_tx_fetching_busy(bool status) + { + if (m_tx_fetching_busy != status) + { + m_tx_fetching_busy = status; + emit txFetchingStatusChanged(); + } + } + + bool atomic_dex::wallet_page::is_tx_fetching_failed() const + { + return m_tx_fetching_failed; + } + + void atomic_dex::wallet_page::set_tx_fetching_failed(bool status) + { + if (m_tx_fetching_failed != status) + { + m_tx_fetching_failed = status; + emit txFetchingOutcomeChanged(); + } + } + + QVariant wallet_page::get_ticker_infos() const + { + QJsonObject obj{ + {"balance", "0"}, + {"name", "Komodo"}, + {"type", "SmartChain"}, + {"is_claimable", true}, + {"address", "foo"}, + {"minimal_balance_asking_rewards", "10.00"}, + {"explorer_url", "foo"}, + {"current_currency_ticker_price", "0.00"}, + {"change_24h", "0"}, + {"tx_state", "InProgress"}, + {"fiat_amount", "0.00"}, + {"trend_7d", QJsonArray()}, + {"fee_ticker", DEX_PRIMARY_COIN}, + {"blocks_left", 1}, + {"transactions_left", 0}, + {"current_block", 1}, + {"is_smartchain_test_coin", false}, + {"qrcode_address", ""}, + {"segwit_supported", false}, + {"is_segwit_on", false}}; + std::error_code ec; + auto& mm2_system = m_system_manager.get_system(); + if (mm2_system.is_mm2_running()) + { + auto& price_service = m_system_manager.get_system(); + const auto& settings_system = m_system_manager.get_system(); + const auto& provider = m_system_manager.get_system(); + const auto& ticker = mm2_system.get_current_ticker(); + const auto& coin_info = mm2_system.get_coin_info(ticker); + const auto& config = settings_system.get_cfg(); + obj["balance"] = QString::fromStdString(mm2_system.my_balance(ticker, ec)); + obj["name"] = QString::fromStdString(coin_info.name); + obj["type"] = QString::fromStdString(coin_info.type); + obj["segwit_supported"] = coin_info.segwit; + obj["is_segwit_on"] = coin_info.is_segwit_on; + obj["has_parent_fees_ticker"] = coin_info.has_parent_fees_ticker; + obj["fees_ticker"] = QString::fromStdString(coin_info.fees_ticker); + obj["is_claimable"] = coin_info.is_claimable; + obj["address"] = QString::fromStdString(mm2_system.address(ticker, ec)); + obj["minimal_balance_for_asking_rewards"] = QString::fromStdString(coin_info.minimal_claim_amount); + obj["explorer_url"] = QString::fromStdString(coin_info.explorer_url[0]); + obj["current_currency_ticker_price"] = QString::fromStdString(price_service.get_rate_conversion(config.current_currency, ticker, true)); + obj["change_24h"] = retrieve_change_24h(provider, coin_info, config, m_system_manager); + const auto& tx_state = mm2_system.get_tx_state(ec); + obj["tx_state"] = QString::fromStdString(tx_state.state); + obj["fiat_amount"] = QString::fromStdString(price_service.get_price_in_fiat(config.current_currency, ticker, ec)); + obj["trend_7d"] = nlohmann_json_array_to_qt_json_array(provider.get_ticker_historical(ticker)); + obj["fee_ticker"] = QString::fromStdString(coin_info.fees_ticker); + obj["blocks_left"] = static_cast(tx_state.blocks_left); + obj["transactions_left"] = static_cast(tx_state.transactions_left); + obj["current_block"] = static_cast(tx_state.current_block); + obj["is_smartchain_test_coin"] = coin_info.ticker == "RICK" || coin_info.ticker == "MORTY"; + std::error_code ec; + qrcodegen::QrCode qr0 = qrcodegen::QrCode::encodeText(mm2_system.address(ticker, ec).c_str(), qrcodegen::QrCode::Ecc::MEDIUM); + std::string svg = qr0.toSvgString(2); + obj["qrcode_address"] = QString::fromStdString("data:image/svg+xml;base64,") + QString::fromStdString(svg).toLocal8Bit().toBase64(); + } + return obj; + } + + QVariant wallet_page::get_validate_address_data() const + { + return m_validate_address_result.get(); + } + + void wallet_page::set_validate_address_data(QVariant rpc_data) + { + auto json_result = rpc_data.toJsonObject(); + if (json_result.contains("reason")) + { + auto reason = json_result["reason"].toString(); + if (!reason.isEmpty()) { - SPDLOG_ERROR("error caught in claim_rewards: {}", e.what()); - auto error_json = QJsonObject({{"error_code", 500}, {"error_message", QString::fromStdString(e.what())}}); - this->set_rpc_claiming_data(error_json); - this->set_claiming_is_busy(false); + if (reason.contains("Checksum verification failed")) + { + reason = tr("Checksum verification failed for %1.").arg(get_current_ticker()); + json_result["convertible"] = false; + } + else if (reason.contains("Invalid address checksum")) + { + reason = + tr("Invalid checksum for %1. Click the button to convert to mixed case address.").arg(json_result["ticker"].toString()); + json_result["convertible"] = true; + json_result["to_address_format"] = QJsonObject{{"format", "mixedcase"}}; + } + else if (reason.contains("Cashaddress address format activated for BCH, but legacy format used instead. Try to call 'convertaddress'")) + { + reason = + tr("Legacy address used for %1. Click the button to convert to a Cashaddress.").arg(json_result["ticker"].toString()); + json_result["to_address_format"] = QJsonObject{{"format", "cashaddress"}, {"network", "bitcoincash"}}; + json_result["convertible"] = true; + } + else if (reason.contains("Address must be prefixed with 0x")) + { + reason = tr("%1 address must be prefixed with 0x").arg(json_result["ticker"].toString()); + json_result["convertible"] = false; + } + else if (reason.contains("Invalid input length")) + { + reason = tr("%1 address length is invalid, please use a valid address.").arg(json_result["ticker"].toString()); + json_result["convertible"] = false; + } + else if (reason.toLower().contains("invalid address")) + { + reason = tr("%1 address is invalid.").arg(json_result["ticker"].toString()); + json_result["convertible"] = false; + } + else if (reason.contains("Invalid Checksum")) + { + reason = tr("Invalid checksum."); + json_result["convertible"] = false; + } + else if (reason.contains("has invalid prefixes")) + { + reason = tr("%1 address has invalid prefixes.").arg(json_result["ticker"].toString()); + } + else + { + reason = tr("Backend error: %1").arg(reason); + json_result["convertible"] = false; + } + json_result["reason"] = reason; } - }; - - mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(error_functor); + } + m_validate_address_result = json_result; + emit validateAddressDataChanged(); } - void - wallet_page ::claim_faucet() + QVariant wallet_page::get_rpc_claiming_data() const { - const auto& mm2_system = m_system_manager.get_system(); - const auto& ticker = mm2_system.get_current_ticker(); - const auto& coin_info = mm2_system.get_coin_info(ticker); - std::error_code ec; - faucet::api::claim_request claim_request{.coin_name = coin_info.ticker, .wallet_address = mm2_system.address(ticker, ec)}; - - this->set_claiming_faucet_is_busy(true); - faucet::api::claim(claim_request) - .then( - [this](web::http::http_response resp) - { - auto claim_result = faucet::api::get_claim_result(resp); - this->set_rpc_claiming_faucet_data( - QJsonObject({{"message", QString::fromStdString(claim_result.message)}, {"status", QString::fromStdString(claim_result.status)}})); - this->set_claiming_faucet_is_busy(false); - }) - .then(&handle_exception_pplx_task); + return m_claiming_rpc_result.get(); } - transactions_model* - wallet_page::get_transactions_mdl() const + void wallet_page::set_rpc_claiming_data(QVariant rpc_data) { - return m_transactions_mdl; + m_claiming_rpc_result = rpc_data.toJsonObject(); + emit claimingRpcDataChanged(); } - void - wallet_page::on_ticker_balance_updated(const ticker_balance_updated&) + QVariant wallet_page::get_rpc_claiming_faucet_data() const { - refresh_ticker_infos(); + return m_claiming_rpc_faucet_result.get(); } - void - wallet_page::on_tx_fetch_finished(const tx_fetch_finished& evt) + void wallet_page::set_rpc_claiming_faucet_data(QVariant rpc_data) { - std::unique_lock{m_update_tx_mutex}; - if (!evt.with_error) - { - std::error_code ec; - t_transactions transactions = m_system_manager.get_system().get_tx_history(ec); - // SPDLOG_INFO("transaction size: {}", transactions.size()); - if (m_transactions_mdl->rowCount() == 0) - { - //! insert all transactions - m_transactions_mdl->init_transactions(transactions); - } - else - { - //! Update tx (only unconfirmed) or insert (new tx) - // SPDLOG_DEBUG("updating / insert tx"); - m_transactions_mdl->update_or_insert_transactions(transactions); - } - if (ec) - { - this->set_tx_fetching_failed(true); - } - else - { - this->set_tx_fetching_failed(false); - } - } - else - { - this->m_transactions_mdl->reset(); - } - this->set_tx_fetching_busy(false); + m_claiming_rpc_faucet_result = rpc_data.toJsonObject(); + emit claimingFaucetRpcDataChanged(); } - void - wallet_page::validate_address(QString address) + QString wallet_page::get_rpc_broadcast_data() const { - auto& mm2_system = m_system_manager.get_system(); - if (mm2_system.is_mm2_running()) - { - const auto& ticker = mm2_system.get_current_ticker(); - validate_address(address, QString::fromStdString(ticker)); - } + return m_broadcast_rpc_result.get(); } - void - wallet_page::validate_address(QString address, QString ticker) + void wallet_page::set_rpc_broadcast_data(QString rpc_data) { - SPDLOG_INFO("validate_address: {} - ticker: {}", address.toStdString(), ticker.toStdString()); - auto& mm2_system = m_system_manager.get_system(); - if (mm2_system.is_mm2_running()) - { - std::error_code ec; - t_validate_address_request req{.coin = ticker.toStdString(), .address = address.toStdString()}; - this->set_validate_address_busy(true); - nlohmann::json batch = nlohmann::json::array(); - nlohmann::json json_data = ::mm2::api::template_request("validateaddress"); - ::mm2::api::to_json(json_data, req); - batch.push_back(json_data); - auto answer_functor = [this, ticker](web::http::http_response resp) - { - std::string body = TO_STD_STR(resp.extract_string(true).get()); - SPDLOG_DEBUG("resp validateaddress: {}", body); - nlohmann::json j_out = nlohmann::json::object(); - j_out["ticker"] = ticker.toStdString(); - if (resp.status_code() == static_cast(antara::app::http_code::ok)) - { - auto answers = nlohmann::json::parse(body); - auto validate_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "validateaddress"); - if (validate_answer.result.has_value()) - { - auto res = validate_answer.result.value(); - j_out["is_valid"] = res.is_valid; - j_out["reason"] = res.reason.value_or(""); - } - else - { - j_out["is_valid"] = false; - j_out["reason"] = "valideaddress unknown error"; - } - } - else - { - j_out["is_valid"] = false; - j_out["reason"] = "valideaddress unknown error"; - } - this->set_validate_address_data(nlohmann_json_object_to_qt_json_object(j_out)); - this->set_validate_address_busy(false); - }; - mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(&handle_exception_pplx_task); - } + m_broadcast_rpc_result = rpc_data; + emit broadcastDataChanged(); } - void - wallet_page::convert_address(QString from, QVariant to_address_format) + QVariant wallet_page::get_rpc_send_data() const { - auto& mm2_system = m_system_manager.get_system(); - if (mm2_system.is_mm2_running()) - { - const auto& ticker = mm2_system.get_current_ticker(); - convert_address(from, QString::fromStdString(ticker), to_address_format); - } + return m_send_rpc_result.get(); } - void - wallet_page::convert_address(QString from, QString ticker, QVariant to_address_format) + void wallet_page::set_rpc_send_data(QVariant rpc_data) { - auto& mm2_system = m_system_manager.get_system(); - if (mm2_system.is_mm2_running()) - { - QVariantMap out = to_address_format.value(); - auto address_fmt = nlohmann::json::parse(QJsonDocument::fromVariant(out).toJson().toStdString()); - t_convert_address_request req{.coin = ticker.toStdString(), .from = from.toStdString(), .to_address_format = address_fmt}; - this->set_convert_address_busy(true); - nlohmann::json batch = nlohmann::json::array(); - nlohmann::json json_data = ::mm2::api::template_request("convertaddress"); - ::mm2::api::to_json(json_data, req); - batch.push_back(json_data); - auto answer_functor = [this](web::http::http_response resp) - { - std::string body = TO_STD_STR(resp.extract_string(true).get()); - SPDLOG_DEBUG("resp convertaddress: {}", body); - if (resp.status_code() == static_cast(antara::app::http_code::ok)) - { - auto answers = nlohmann::json::parse(body); - auto convert_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "convertaddress"); - if (convert_answer.result.has_value()) - { - auto res = QString::fromStdString(convert_answer.result.value().address); - this->set_converted_address(res); - } - } - this->set_convert_address_busy(false); - }; - mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(&handle_exception_pplx_task); - } + m_send_rpc_result = rpc_data.toJsonObject(); + emit sendDataChanged(); } - QString - wallet_page::get_converted_address() const + bool wallet_page::has_auth_succeeded() const { - return m_converted_address.get(); + return m_auth_succeeded; } - void - wallet_page::set_converted_address(QString converted_address) + bool wallet_page::is_send_available() { - m_converted_address = converted_address; - emit convertedAddressChanged(); + return m_send_available; } - QString - wallet_page::switch_address_mode(bool checked) + QString wallet_page::get_send_availability_state() { - auto& mm2_system = m_system_manager.get_system(); - std::string address = ""; - if (mm2_system.is_mm2_running()) - { - const auto ticker = get_current_ticker().toStdString(); - const auto coin_cfg = mm2_system.get_coin_info(ticker); - if (coin_cfg.segwit) - { - nlohmann::json address_format = nlohmann::json::object(); - address_format = {{"format", "segwit"}}; - if (!checked) - { - //! We go from segwit to legacy - if (coin_cfg.ticker != "BCH") - { - address_format = {{"format", "standard"}}; - } - else - { - address_format = {{"format", "bch"}}; - } - } - - - std::error_code ec; - address = mm2_system.address(ticker, ec); - t_convert_address_request req{.coin = ticker, .from = address, .to_address_format = address_format}; - nlohmann::json batch = nlohmann::json::array(); - nlohmann::json json_data = ::mm2::api::template_request("convertaddress"); - ::mm2::api::to_json(json_data, req); - batch.push_back(json_data); - json_data["userpass"] = "******"; - SPDLOG_INFO("convertaddress request: {}", json_data.dump()); - web::http::http_response resp = mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).get(); - std::string body = TO_STD_STR(resp.extract_string(true).get()); - SPDLOG_DEBUG("resp convertaddress: {}", body); - if (resp.status_code() == static_cast(antara::app::http_code::ok)) - { - auto answers = nlohmann::json::parse(body); - auto convert_answer = ::mm2::api::rpc_process_answer_batch(answers[0], "convertaddress"); - if (convert_answer.result.has_value()) - { - return QString::fromStdString(convert_answer.result.value().address); - } - } - } - } - return QString::fromStdString(address); + return m_send_availability_state; } - void - wallet_page::post_switch_address_mode(bool is_segwit) + bool wallet_page::is_current_ticker_fees_coin_enabled() { - SPDLOG_INFO("switching to : {}", is_segwit ? "segwit" : "legacy"); - auto& mm2_system = m_system_manager.get_system(); - if (mm2_system.is_mm2_running()) - { - //! Need disable + enable + refresh balance + refresh current coin info (address) + change segwit in cfg - const auto ticker = get_current_ticker().toStdString(); - nlohmann::json batch = nlohmann::json::array(); - nlohmann::json json_data = ::mm2::api::template_request("disable_coin"); - t_disable_coin_request req{.coin = ticker}; - ::mm2::api::to_json(json_data, req); - batch.push_back(json_data); - //! Disable is in the batch - - //! electrum - auto coin_info = mm2_system.get_coin_info(ticker); - t_electrum_request electrum_req{ - .coin_name = coin_info.ticker, .servers = coin_info.electrum_urls.value(), .coin_type = coin_info.coin_type, .with_tx_history = true}; - if (is_segwit) - { - electrum_req.address_format = nlohmann::json::object(); - electrum_req.address_format.value()["format"] = "segwit"; - } - nlohmann::json electrum_data = ::mm2::api::template_request("electrum"); - ::mm2::api::to_json(electrum_data, electrum_req); - batch.push_back(electrum_data); - electrum_data["userpass"] = "*******"; - SPDLOG_INFO("electrum_req: {}", electrum_data.dump(-1)); + return m_current_ticker_fees_coin_enabled; + } - //! Answer functor - auto answer_functor = [this, ticker, is_segwit](web::http::http_response resp) - { - std::string body = TO_STD_STR(resp.extract_string(true).get()); - SPDLOG_DEBUG("resp disable/enable: {}", body); - if (resp.status_code() == static_cast(antara::app::http_code::ok)) - { - auto& mm2_system = m_system_manager.get_system(); - mm2_system.change_segwit_status(ticker, is_segwit); - mm2_system.fetch_infos_thread(true, false); - SPDLOG_INFO("Switching address mode success"); - } - }; + bool wallet_page::is_page_open() const + { + return m_page_open; + } - //! Rpc processing - mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(&handle_exception_pplx_task); - } + void wallet_page::set_page_open(bool value) + { + m_page_open = value; + emit isPageOpenChanged(); } } // namespace atomic_dex diff --git a/src/core/atomicdex/pages/qt.wallet.page.hpp b/src/core/atomicdex/pages/qt.wallet.page.hpp index 40a4a1bc78..a7f7a5e8f4 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.hpp +++ b/src/core/atomicdex/pages/qt.wallet.page.hpp @@ -1,32 +1,36 @@ #pragma once -//! Qt #include #include #include -#include "atomicdex/models/qt.wallet.transactions.model.hpp" +#include "atomicdex/models/transactions_model.hpp" namespace atomic_dex { class wallet_page final : public QObject, public ag::ecs::pre_update_system { - // Q_Object definition Q_OBJECT using t_qt_synchronized_json = boost::synchronized_value; using t_qt_synchronized_string = boost::synchronized_value; - public: + public: explicit wallet_page(entt::registry& registry, ag::ecs::system_manager& system_manager, QObject* parent = nullptr); - void update() override; ~wallet_page() final = default; - void refresh_ticker_infos(); + void update() override; - void on_tx_fetch_finished(const tx_fetch_finished&); + void refresh_ticker_infos(); + + void on_mm2_tx_fetch_finished(const mm2_service::tx_fetch_finished&); void on_ticker_balance_updated(const ticker_balance_updated&); + private: + // When called, refreshes `send_available` and `send_availability_state` values. `send_available` is set to false when you cannot send coins from the selected asset, thus `send_availability_state` will contain the reason of why it's not possible. + void check_send_availability(); + + public: // Getters/Setters [[nodiscard]] transactions_model* get_transactions_mdl() const; [[nodiscard]] QString get_current_ticker() const; @@ -68,23 +72,16 @@ namespace atomic_dex [[nodiscard]] bool is_page_open() const; void set_page_open(bool value); - void check_send_availability(); // When called, refreshes `m_send_availability_state` and `m_send_available` respective values. `m_send_available` is - // equal to false when you cannot send the selected coin, thus `m_send_availability_state` will contain the reason of - // why it's not possible. - // QML API - Q_INVOKABLE void validate_address(QString address); - Q_INVOKABLE void validate_address(QString address, QString ticker); - Q_INVOKABLE void convert_address(QString from, QVariant to_address_format); // https://developers.atomicdex.io/basic-docs/atomicdex/atomicdex-api.html#convertaddress - Q_INVOKABLE void convert_address(QString from, QString ticker, QVariant to_address_format); // https://developers.atomicdex.io/basic-docs/atomicdex/atomicdex-api.html#convertaddress - Q_INVOKABLE void claim_rewards(); - Q_INVOKABLE void claim_faucet(); - Q_INVOKABLE void broadcast(const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount); - void broadcast_on_auth_finished( - bool is_auth, const QString& tx_hex, bool is_claiming, bool is_max, - const QString& amount); // Broadcast requires OS local user credentials verification. This is called by the Q_INVOKABLE broadcast() method after - // entering credentials. - Q_INVOKABLE void send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data); + Q_INVOKABLE void validate_address(QString address); + Q_INVOKABLE void validate_address(QString address, QString ticker); + Q_INVOKABLE void convert_address(QString from, QVariant to_address_format); // https://developers.atomicdex.io/basic-docs/atomicdex/atomicdex-api.html#convertaddress + Q_INVOKABLE void convert_address(QString from, QString ticker, QVariant to_address_format); // https://developers.atomicdex.io/basic-docs/atomicdex/atomicdex-api.html#convertaddress + Q_INVOKABLE void claim_rewards(); + Q_INVOKABLE void claim_faucet(); + Q_INVOKABLE void broadcast(const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount); + void broadcast_on_auth_finished(bool is_auth, const QString& tx_hex, bool is_claiming, bool is_max,const QString& amount); // Sending coins requires OS local user credentials verification. This is called by the Q_INVOKABLE broadcast() method after entering credentials. + Q_INVOKABLE void send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data); Q_INVOKABLE QString switch_address_mode(bool checked); Q_INVOKABLE void post_switch_address_mode(bool is_segwit); @@ -113,7 +110,7 @@ namespace atomic_dex Q_PROPERTY(QString converted_address READ get_converted_address WRITE set_converted_address NOTIFY convertedAddressChanged) // QML API Properties Signals - signals: + signals: void currentTickerChanged(); void tickerInfosChanged(); void rpcClaimingStatusChanged(); @@ -137,7 +134,7 @@ namespace atomic_dex void convertAddressBusyChanged(); void convertedAddressChanged(); - private: + private: ag::ecs::system_manager& m_system_manager; transactions_model* m_transactions_mdl; std::atomic_bool m_is_claiming_busy{false}; @@ -159,7 +156,7 @@ namespace atomic_dex bool m_send_available{true}; QString m_send_availability_state; bool m_current_ticker_fees_coin_enabled{true}; // Tells if the current ticker's fees coin is enabled. - std::chrono::high_resolution_clock::time_point m_update_clock; // Clock used to time the `update()` loop of this ecs system. + std::chrono::high_resolution_clock::time_point m_update_clock; // Clock used as a refresh rate in `update()`. bool m_page_open{false}; mutable std::shared_mutex m_update_tx_mutex; diff --git a/src/core/atomicdex/services/mm2/mm2.service.cpp b/src/core/atomicdex/services/mm2/mm2.service.cpp index d3f0265a8a..a61c13792e 100644 --- a/src/core/atomicdex/services/mm2/mm2.service.cpp +++ b/src/core/atomicdex/services/mm2/mm2.service.cpp @@ -469,13 +469,12 @@ namespace atomic_dex update_coin_status(this->m_current_wallet_name, tickers, false, m_coins_informations, m_coin_cfg_mutex); } - auto - mm2_service::batch_balance_and_tx(bool is_a_reset, std::vector tickers, bool is_during_enabling, bool only_tx) + auto mm2_service::batch_balance_and_tx(bool is_a_reset, std::vector tickers, bool is_during_enabling, bool only_tx) { - // SPDLOG_INFO("batch_balance_and_tx"); (void)tickers; (void)is_during_enabling; auto&& [batch_array, tickers_idx, tokens_to_fetch] = prepare_batch_balance_and_tx(only_tx); + return m_mm2_client.async_rpc_batch_standalone(batch_array) .then( [this, tokens_to_fetch = tokens_to_fetch, is_a_reset, tickers](web::http::http_response resp) @@ -1471,8 +1470,7 @@ namespace atomic_dex } } - void - mm2_service::process_tx_answer(const nlohmann::json& answer_json) + void mm2_service::process_tx_answer(const nlohmann::json& answer_json) { ::mm2::api::tx_history_answer answer; ::mm2::api::from_json(answer_json, answer); diff --git a/src/core/atomicdex/services/mm2/mm2.service.hpp b/src/core/atomicdex/services/mm2/mm2.service.hpp index 654a4b7fbd..2199e765c1 100644 --- a/src/core/atomicdex/services/mm2/mm2.service.hpp +++ b/src/core/atomicdex/services/mm2/mm2.service.hpp @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2013-2021 The Komodo Platform Developers. * + * Copyright © 2013-2022 The Komodo Platform Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -16,21 +16,18 @@ #pragma once -//! Qt -#include - #include #include #include -//! Deps +#include + #include #include + #include #include -//#include -//! Project Headers #include "atomicdex/api/mm2/mm2.client.hpp" #include "atomicdex/api/mm2/mm2.constants.hpp" #include "atomicdex/api/mm2/mm2.error.code.hpp" @@ -59,17 +56,18 @@ namespace atomic_dex using t_coins_registry = std::unordered_map; using t_coins = std::vector; - //! Constants - inline constexpr const std::size_t g_tx_max_limit{50}; - class ENTT_API mm2_service final : public ag::ecs::pre_update_system { - public: + public: using t_pair_max_vol = std::pair; using t_pair_min_vol = std::pair; - private: - //! Private typedefs + struct tx_fetch_finished + { + bool with_error{false}; + }; + + private: using t_mm2_time_point = std::chrono::high_resolution_clock::time_point; using t_balance_registry = std::unordered_map; using t_tx_registry = t_shared_synchronized_value>>; @@ -82,16 +80,9 @@ namespace atomic_dex ag::ecs::system_manager& m_system_manager; - //! Client - // std::shared_ptr m_mm2_client{nullptr}; - // pplx::cancellation_token_source m_token_source; mm2_client m_mm2_client; - //! Process - //reproc::process m_mm2_instance; - - //! Current ticker - t_synchronized_ticker m_current_ticker{g_primary_dex_coin}; + t_synchronized_ticker m_current_ticker{g_primary_dex_coin}; // Current tiker //! Current orderbook t_synchronized_ticker_pair m_synchronized_ticker_pair{std::make_pair(g_primary_dex_coin, g_second_primary_dex_coin)}; @@ -110,7 +101,6 @@ namespace atomic_dex //! Current wallet name std::string m_current_wallet_name; - //! Mutex mutable std::shared_mutex m_balance_mutex; mutable std::shared_mutex m_coin_cfg_mutex; mutable std::shared_mutex m_raw_coin_cfg_mutex; @@ -146,16 +136,12 @@ namespace atomic_dex void handle_exception_pplx_task(pplx::task previous_task, const std::string& from, nlohmann::json batch); public: - //! Constructor explicit mm2_service(entt::registry& registry, ag::ecs::system_manager& system_manager); - - //! Delete useless operator mm2_service(const mm2_service& other) = delete; mm2_service(const mm2_service&& other) = delete; mm2_service& operator=(const mm2_service& other) = delete; mm2_service& operator=(const mm2_service&& other) = delete; - //! Destructor ~mm2_service() final; //! Events @@ -250,13 +236,9 @@ namespace atomic_dex void batch_fetch_orders_and_swap(bool after_manual_reset = false); void add_orders_answer(t_my_orders_answer answer); - //! Async API - mm2_client& get_mm2_client(); - //[[nodiscard]] pplx::cancellation_token get_cancellation_token() const; - - //! Wallet api - [[nodiscard]] std::string get_current_ticker() const; - bool set_current_ticker(const std::string& ticker); + [[nodiscard]] mm2_client& get_mm2_client(); + [[nodiscard]] std::string get_current_ticker() const; + bool set_current_ticker(const std::string& ticker); //! Pagination void set_orders_and_swaps_pagination_infos(std::size_t current_page = 1, std::size_t limit = 50, t_filtering_infos infos = {}); diff --git a/src/core/atomicdex/utilities/qt.utilities.hpp b/src/core/atomicdex/utilities/qt.utilities.hpp index 036c8c0df7..ab760bc71d 100644 --- a/src/core/atomicdex/utilities/qt.utilities.hpp +++ b/src/core/atomicdex/utilities/qt.utilities.hpp @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2013-2021 The Komodo Platform Developers. * + * Copyright © 2013-2022 The Komodo Platform Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -16,7 +16,6 @@ #pragma once -//! QT Headers #include #include #include @@ -25,7 +24,6 @@ #include #include //> QCryptographicHash::hash, QCryptographicHash::Keccak_256 -//! Project Headers #include "atomicdex/config/app.cfg.hpp" #include "atomicdex/config/coins.cfg.hpp" #include "atomicdex/config/wallet.cfg.hpp" @@ -33,9 +31,8 @@ namespace atomic_dex { - template - auto - update_value(int role, const QVariant& value, const QModelIndex& idx, TModel& model) + template + inline auto update_value(int role, const QVariant& value, const QModelIndex& idx, QtModel& model) { if (auto prev_value = model.data(idx, role); value != prev_value) { @@ -54,8 +51,7 @@ namespace atomic_dex const atomic_dex::komodo_prices_provider& provider, const atomic_dex::coin_config& coin, const atomic_dex::cfg& config, const ag::ecs::system_manager& system_manager); - [[nodiscard]] QString - inline sha256_qstring_from_qt_byte_array(const QByteArray& byte_array) + [[nodiscard]] QString inline sha256_qstring_from_qt_byte_array(const QByteArray& byte_array) { return QLatin1String(QCryptographicHash::hash(byte_array, QCryptographicHash::Sha256).toHex()); } From 153393e9faeda37491c18d4619277c6ec5881af1 Mon Sep 17 00:00:00 2001 From: syl Date: Thu, 4 Aug 2022 03:24:27 +0200 Subject: [PATCH 05/17] Coding style --- atomic_defi_design/Dex/Wallet/Sidebar.qml | 26 ++++++++++++------- .../Dex/Wallet/SidebarItemDelegate.qml | 3 ++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/atomic_defi_design/Dex/Wallet/Sidebar.qml b/atomic_defi_design/Dex/Wallet/Sidebar.qml index 4b54ce976d..aca26235ad 100644 --- a/atomic_defi_design/Dex/Wallet/Sidebar.qml +++ b/atomic_defi_design/Dex/Wallet/Sidebar.qml @@ -9,6 +9,7 @@ import "../Components" import "../Constants" as Constants import App 1.0 import Dex.Themes 1.0 as Dex +import Dex.Components 1.0 as Dex // Coins bar at left side Item @@ -59,7 +60,7 @@ Item Layout.preferredHeight: 38 textField.placeholderText: qsTr("Search") - forceFocus: true + //forceFocus: true searchModel: portfolio_coins } @@ -71,14 +72,21 @@ Item Layout.fillHeight: true Layout.alignment: Qt.AlignHCenter color: 'transparent' - content: DexListView { + content: Dex.ListView + { id: list height: list_bg.height model: portfolio_coins topMargin: 5 bottomMargin: 5 scrollbar_visible: false - DexRectangle { + + reuseItems: true + + delegate: SidebarItemDelegate { } + + Dex.Rectangle + { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter width: parent.width + 4 @@ -94,23 +102,21 @@ Item } } - DexRectangle { + Dex.Rectangle + { anchors.horizontalCenter: parent.horizontalCenter width: parent.width + 4 height: 30 radius: 8 opacity: .5 visible: list.position > 0 ? true : false - Qaterial.Icon { + Qaterial.Icon + { anchors.centerIn: parent - color: DexTheme.foregroundColor + color: Dex.CurrentTheme.foregroundColor icon: Qaterial.Icons.arrowUpCircleOutline } } - - reuseItems: true - - delegate: SidebarItemDelegate { } } } diff --git a/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml b/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml index 78b270b146..b488fda855 100644 --- a/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml +++ b/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml @@ -9,7 +9,8 @@ import "../Components" import "../Constants" as Constants import App 1.0 -GradientRectangle { +GradientRectangle +{ width: list_bg.width - list_bg.border.width*2 - 6 height: 44 radius: Constants.Style.rectangleCornerRadius + 4 From 6b5e2132cec1d4c93c16bb4ed424070d55ce3f82 Mon Sep 17 00:00:00 2001 From: syl Date: Thu, 4 Aug 2022 03:32:16 +0200 Subject: [PATCH 06/17] wallet_page: when changing ticker, cancel mm2 service tx fetch thread if it's already running --- src/core/atomicdex/pages/qt.wallet.page.cpp | 4 ++++ src/core/atomicdex/services/mm2/mm2.service.cpp | 15 +++++++++------ src/core/atomicdex/services/mm2/mm2.service.hpp | 10 ++++++---- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/core/atomicdex/pages/qt.wallet.page.cpp b/src/core/atomicdex/pages/qt.wallet.page.cpp index ef9eab3a6e..17f8cd6ce9 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.cpp +++ b/src/core/atomicdex/pages/qt.wallet.page.cpp @@ -626,6 +626,10 @@ namespace atomic_dex void wallet_page::set_current_ticker(const QString& ticker) { auto& mm2_system = m_system_manager.get_system(); + if (is_tx_fetching_busy()) + { + mm2_system.cancel_fetch_infos_thread(); + } if (mm2_system.set_current_ticker(ticker.toStdString())) { SPDLOG_INFO("new ticker: {}", ticker.toStdString()); diff --git a/src/core/atomicdex/services/mm2/mm2.service.cpp b/src/core/atomicdex/services/mm2/mm2.service.cpp index a61c13792e..3e2faff06d 100644 --- a/src/core/atomicdex/services/mm2/mm2.service.cpp +++ b/src/core/atomicdex/services/mm2/mm2.service.cpp @@ -515,9 +515,9 @@ namespace atomic_dex SPDLOG_ERROR("exception in batch_balance_and_tx: {}", error.what()); this->dispatcher_.trigger(true); } - }) + }, m_cancellation_token_source.get_token()) .then([this, batch = batch_array](pplx::task previous_task) - { this->handle_exception_pplx_task(previous_task, "batch_balance_and_tx", batch); }); + { this->handle_exception_pplx_task(previous_task, "batch_balance_and_tx", batch); }, m_cancellation_token_source.get_token()); } std::tuple, std::vector> @@ -941,13 +941,11 @@ namespace atomic_dex }; auto error_functor = [this, batch = batch_array](pplx::task previous_task) { this->handle_exception_pplx_task(previous_task, "fetch_single_balance", batch); }; - m_mm2_client.async_rpc_batch_standalone(batch_array).then(answer_functor).then(error_functor); + m_mm2_client.async_rpc_batch_standalone(batch_array).then(answer_functor, m_cancellation_token_source.get_token()).then(error_functor); } - void - mm2_service::fetch_infos_thread(bool is_a_refresh, bool only_tx) + void mm2_service::fetch_infos_thread(bool is_a_refresh, bool only_tx) { - SPDLOG_INFO("fetch_infos_thread"); if (only_tx) { batch_balance_and_tx(is_a_refresh, {}, false, only_tx); @@ -960,6 +958,11 @@ namespace atomic_dex } } + void mm2_service::cancel_fetch_infos_thread() + { + m_cancellation_token_source.cancel(); + } + void mm2_service::spawn_mm2_instance(std::string wallet_name, std::string passphrase, bool with_pin_cfg) { diff --git a/src/core/atomicdex/services/mm2/mm2.service.hpp b/src/core/atomicdex/services/mm2/mm2.service.hpp index 2199e765c1..58d4d056c8 100644 --- a/src/core/atomicdex/services/mm2/mm2.service.hpp +++ b/src/core/atomicdex/services/mm2/mm2.service.hpp @@ -94,9 +94,10 @@ namespace atomic_dex t_mm2_time_point m_info_clock; //! Atomicity / Threads - std::atomic_bool m_mm2_running{false}; - std::atomic_bool m_orderbook_thread_active{false}; - std::thread m_mm2_init_thread; + std::atomic_bool m_mm2_running{false}; + std::atomic_bool m_orderbook_thread_active{false}; + std::thread m_mm2_init_thread; + pplx::cancellation_token_source m_cancellation_token_source; //! Current wallet name std::string m_current_wallet_name; @@ -154,8 +155,9 @@ namespace atomic_dex //! Spawn mm2 instance with given seed void spawn_mm2_instance(std::string wallet_name, std::string passphrase, bool with_pin_cfg = false); - //! Refresh the current info (internally call process_balance and process_tx) + // Refresh the current info (internally call process_balance and process_tx) void fetch_infos_thread(bool is_a_fresh = true, bool only_tx = false); + void cancel_fetch_infos_thread(); //! Enable coins bool enable_default_coins(); From 54b24455b5f1cdeb8638f07914a7bde791d97274 Mon Sep 17 00:00:00 2001 From: alamshafil Date: Wed, 17 Aug 2022 21:46:52 -0400 Subject: [PATCH 07/17] Add Avian --- assets/config/0.5.6-coins.json | 23 ++++++++++++++++++ atomic_defi_design/Dex/Constants/Style.qml | 1 + .../assets/images/coins/avn.png | Bin 0 -> 52750 bytes 3 files changed, 24 insertions(+) create mode 100644 atomic_defi_design/assets/images/coins/avn.png diff --git a/assets/config/0.5.6-coins.json b/assets/config/0.5.6-coins.json index 0fa959662e..7cb97a0fc7 100644 --- a/assets/config/0.5.6-coins.json +++ b/assets/config/0.5.6-coins.json @@ -5228,6 +5228,29 @@ "active": false, "currently_enabled": false }, + "AVN": { + "coin": "AVN", + "name": "Avian", + "coinpaprika_id": "avn-avian", + "coingecko_id": "avian-network", + "nomics_id": "avn3-avian", + "electrum": [ + { + "url": "electrum-us.avn.network:50001", + "protocol": "TCP" + }, + { + "url": "electrum-eu.avn.network:50001", + "protocol": "TCP" + } + ], + "explorer_url": [ + "https://explorer.avn.network/" + ], + "type": "UTXO", + "active": false, + "currently_enabled": false + }, "AWC": { "active": false, "coin": "AWC", diff --git a/atomic_defi_design/Dex/Constants/Style.qml b/atomic_defi_design/Dex/Constants/Style.qml index dfe480e881..82d3fec477 100644 --- a/atomic_defi_design/Dex/Constants/Style.qml +++ b/atomic_defi_design/Dex/Constants/Style.qml @@ -237,6 +237,7 @@ QtObject { "AUR": "#0A6C5E", "AVA": "#5B567F", "AVAX": "#E84142", + "AVN": "#33E0CE", "AXS": "#0055D5", "BAL": "#4D4D4D", "BNB": "#F9D987", diff --git a/atomic_defi_design/assets/images/coins/avn.png b/atomic_defi_design/assets/images/coins/avn.png new file mode 100644 index 0000000000000000000000000000000000000000..a22e149551a36e3a819e5c5e7db9a7553fdb152a GIT binary patch literal 52750 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 bKP_*`XqiAHlZ#!a0DFPiUbU&Q_DT3Jk$vvN literal 0 HcmV?d00001 From c1a5f19bde94e70f099fe2aaf64d6c086453a9a2 Mon Sep 17 00:00:00 2001 From: MerlinMagic2018 Date: Sat, 10 Sep 2022 13:53:53 -0400 Subject: [PATCH 08/17] Add LightningCash (LNC) --- assets/config/0.5.6-coins.json | 22 +++++++++++++++++- atomic_defi_design/Dex/Constants/Style.qml | 1 + .../assets/images/coins/lnc.png | Bin 0 -> 8933 bytes 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 atomic_defi_design/assets/images/coins/lnc.png diff --git a/assets/config/0.5.6-coins.json b/assets/config/0.5.6-coins.json index d3ba296a3f..7743c721db 100644 --- a/assets/config/0.5.6-coins.json +++ b/assets/config/0.5.6-coins.json @@ -3985,6 +3985,26 @@ "active": false, "currently_enabled": false }, + "LNC": { + "coin": "LNC", + "name": "LightningCash", + "coinpaprika_id": "lnc-lightningcash", + "coingecko_id": "lightningcash", + "nomics_id": "LNC3", + "electrum": [ + { + "url": "74.208.95.26:50001" + } + ], + "explorer_url": [ + "https://explorer.lightningcash-coin.com/" + ], + "explorer_tx_url": "tx/", + "explorer_address_url": "address/", + "type": "UTXO", + "active": false, + "currently_enabled": false + }, "LOOM-ERC20": { "coin": "LOOM-ERC20", "name": "Loom Network", @@ -10841,4 +10861,4 @@ "currently_enabled": false, "wallet_only": false } -} \ No newline at end of file +} diff --git a/atomic_defi_design/Dex/Constants/Style.qml b/atomic_defi_design/Dex/Constants/Style.qml index 89e7d0ea98..52c63d90a1 100644 --- a/atomic_defi_design/Dex/Constants/Style.qml +++ b/atomic_defi_design/Dex/Constants/Style.qml @@ -324,6 +324,7 @@ QtObject { "CRYPTO": "#F58736", "LABS": "#C1F6E1", "LCC": "#068210", + "LNC": "#C3A635", "MESH": "#0098DA", "MGW": "#854F2F", "MONA": "#DEC799", diff --git a/atomic_defi_design/assets/images/coins/lnc.png b/atomic_defi_design/assets/images/coins/lnc.png new file mode 100644 index 0000000000000000000000000000000000000000..c71ff557db6aff5a21b057c7dfa53b9d58d11578 GIT binary patch literal 8933 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 literal 0 HcmV?d00001 From 9424bbcf23295e7890f2d83f882ba3e7a6f852fc Mon Sep 17 00:00:00 2001 From: syl Date: Wed, 14 Sep 2022 19:36:07 +0200 Subject: [PATCH 09/17] Fix compilation --- src/core/atomicdex/pages/qt.wallet.page.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/atomicdex/pages/qt.wallet.page.hpp b/src/core/atomicdex/pages/qt.wallet.page.hpp index 3b23b7296d..65966d5da9 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.hpp +++ b/src/core/atomicdex/pages/qt.wallet.page.hpp @@ -23,7 +23,7 @@ namespace atomic_dex void refresh_ticker_infos(); - void on_mm2_tx_fetch_finished(const mm2_service::tx_fetch_finished&); + void on_mm2_tx_fetch_finished(const tx_fetch_finished&); void on_ticker_balance_updated(const ticker_balance_updated&); private: From 3e1ad276895a30af112608d8d0a8d634e8e1f46e Mon Sep 17 00:00:00 2001 From: syl Date: Wed, 14 Sep 2022 19:50:48 +0200 Subject: [PATCH 10/17] Fix previous sync with slp tokens PR --- src/app/app.cpp | 2 +- src/core/atomicdex/pages/qt.wallet.page.cpp | 77 ++- src/core/atomicdex/pages/qt.wallet.page.hpp | 49 +- .../atomicdex/services/mm2/mm2.service.cpp | 14 +- .../atomicdex/services/mm2/mm2.service.hpp | 437 +++++++++--------- 5 files changed, 316 insertions(+), 263 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 0c6c2b6a57..9684d5cfd1 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -249,7 +249,7 @@ namespace atomic_dex if (std::find(to_init.begin(), to_init.end(), g_primary_dex_coin) != to_init.end()) { get_wallet_page()->get_transactions_mdl()->reset(); - this->dispatcher_.trigger(); + this->dispatcher_.trigger(); } get_wallet_page()->refresh_ticker_infos(); system_manager_.get_system().set_status("complete"); diff --git a/src/core/atomicdex/pages/qt.wallet.page.cpp b/src/core/atomicdex/pages/qt.wallet.page.cpp index b0a91cc686..bb2a691db4 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.cpp +++ b/src/core/atomicdex/pages/qt.wallet.page.cpp @@ -3,11 +3,13 @@ #include #include #include -#include +//! Deps +#include #include #include +//! Project Headers #include "atomicdex/api/faucet/faucet.hpp" #include "atomicdex/api/mm2/rpc.convertaddress.hpp" #include "atomicdex/api/mm2/rpc.electrum.hpp" @@ -28,11 +30,12 @@ namespace atomic_dex wallet_page::wallet_page(entt::registry& registry, ag::ecs::system_manager& system_manager, QObject* parent) : QObject(parent), system(registry), m_system_manager(system_manager), m_transactions_mdl(new transactions_model(system_manager, this)) { - this->dispatcher_.sink().connect<&wallet_page::on_mm2_tx_fetch_finished>(*this); + this->dispatcher_.sink().connect<&wallet_page::on_tx_fetch_finished>(*this); this->dispatcher_.sink().connect<&wallet_page::on_ticker_balance_updated>(*this); } - void wallet_page::update() + void + wallet_page::update() { if (!m_page_open) { @@ -48,8 +51,13 @@ namespace atomic_dex m_update_clock = std::chrono::high_resolution_clock::now(); } } +} // namespace atomic_dex - void wallet_page::check_send_availability() +//! Private API +namespace atomic_dex +{ + void + wallet_page::check_send_availability() { auto& mm2 = m_system_manager.get_system(); auto global_coins_cfg = m_system_manager.get_system().get_global_cfg(); @@ -88,6 +96,7 @@ namespace atomic_dex emit sendAvailabilityStateChanged(); emit currentTickerFeesCoinEnabledChanged(); } +} // namespace atomic_dex //! Getters/Setters namespace atomic_dex @@ -486,7 +495,8 @@ namespace atomic_dex emit tickerInfosChanged(); } - void wallet_page::send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data) + void + wallet_page::send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data) { //! Preparation this->set_send_busy(true); @@ -780,7 +790,8 @@ namespace atomic_dex } } - void wallet_page::broadcast(const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount) + void + wallet_page::broadcast(const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount) { #if defined(__APPLE__) || defined(WIN32) || defined(_WIN32) QSettings& settings = this->entity_registry_.ctx(); @@ -798,7 +809,8 @@ namespace atomic_dex #endif } - void wallet_page::broadcast_on_auth_finished(bool is_auth, const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount) + void + wallet_page::broadcast_on_auth_finished(bool is_auth, const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount) { if (!is_auth) { @@ -827,6 +839,7 @@ namespace atomic_dex auto& mm2_system = m_system_manager.get_system(); const auto& ticker = mm2_system.get_current_ticker(); auto answers = nlohmann::json::parse(body); + // SPDLOG_INFO("broadcast answer: {}", answers.dump(4)); if (answers[0].contains("tx_hash")) { this->set_rpc_broadcast_data(QString::fromStdString(answers[0].at("tx_hash").get())); @@ -869,7 +882,8 @@ namespace atomic_dex mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(error_functor); } - void wallet_page::claim_rewards() + void + wallet_page::claim_rewards() { this->set_claiming_is_busy(true); nlohmann::json batch = nlohmann::json::array(); @@ -885,6 +899,7 @@ namespace atomic_dex auto answer_functor = [this](web::http::http_response resp) { std::string body = TO_STD_STR(resp.extract_string(true).get()); + // SPDLOG_DEBUG("resp claiming: {}", body); if (resp.status_code() == static_cast(antara::app::http_code::ok) && body.find("error") == std::string::npos) { auto answers = nlohmann::json::parse(body); @@ -922,7 +937,8 @@ namespace atomic_dex mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).then(answer_functor).then(error_functor); } - void wallet_page::claim_faucet() + void + wallet_page ::claim_faucet() { const auto& mm2_system = m_system_manager.get_system(); const auto& ticker = mm2_system.get_current_ticker(); @@ -943,17 +959,20 @@ namespace atomic_dex .then(&handle_exception_pplx_task); } - transactions_model* wallet_page::get_transactions_mdl() const + transactions_model* + wallet_page::get_transactions_mdl() const { return m_transactions_mdl; } - void wallet_page::on_ticker_balance_updated(const ticker_balance_updated&) + void + wallet_page::on_ticker_balance_updated(const ticker_balance_updated&) { refresh_ticker_infos(); } - void wallet_page::on_mm2_tx_fetch_finished(const mm2_service::tx_fetch_finished& evt) + void + wallet_page::on_tx_fetch_finished(const tx_fetch_finished& evt) { if (!evt.with_error && QString::fromStdString(evt.ticker) == get_current_ticker()) { @@ -985,7 +1004,8 @@ namespace atomic_dex this->set_tx_fetching_busy(false); } - void wallet_page::validate_address(QString address) + void + wallet_page::validate_address(QString address) { auto& mm2_system = m_system_manager.get_system(); if (mm2_system.is_mm2_running()) @@ -995,8 +1015,10 @@ namespace atomic_dex } } - void wallet_page::validate_address(QString address, QString ticker) + void + wallet_page::validate_address(QString address, QString ticker) { + // SPDLOG_INFO("validate_address: {} - ticker: {}", address.toStdString(), ticker.toStdString()); auto& mm2_system = m_system_manager.get_system(); if (mm2_system.is_mm2_running()) { @@ -1010,6 +1032,7 @@ namespace atomic_dex auto answer_functor = [this, ticker](web::http::http_response resp) { std::string body = TO_STD_STR(resp.extract_string(true).get()); + // SPDLOG_DEBUG("resp validateaddress: {}", body); nlohmann::json j_out = nlohmann::json::object(); j_out["ticker"] = ticker.toStdString(); if (resp.status_code() == static_cast(antara::app::http_code::ok)) @@ -1048,7 +1071,8 @@ namespace atomic_dex } } - void wallet_page::convert_address(QString from, QVariant to_address_format) + void + wallet_page::convert_address(QString from, QVariant to_address_format) { auto& mm2_system = m_system_manager.get_system(); if (mm2_system.is_mm2_running()) @@ -1058,7 +1082,8 @@ namespace atomic_dex } } - void wallet_page::convert_address(QString from, QString ticker, QVariant to_address_format) + void + wallet_page::convert_address(QString from, QString ticker, QVariant to_address_format) { auto& mm2_system = m_system_manager.get_system(); if (mm2_system.is_mm2_running()) @@ -1106,6 +1131,19 @@ namespace atomic_dex QString wallet_page::get_converted_address() const + { + return m_converted_address.get(); + } + + void + wallet_page::set_converted_address(QString converted_address) + { + m_converted_address = converted_address; + emit convertedAddressChanged(); + } + + QString + wallet_page::switch_address_mode(bool checked) { auto& mm2_system = m_system_manager.get_system(); std::string address = ""; @@ -1139,6 +1177,7 @@ namespace atomic_dex mm2::to_json(json_data, req); batch.push_back(json_data); json_data["userpass"] = "******"; + SPDLOG_INFO("convertaddress request: {}", json_data.dump()); web::http::http_response resp = mm2_system.get_mm2_client().async_rpc_batch_standalone(batch).get(); std::string body = TO_STD_STR(resp.extract_string(true).get()); SPDLOG_DEBUG("resp convertaddress: {}", body); @@ -1156,8 +1195,10 @@ namespace atomic_dex return QString::fromStdString(address); } - void wallet_page::post_switch_address_mode(bool is_segwit) + void + wallet_page::post_switch_address_mode(bool is_segwit) { + SPDLOG_INFO("switching to : {}", is_segwit ? "segwit" : "legacy"); auto& mm2_system = m_system_manager.get_system(); if (mm2_system.is_mm2_running()) { @@ -1183,6 +1224,7 @@ namespace atomic_dex mm2::to_json(electrum_data, electrum_req); batch.push_back(electrum_data); electrum_data["userpass"] = "*******"; + SPDLOG_INFO("electrum_req: {}", electrum_data.dump(-1)); //! Answer functor auto answer_functor = [this, ticker, is_segwit](web::http::http_response resp) @@ -1194,6 +1236,7 @@ namespace atomic_dex auto& mm2_system = m_system_manager.get_system(); mm2_system.change_segwit_status(ticker, is_segwit); mm2_system.fetch_infos_thread(true, false); + SPDLOG_INFO("Switching address mode success"); } }; diff --git a/src/core/atomicdex/pages/qt.wallet.page.hpp b/src/core/atomicdex/pages/qt.wallet.page.hpp index 65966d5da9..033699279b 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.hpp +++ b/src/core/atomicdex/pages/qt.wallet.page.hpp @@ -1,5 +1,6 @@ #pragma once +//! Qt #include #include #include @@ -10,27 +11,22 @@ namespace atomic_dex { class wallet_page final : public QObject, public ag::ecs::pre_update_system { + // Q_Object definition Q_OBJECT using t_qt_synchronized_json = boost::synchronized_value; using t_qt_synchronized_string = boost::synchronized_value; - public: + public: explicit wallet_page(entt::registry& registry, ag::ecs::system_manager& system_manager, QObject* parent = nullptr); - ~wallet_page() final = default; - void update() override; + ~wallet_page() final = default; void refresh_ticker_infos(); - - void on_mm2_tx_fetch_finished(const tx_fetch_finished&); - void on_ticker_balance_updated(const ticker_balance_updated&); - private: - // When called, refreshes `send_available` and `send_availability_state` values. `send_available` is set to false when you cannot send coins from the selected asset, thus `send_availability_state` will contain the reason of why it's not possible. - void check_send_availability(); + void on_tx_fetch_finished(const tx_fetch_finished&); + void on_ticker_balance_updated(const ticker_balance_updated&); - public: // Getters/Setters [[nodiscard]] transactions_model* get_transactions_mdl() const; [[nodiscard]] QString get_current_ticker() const; @@ -76,16 +72,23 @@ namespace atomic_dex [[nodiscard]] bool is_page_open() const; void set_page_open(bool value); + void check_send_availability(); // When called, refreshes `m_send_availability_state` and `m_send_available` respective values. `m_send_available` is + // equal to false when you cannot send the selected coin, thus `m_send_availability_state` will contain the reason of + // why it's not possible. + // QML API - Q_INVOKABLE void validate_address(QString address); - Q_INVOKABLE void validate_address(QString address, QString ticker); - Q_INVOKABLE void convert_address(QString from, QVariant to_address_format); // https://developers.atomicdex.io/basic-docs/atomicdex/atomicdex-api.html#convertaddress - Q_INVOKABLE void convert_address(QString from, QString ticker, QVariant to_address_format); // https://developers.atomicdex.io/basic-docs/atomicdex/atomicdex-api.html#convertaddress - Q_INVOKABLE void claim_rewards(); - Q_INVOKABLE void claim_faucet(); - Q_INVOKABLE void broadcast(const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount); - void broadcast_on_auth_finished(bool is_auth, const QString& tx_hex, bool is_claiming, bool is_max,const QString& amount); // Sending coins requires OS local user credentials verification. This is called by the Q_INVOKABLE broadcast() method after entering credentials. - Q_INVOKABLE void send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data); + Q_INVOKABLE void validate_address(QString address); + Q_INVOKABLE void validate_address(QString address, QString ticker); + Q_INVOKABLE void convert_address(QString from, QVariant to_address_format); // https://developers.atomicdex.io/basic-docs/atomicdex/atomicdex-api.html#convertaddress + Q_INVOKABLE void convert_address(QString from, QString ticker, QVariant to_address_format); // https://developers.atomicdex.io/basic-docs/atomicdex/atomicdex-api.html#convertaddress + Q_INVOKABLE void claim_rewards(); + Q_INVOKABLE void claim_faucet(); + Q_INVOKABLE void broadcast(const QString& tx_hex, bool is_claiming, bool is_max, const QString& amount); + void broadcast_on_auth_finished( + bool is_auth, const QString& tx_hex, bool is_claiming, bool is_max, + const QString& amount); // Broadcast requires OS local user credentials verification. This is called by the Q_INVOKABLE broadcast() method after + // entering credentials. + Q_INVOKABLE void send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data); Q_INVOKABLE QString switch_address_mode(bool checked); Q_INVOKABLE void post_switch_address_mode(bool is_segwit); @@ -116,7 +119,7 @@ namespace atomic_dex Q_PROPERTY(QString withdraw_status READ get_withdraw_status WRITE set_withdraw_status NOTIFY withdrawStatusChanged) // QML API Properties Signals - signals: + signals: void currentTickerChanged(); void tickerInfosChanged(); void rpcClaimingStatusChanged(); @@ -142,7 +145,7 @@ namespace atomic_dex void convertedAddressChanged(); void withdrawStatusChanged(); - private: + private: ag::ecs::system_manager& m_system_manager; transactions_model* m_transactions_mdl; std::atomic_bool m_is_claiming_busy{false}; @@ -166,10 +169,8 @@ namespace atomic_dex bool m_send_available{true}; QString m_send_availability_state; bool m_current_ticker_fees_coin_enabled{true}; // Tells if the current ticker's fees coin is enabled. - std::chrono::high_resolution_clock::time_point m_update_clock; // Clock used as a refresh rate in `update()`. + std::chrono::high_resolution_clock::time_point m_update_clock; // Clock used to time the `update()` loop of this ecs system. bool m_page_open{false}; - - mutable std::shared_mutex m_update_tx_mutex; }; } // namespace atomic_dex diff --git a/src/core/atomicdex/services/mm2/mm2.service.cpp b/src/core/atomicdex/services/mm2/mm2.service.cpp index c636dbbc70..f789c5dfa3 100644 --- a/src/core/atomicdex/services/mm2/mm2.service.cpp +++ b/src/core/atomicdex/services/mm2/mm2.service.cpp @@ -893,12 +893,12 @@ namespace atomic_dex update_coin_status(this->m_current_wallet_name, tickers, false, m_coins_informations, m_coin_cfg_mutex); } - auto mm2_service::batch_balance_and_tx(bool is_a_reset, std::vector tickers, bool is_during_enabling, bool only_tx) + auto + mm2_service::batch_balance_and_tx(bool is_a_reset, std::vector tickers, bool is_during_enabling, bool only_tx) { (void)tickers; (void)is_during_enabling; auto&& [batch_array, tickers_idx, tokens_to_fetch] = prepare_batch_balance_and_tx(only_tx); - return m_mm2_client.async_rpc_batch_standalone(batch_array) .then( [this, tokens_to_fetch = tokens_to_fetch, is_a_reset, tickers, batch_array = batch_array](web::http::http_response resp) @@ -951,9 +951,9 @@ namespace atomic_dex SPDLOG_ERROR("exception in batch_balance_and_tx: {}", error.what()); this->dispatcher_.trigger(true); } - }, m_cancellation_token_source.get_token()) + }) .then([this, batch = batch_array](pplx::task previous_task) - { this->handle_exception_pplx_task(previous_task, "batch_balance_and_tx", batch); }, m_cancellation_token_source.get_token()); + { this->handle_exception_pplx_task(previous_task, "batch_balance_and_tx", batch); }); } std::tuple, std::vector> @@ -1498,11 +1498,13 @@ namespace atomic_dex auto error_functor = [this, batch = batch_array](pplx::task previous_task) { this->handle_exception_pplx_task(previous_task, "fetch_single_balance", batch); }; - m_mm2_client.async_rpc_batch_standalone(batch_array).then(answer_functor, m_cancellation_token_source.get_token()).then(error_functor); + m_mm2_client.async_rpc_batch_standalone(batch_array).then(answer_functor).then(error_functor); } - void mm2_service::fetch_infos_thread(bool is_a_refresh, bool only_tx) + void + mm2_service::fetch_infos_thread(bool is_a_refresh, bool only_tx) { + SPDLOG_INFO("fetch_infos_thread"); if (only_tx) { batch_balance_and_tx(is_a_refresh, {}, false, only_tx); diff --git a/src/core/atomicdex/services/mm2/mm2.service.hpp b/src/core/atomicdex/services/mm2/mm2.service.hpp index 9128360f55..639ec8218f 100644 --- a/src/core/atomicdex/services/mm2/mm2.service.hpp +++ b/src/core/atomicdex/services/mm2/mm2.service.hpp @@ -1,18 +1,18 @@ /****************************************************************************** - * Copyright © 2013-2022 The Komodo Platform Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * Komodo Platform software, including this file may be copied, modified, * - * propagated or distributed except according to the terms contained in the * - * LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ +* Copyright © 2013-2022 The Komodo Platform Developers. * +* * +* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * +* the top-level directory of this distribution for the individual copyright * +* holder information and the developer policies on copyright and licensing. * +* * +* Unless otherwise agreed in a custom licensing agreement, no part of the * +* Komodo Platform software, including this file may be copied, modified, * +* propagated or distributed except according to the terms contained in the * +* LICENSE file * +* * +* Removal or modification of this copyright notice is prohibited. * +* * +******************************************************************************/ #pragma once @@ -24,7 +24,6 @@ #include #include #include - #include #include @@ -47,227 +46,235 @@ namespace atomic_dex { - namespace bm = boost::multiprecision; - namespace ag = antara::gaming; - - template - using t_shared_synchronized_value = boost::synchronized_value; - - using t_ticker = std::string; - using t_coins_registry = std::unordered_map; - using t_coins = std::vector; + namespace bm = boost::multiprecision; + namespace ag = antara::gaming; + + template + using t_shared_synchronized_value = boost::synchronized_value; + + using t_ticker = std::string; + using t_coins_registry = std::unordered_map; + using t_coins = std::vector; + + class ENTT_API mm2_service final : public ag::ecs::pre_update_system + { + public: + using t_pair_max_vol = std::pair; + using t_pair_min_vol = std::pair; + + private: + using t_mm2_time_point = std::chrono::high_resolution_clock::time_point; + using t_balance_registry = std::unordered_map; + using t_tx_registry = t_shared_synchronized_value>>; + using t_orderbook = boost::synchronized_value; + using t_orders_and_swaps = boost::synchronized_value; + using t_synchronized_ticker_pair = boost::synchronized_value>; + using t_synchronized_max_taker_vol = boost::synchronized_value; + using t_synchronized_min_taker_vol = boost::synchronized_value; + using t_synchronized_ticker = boost::synchronized_value; + + ag::ecs::system_manager& m_system_manager; + + mm2::mm2_client m_mm2_client; + + //! Current ticker + t_synchronized_ticker m_current_ticker{g_primary_dex_coin}; + + //! Current orderbook + t_synchronized_ticker_pair m_synchronized_ticker_pair{std::make_pair(g_primary_dex_coin, g_second_primary_dex_coin)}; + t_synchronized_max_taker_vol m_synchronized_max_taker_vol; + t_synchronized_min_taker_vol m_synchronized_min_taker_vol; + + //! Timers + t_mm2_time_point m_orderbook_clock; + t_mm2_time_point m_info_clock; + + //! Atomicity / Threads + std::atomic_bool m_mm2_running{false}; + std::atomic_bool m_orderbook_thread_active{false}; + std::atomic_bool m_zhtlc_enable_thread_active{false}; + std::atomic_size_t m_nb_update_required{0}; + std::thread m_mm2_init_thread; + + //! Current wallet name + std::string m_current_wallet_name; + + //! Mutex + mutable std::shared_mutex m_balance_mutex; + mutable std::shared_mutex m_coin_cfg_mutex; + mutable std::shared_mutex m_raw_coin_cfg_mutex; + + //! Concurrent Registry. + t_coins_registry& m_coins_informations{entity_registry_.set()}; + t_balance_registry m_balance_informations; + t_tx_registry m_tx_informations; + t_orderbook m_orderbook{t_orderbook_answer{}}; + t_orders_and_swaps m_orders_and_swaps{orders_and_swaps{}}; + t_mm2_raw_coins_registry m_mm2_raw_coins_cfg{parse_raw_mm2_coins_file()}; + + //! Balance factor + double m_balance_factor{1.0}; + + //! Refresh the orderbook registry (internal) + nlohmann::json prepare_batch_orderbook(bool is_a_reset); + + //! Batch balance / tx + std::tuple, std::vector> prepare_batch_balance_and_tx(bool only_tx = false) const; + auto batch_balance_and_tx(bool is_a_reset, std::vector tickers = {}, bool is_during_enabling = false, bool only_tx = false); + void process_balance_answer(const nlohmann::json& answer); + void process_tx_answer(const nlohmann::json& answer_json, std::string ticker); + void process_tx_tokenscan(const std::string& ticker, bool is_a_refresh); + void fetch_single_balance(const coin_config& cfg_infos); + + //! + std::pair process_batch_enable_answer(const nlohmann::json& answer); + [[nodiscard]] std::pair get_tx(t_mm2_ec& ec) const; + std::vector get_electrum_server_from_token(const std::string& ticker); + std::vector retrieve_coins_informations(); + + void handle_exception_pplx_task(pplx::task previous_task, const std::string& from, nlohmann::json batch); + + public: + //! Constructor + explicit mm2_service(entt::registry& registry, ag::ecs::system_manager& system_manager); + + //! Delete useless operator + mm2_service(const mm2_service& other) = delete; + mm2_service(const mm2_service&& other) = delete; + mm2_service& operator=(const mm2_service& other) = delete; + mm2_service& operator=(const mm2_service&& other) = delete; + + //! Destructor + ~mm2_service() final; + + //! Events + void on_refresh_orderbook(const orderbook_refresh& evt); + + void on_gui_enter_trading(const gui_enter_trading& evt); + + void on_gui_leave_trading(const gui_leave_trading& evt); + + void on_zhtlc_enter_enabling(const zhtlc_enter_enabling& evt); + + void on_zhtlc_leave_enabling(const zhtlc_leave_enabling& evt); + + //! Spawn mm2 instance with given seed + void spawn_mm2_instance(std::string wallet_name, std::string passphrase, bool with_pin_cfg = false); + + //! Refresh the current info (internally call process_balance and process_tx) + void fetch_infos_thread(bool is_a_fresh = true, bool only_tx = false); + + // Coins enabling functions + bool enable_default_coins(); // Enables required coins + coins enabled in the config + void enable_coins(const std::vector& tickers); + void enable_coins(const t_coins& coins); + void enable_coin(const std::string& ticker); + void enable_coin(const coin_config& coin_config); + private: + void enable_erc_family_coin(const coin_config& coin_config); + void enable_erc_family_coins(const t_coins& coins); + void enable_utxo_qrc20_coin(coin_config coin_config); + void enable_utxo_qrc20_coins(const t_coins& coins); + void enable_slp_coin(coin_config coin_config); + void enable_slp_coins(const t_coins& coins); + void enable_slp_testnet_coin(coin_config coin_config); + void enable_slp_testnet_coins(const t_coins& coins); + void enable_zhtlc(const t_coins& coins); + + // Balances processing functions + void process_balance_answer(const mm2::enable_bch_with_tokens_rpc& rpc); // Called after enabling SLP coins along tBCH/BCH. + void process_balance_answer(const mm2::enable_slp_rpc& rpc); // Called after enabling an SLP coin. + + public: + //! Add a new coin in the coin_info cfg add_new_coin(normal_cfg, mm2_cfg) + void add_new_coin(const nlohmann::json& coin_cfg_json, const nlohmann::json& raw_coin_cfg_json); + void remove_custom_coin(const std::string& ticker); + [[nodiscard]] bool is_this_ticker_present_in_raw_cfg(const std::string& ticker) const; + [[nodiscard]] bool is_this_ticker_present_in_normal_cfg(const std::string& ticker) const; + [[nodiscard]] bool is_zhtlc_coin_ready(const std::string coin) const; + + //! Disable a single coin + bool disable_coin(const std::string& ticker, std::error_code& ec); + + //! Disable multiple coins, prefer this function if you want persistent disabling + void disable_multiple_coins(const std::vector& tickers); - class ENTT_API mm2_service final : public ag::ecs::pre_update_system - { - public: - using t_pair_max_vol = std::pair; - using t_pair_min_vol = std::pair; - - private: - using t_mm2_time_point = std::chrono::high_resolution_clock::time_point; - using t_balance_registry = std::unordered_map; - using t_tx_registry = t_shared_synchronized_value>>; - using t_orderbook = boost::synchronized_value; - using t_orders_and_swaps = boost::synchronized_value; - using t_synchronized_ticker_pair = boost::synchronized_value>; - using t_synchronized_max_taker_vol = boost::synchronized_value; - using t_synchronized_min_taker_vol = boost::synchronized_value; - using t_synchronized_ticker = boost::synchronized_value; - - ag::ecs::system_manager& m_system_manager; - - mm2::mm2_client m_mm2_client; - - //! Current ticker - t_synchronized_ticker m_current_ticker{g_primary_dex_coin}; - - //! Current orderbook - t_synchronized_ticker_pair m_synchronized_ticker_pair{std::make_pair(g_primary_dex_coin, g_second_primary_dex_coin)}; - t_synchronized_max_taker_vol m_synchronized_max_taker_vol; - t_synchronized_min_taker_vol m_synchronized_min_taker_vol; - - //! Timers - t_mm2_time_point m_orderbook_clock; - t_mm2_time_point m_info_clock; - - //! Atomicity / Threads - std::atomic_bool m_mm2_running{false}; - std::atomic_bool m_orderbook_thread_active{false}; - std::atomic_bool m_zhtlc_enable_thread_active{false}; - std::atomic_size_t m_nb_update_required{0}; - std::thread m_mm2_init_thread; - - //! Current wallet name - std::string m_current_wallet_name; - - mutable std::shared_mutex m_balance_mutex; - mutable std::shared_mutex m_coin_cfg_mutex; - mutable std::shared_mutex m_raw_coin_cfg_mutex; - - //! Concurrent Registry. - t_coins_registry& m_coins_informations{entity_registry_.set()}; - t_balance_registry m_balance_informations; - t_tx_registry m_tx_informations; - t_orderbook m_orderbook{t_orderbook_answer{}}; - t_orders_and_swaps m_orders_and_swaps{orders_and_swaps{}}; - t_mm2_raw_coins_registry m_mm2_raw_coins_cfg{parse_raw_mm2_coins_file()}; - - //! Balance factor - double m_balance_factor{1.0}; - - //! Refresh the orderbook registry (internal) - nlohmann::json prepare_batch_orderbook(bool is_a_reset); - - //! Batch balance / tx - std::tuple, std::vector> prepare_batch_balance_and_tx(bool only_tx = false) const; - auto batch_balance_and_tx(bool is_a_reset, std::vector tickers = {}, bool is_during_enabling = false, bool only_tx = false); - void process_balance_answer(const nlohmann::json& answer); - void process_tx_answer(const nlohmann::json& answer_json, std::string ticker); - void process_tx_tokenscan(const std::string& ticker, bool is_a_refresh); - void fetch_single_balance(const coin_config& cfg_infos); - - //! - std::pair process_batch_enable_answer(const nlohmann::json& answer); - [[nodiscard]] std::pair get_tx(t_mm2_ec& ec) const; - std::vector get_electrum_server_from_token(const std::string& ticker); - std::vector retrieve_coins_informations(); - - void handle_exception_pplx_task(pplx::task previous_task, const std::string& from, nlohmann::json batch); - - public: - explicit mm2_service(entt::registry& registry, ag::ecs::system_manager& system_manager); - mm2_service(const mm2_service& other) = delete; - mm2_service(const mm2_service&& other) = delete; - mm2_service& operator=(const mm2_service& other) = delete; - mm2_service& operator=(const mm2_service&& other) = delete; - - ~mm2_service() final; - - //! Events - void on_refresh_orderbook(const orderbook_refresh& evt); - - void on_gui_enter_trading(const gui_enter_trading& evt); - - void on_gui_leave_trading(const gui_leave_trading& evt); - - void on_zhtlc_enter_enabling(const zhtlc_enter_enabling& evt); - - void on_zhtlc_leave_enabling(const zhtlc_leave_enabling& evt); - - //! Spawn mm2 instance with given seed - void spawn_mm2_instance(std::string wallet_name, std::string passphrase, bool with_pin_cfg = false); - - // Refresh the current info (internally call process_balance and process_tx) - void fetch_infos_thread(bool is_a_fresh = true, bool only_tx = false); - void cancel_fetch_infos_thread(); - - // Coins enabling functions - bool enable_default_coins(); // Enables required coins + coins enabled in the config - void enable_coins(const std::vector& tickers); - void enable_coins(const t_coins& coins); - void enable_coin(const std::string& ticker); - void enable_coin(const coin_config& coin_config); - private: - void enable_erc_family_coin(const coin_config& coin_config); - void enable_erc_family_coins(const t_coins& coins); - void enable_utxo_qrc20_coin(coin_config coin_config); - void enable_utxo_qrc20_coins(const t_coins& coins); - void enable_slp_coin(coin_config coin_config); - void enable_slp_coins(const t_coins& coins); - void enable_slp_testnet_coin(coin_config coin_config); - void enable_slp_testnet_coins(const t_coins& coins); - void enable_zhtlc(const t_coins& coins); - - // Balances processing functions - void process_balance_answer(const mm2::enable_bch_with_tokens_rpc& rpc); // Called after enabling SLP coins along tBCH/BCH. - void process_balance_answer(const mm2::enable_slp_rpc& rpc); // Called after enabling an SLP coin. - - public: - //! Add a new coin in the coin_info cfg add_new_coin(normal_cfg, mm2_cfg) - void add_new_coin(const nlohmann::json& coin_cfg_json, const nlohmann::json& raw_coin_cfg_json); - void remove_custom_coin(const std::string& ticker); - [[nodiscard]] bool is_this_ticker_present_in_raw_cfg(const std::string& ticker) const; - [[nodiscard]] bool is_this_ticker_present_in_normal_cfg(const std::string& ticker) const; - [[nodiscard]] bool is_zhtlc_coin_ready(const std::string coin) const; + //! Called every ticks, and execute tasks if the timer expire. + void update() final; - //! Disable a single coin - bool disable_coin(const std::string& ticker, std::error_code& ec); - - //! Disable multiple coins, prefer this function if you want persistent disabling - void disable_multiple_coins(const std::vector& tickers); + //! Retrieve public address of the given ticker + std::string address(const std::string& ticker, t_mm2_ec& ec) const; - //! Called every ticks, and execute tasks if the timer expire. - void update() final; + //! Is MM2 Process correctly running ? + [[nodiscard]] const std::atomic_bool& is_mm2_running() const; - //! Retrieve public address of the given ticker - std::string address(const std::string& ticker, t_mm2_ec& ec) const; + //! Retrieve my balance for a given ticker as a string. + [[nodiscard]] std::string my_balance(const std::string& ticker, t_mm2_ec& ec) const; - //! Is MM2 Process correctly running ? - [[nodiscard]] const std::atomic_bool& is_mm2_running() const; - - //! Retrieve my balance for a given ticker as a string. - [[nodiscard]] std::string my_balance(const std::string& ticker, t_mm2_ec& ec) const; + //! Refresh the current orderbook (internally call process_orderbook) + void fetch_current_orderbook_thread(bool is_a_reset = false); - //! Refresh the current orderbook (internally call process_orderbook) - void fetch_current_orderbook_thread(bool is_a_reset = false); + void process_orderbook(bool is_a_reset = false); - void process_orderbook(bool is_a_reset = false); + //! Last 50 transactions maximum + [[nodiscard]] t_transactions get_tx_history(t_mm2_ec& ec) const; - //! Last 50 transactions maximum - [[nodiscard]] t_transactions get_tx_history(t_mm2_ec& ec) const; + //! Last 50 transactions maximum + [[nodiscard]] t_tx_state get_tx_state(t_mm2_ec& ec) const; - //! Last 50 transactions maximum - [[nodiscard]] t_tx_state get_tx_state(t_mm2_ec& ec) const; + //! Get coins that are currently enabled + [[nodiscard]] t_coins get_enabled_coins() const; - //! Get coins that are currently enabled - [[nodiscard]] t_coins get_enabled_coins() const; + //! Get coins that are active, but may be not enabled + [[nodiscard]] t_coins get_active_coins() const; - //! Get coins that are active, but may be not enabled - [[nodiscard]] t_coins get_active_coins() const; + //! Get Specific info about one coin + [[nodiscard]] coin_config get_coin_info(const std::string& ticker) const; + + // Tells if the given coin is enabled. + [[nodiscard]] bool is_coin_enabled(const std::string& ticker) const; + + // Tells if the given is coin is present inside the config. + [[nodiscard]] bool has_coin(const std::string& ticker) const; - //! Get Specific info about one coin - [[nodiscard]] coin_config get_coin_info(const std::string& ticker) const; - - // Tells if the given coin is enabled. - [[nodiscard]] bool is_coin_enabled(const std::string& ticker) const; - - // Tells if the given is coin is present inside the config. - [[nodiscard]] bool has_coin(const std::string& ticker) const; + //! Get Current orderbook + [[nodiscard]] t_orderbook_answer get_orderbook(t_mm2_ec& ec) const; - //! Get Current orderbook - [[nodiscard]] t_orderbook_answer get_orderbook(t_mm2_ec& ec) const; + //! Get Swaps + [[nodiscard]] orders_and_swaps get_orders_and_swaps() const; - //! Get Swaps - [[nodiscard]] orders_and_swaps get_orders_and_swaps() const; + //! Get balance with locked funds for a given ticker as a boost::multiprecision::cpp_dec_float_50. + [[nodiscard]] t_float_50 get_balance(const std::string& ticker) const; - //! Get balance with locked funds for a given ticker as a boost::multiprecision::cpp_dec_float_50. - [[nodiscard]] t_float_50 get_balance(const std::string& ticker) const; + //! Return true if we the balance of the `ticker` > amount, false otherwise. + [[nodiscard]] bool do_i_have_enough_funds(const std::string& ticker, const t_float_50& amount) const; - //! Return true if we the balance of the `ticker` > amount, false otherwise. - [[nodiscard]] bool do_i_have_enough_funds(const std::string& ticker, const t_float_50& amount) const; + [[nodiscard]] bool is_orderbook_thread_active() const; + [[nodiscard]] bool is_zhtlc_enable_thread_active() const; - [[nodiscard]] bool is_orderbook_thread_active() const; - [[nodiscard]] bool is_zhtlc_enable_thread_active() const; + [[nodiscard]] nlohmann::json get_raw_mm2_ticker_cfg(const std::string& ticker) const; - [[nodiscard]] nlohmann::json get_raw_mm2_ticker_cfg(const std::string& ticker) const; + [[nodiscard]] t_pair_max_vol get_taker_vol() const; + [[nodiscard]] t_pair_min_vol get_min_vol() const; - [[nodiscard]] t_pair_max_vol get_taker_vol() const; - [[nodiscard]] t_pair_min_vol get_min_vol() const; + //! Pin cfg api + [[nodiscard]] bool is_pin_cfg_enabled() const; + void reset_fake_balance_to_zero(const std::string& ticker); + void decrease_fake_balance(const std::string& ticker, const std::string& amount); + void batch_fetch_orders_and_swap(bool after_manual_reset = false); + + //! Async API + mm2::mm2_client& get_mm2_client(); + + //! Wallet api + [[nodiscard]] std::string get_current_ticker() const; + bool set_current_ticker(const std::string& ticker); - //! Pin cfg api - [[nodiscard]] bool is_pin_cfg_enabled() const; - void reset_fake_balance_to_zero(const std::string& ticker); - void decrease_fake_balance(const std::string& ticker, const std::string& amount); - void batch_fetch_orders_and_swap(bool after_manual_reset = false); - - //! Async API - mm2::mm2_client& get_mm2_client(); - - //! Pagination - void set_orders_and_swaps_pagination_infos(std::size_t current_page = 1, std::size_t limit = 50, t_filtering_infos infos = {}); + //! Pagination + void set_orders_and_swaps_pagination_infos(std::size_t current_page = 1, std::size_t limit = 50, t_filtering_infos infos = {}); - void change_segwit_status(std::string ticker, bool status); - }; + void change_segwit_status(std::string ticker, bool status); + }; } // namespace atomic_dex REFL_AUTO(type(atomic_dex::mm2_service)) From 544f8a387566a573bbc5d36f0632431a01914bc6 Mon Sep 17 00:00:00 2001 From: MerlinMagic2018 <36725554+MerlinMagic2018@users.noreply.github.com> Date: Thu, 15 Sep 2022 04:57:08 -0400 Subject: [PATCH 11/17] Update 0.5.6-coins.json --> LNC 2nd electrumx --- assets/config/0.5.6-coins.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/config/0.5.6-coins.json b/assets/config/0.5.6-coins.json index ae0389bf41..84bac98de6 100644 --- a/assets/config/0.5.6-coins.json +++ b/assets/config/0.5.6-coins.json @@ -3969,6 +3969,9 @@ "electrum": [ { "url": "74.208.95.26:50001" + }, + { + "url": "74.56.11.23:50001" } ], "explorer_url": [ From 5126c7bfddc5022e9932889821eab9ba7d84d09b Mon Sep 17 00:00:00 2001 From: ShorelineCrypto Date: Wed, 21 Sep 2022 20:10:59 -0400 Subject: [PATCH 12/17] add Cheetahcoin --- assets/config/0.5.6-coins.json | 24 ++++++++++++++++++++++ atomic_defi_design/Dex/Constants/Style.qml | 1 + 2 files changed, 25 insertions(+) diff --git a/assets/config/0.5.6-coins.json b/assets/config/0.5.6-coins.json index 57edb486b5..553cb5d258 100644 --- a/assets/config/0.5.6-coins.json +++ b/assets/config/0.5.6-coins.json @@ -1889,6 +1889,30 @@ "currently_enabled": false, "wallet_only": true }, + "CHTA": { + "coin": "CHTA", + "name": "Cheetahcoin", + "type": "UTXO", + "coingecko_id": "test-coin", + "coinpaprika_id": "chta-cheetahcoin", + "nomics_id": "CHTA", + "electrum": [ + { + "url": "electrum.shorelinecrypto.com:10007" + }, + { + "url": "electrum1.mooo.com:10007" + }, + { + "url": "electrum2.mooo.com:10007" + } + ], + "explorer_url": [ + "http://chtaexplorer.mooo.com:3002/" + ], + "active": false, + "currently_enabled": false + }, "CLC": { "coin": "CLC", "name": "Collider Coin", diff --git a/atomic_defi_design/Dex/Constants/Style.qml b/atomic_defi_design/Dex/Constants/Style.qml index 3d5ff4f4e4..4137b3a58b 100644 --- a/atomic_defi_design/Dex/Constants/Style.qml +++ b/atomic_defi_design/Dex/Constants/Style.qml @@ -384,6 +384,7 @@ QtObject { "CEL": "#4055A6", "CELR": "#595959", "CENNZ": "#2E87F1", + "CHTA": "#C3A634", "COMP": "#00DBA3", "CRO": "#243565", "CVC": "#3AB03E", From 3ec2c348b542d8b9bda6c1d85290e0e9f25ef0b0 Mon Sep 17 00:00:00 2001 From: ShorelineCrypto Date: Wed, 21 Sep 2022 20:22:49 -0400 Subject: [PATCH 13/17] add CHTA image --- atomic_defi_design/assets/images/coins/chta.png | Bin 0 -> 28045 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 atomic_defi_design/assets/images/coins/chta.png diff --git a/atomic_defi_design/assets/images/coins/chta.png b/atomic_defi_design/assets/images/coins/chta.png new file mode 100755 index 0000000000000000000000000000000000000000..250081362e749a3c55e23b435e1ced4e204494d6 GIT binary patch literal 28045 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 P00000NkvXXu0mjfye>9D literal 0 HcmV?d00001 From 078ea96436663ceb66bc6cc64101a3125338733f Mon Sep 17 00:00:00 2001 From: smk762 Date: Fri, 23 Sep 2022 23:29:19 +0800 Subject: [PATCH 14/17] use listindex --- atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml b/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml index 41a4ecd409..82be149a84 100644 --- a/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml +++ b/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml @@ -15,7 +15,7 @@ GradientRectangle height: 44 radius: Dex.Style.rectangleCornerRadius + 4 - start_color: api_wallet_page.ticker === ticker ? Dex.DexTheme.buttonColorEnabled : mouse_area.containsMouse ? Dex.DexTheme.buttonColorHovered : 'transparent' + start_color: list.currentIndex == index ? Dex.DexTheme.buttonColorEnabled : mouse_area.containsMouse ? Dex.DexTheme.buttonColorHovered : 'transparent' end_color: 'transparent' // Click area @@ -28,7 +28,12 @@ GradientRectangle onClicked: { if (mouse.button === Qt.RightButton) context_menu.popup() - else api_wallet_page.ticker = ticker + else + { + api_wallet_page.ticker = ticker + if(list.currentIndex != index) + list.currentIndex = index + } } onPressAndHold: if (mouse.source === Qt.MouseEventNotSynthesized) context_menu.popup() } From 3b58eb708a7e2429274cc21d2147488d9200d25e Mon Sep 17 00:00:00 2001 From: smk762 Date: Tue, 27 Sep 2022 02:38:45 +0800 Subject: [PATCH 15/17] use v2 request format for bestorders --- src/core/atomicdex/services/price/orderbook.scanner.service.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/atomicdex/services/price/orderbook.scanner.service.cpp b/src/core/atomicdex/services/price/orderbook.scanner.service.cpp index 42f7645cb4..ca6fbce557 100644 --- a/src/core/atomicdex/services/price/orderbook.scanner.service.cpp +++ b/src/core/atomicdex/services/price/orderbook.scanner.service.cpp @@ -60,7 +60,7 @@ namespace atomic_dex //! Prepare request nlohmann::json batch = nlohmann::json::array(); - nlohmann::json best_orders_req_json = mm2::template_request("best_orders"); + nlohmann::json best_orders_req_json = mm2::template_request("best_orders", true); to_json(best_orders_req_json, req); batch.push_back(best_orders_req_json); From 9cb2868e3e612a4c2f8e492a1a5f84a51bb27301 Mon Sep 17 00:00:00 2001 From: alamshafil Date: Mon, 26 Sep 2022 18:30:39 -0400 Subject: [PATCH 16/17] Use correct Nomics ID for AVN --- assets/config/0.5.6-coins.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/config/0.5.6-coins.json b/assets/config/0.5.6-coins.json index 7cb97a0fc7..be4b2c5024 100644 --- a/assets/config/0.5.6-coins.json +++ b/assets/config/0.5.6-coins.json @@ -5233,7 +5233,7 @@ "name": "Avian", "coinpaprika_id": "avn-avian", "coingecko_id": "avian-network", - "nomics_id": "avn3-avian", + "nomics_id": "AVN3", "electrum": [ { "url": "electrum-us.avn.network:50001", From 594f0437213e7a27cb4c05dcad0213653df238b1 Mon Sep 17 00:00:00 2001 From: smk762 Date: Fri, 23 Sep 2022 23:29:19 +0800 Subject: [PATCH 17/17] Revert "use listindex" This reverts commit 078ea96436663ceb66bc6cc64101a3125338733f. --- atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml b/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml index 82be149a84..41a4ecd409 100644 --- a/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml +++ b/atomic_defi_design/Dex/Wallet/SidebarItemDelegate.qml @@ -15,7 +15,7 @@ GradientRectangle height: 44 radius: Dex.Style.rectangleCornerRadius + 4 - start_color: list.currentIndex == index ? Dex.DexTheme.buttonColorEnabled : mouse_area.containsMouse ? Dex.DexTheme.buttonColorHovered : 'transparent' + start_color: api_wallet_page.ticker === ticker ? Dex.DexTheme.buttonColorEnabled : mouse_area.containsMouse ? Dex.DexTheme.buttonColorHovered : 'transparent' end_color: 'transparent' // Click area @@ -28,12 +28,7 @@ GradientRectangle onClicked: { if (mouse.button === Qt.RightButton) context_menu.popup() - else - { - api_wallet_page.ticker = ticker - if(list.currentIndex != index) - list.currentIndex = index - } + else api_wallet_page.ticker = ticker } onPressAndHold: if (mouse.source === Qt.MouseEventNotSynthesized) context_menu.popup() }