diff --git a/CMakeLists.txt b/CMakeLists.txt index 2372d2063..fcb11395f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,7 @@ FetchContent_Declare( ) FetchContent_Declare(jl777-coins - URL https://github.com/KomodoPlatform/coins/archive/master.zip) + URL https://github.com/KomodoPlatform/coins/archive/add/tendermint-coins.zip) #FetchContent_Declare(adex-generics-coins # URL https://github.com/KomodoPlatform/komodo-wallet-desktop/archive/main.zip) diff --git a/atomic_defi_design/Dex/Components/AddressField.qml b/atomic_defi_design/Dex/Components/AddressField.qml index aa972a1a9..d3c31e846 100644 --- a/atomic_defi_design/Dex/Components/AddressField.qml +++ b/atomic_defi_design/Dex/Components/AddressField.qml @@ -1,6 +1,6 @@ import QtQuick 2.15 -DefaultTextField { +DexTextField { readonly property int max_length: 50 validator: RegExpValidator { diff --git a/atomic_defi_design/Dex/Components/AmountField.qml b/atomic_defi_design/Dex/Components/AmountField.qml index 1d280a8bc..ed2ce8d9f 100644 --- a/atomic_defi_design/Dex/Components/AmountField.qml +++ b/atomic_defi_design/Dex/Components/AmountField.qml @@ -1,6 +1,6 @@ import QtQuick 2.15 -DefaultTextField +DexTextField { validator: RegExpValidator { diff --git a/atomic_defi_design/Dex/Components/AmountFloatField.qml b/atomic_defi_design/Dex/Components/AmountFloatField.qml index 06af631e1..17bc43ef5 100644 --- a/atomic_defi_design/Dex/Components/AmountFloatField.qml +++ b/atomic_defi_design/Dex/Components/AmountFloatField.qml @@ -1,6 +1,6 @@ import QtQuick 2.15 -DefaultTextField +DexTextField { validator: RegExpValidator { diff --git a/atomic_defi_design/Dex/Components/AmountIntField.qml b/atomic_defi_design/Dex/Components/AmountIntField.qml index 30dce4be6..6a78de3d8 100644 --- a/atomic_defi_design/Dex/Components/AmountIntField.qml +++ b/atomic_defi_design/Dex/Components/AmountIntField.qml @@ -1,6 +1,6 @@ import QtQuick 2.15 -DefaultTextField +DexTextField { property bool allowFloat: false validator: RegExpValidator diff --git a/atomic_defi_design/Dex/Components/DefaultTextField.qml b/atomic_defi_design/Dex/Components/DefaultTextField.qml deleted file mode 100644 index a0c57e16c..000000000 --- a/atomic_defi_design/Dex/Components/DefaultTextField.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import "../Constants" -import App 1.0 - -DexTextField { - -} diff --git a/atomic_defi_design/Dex/Components/DexSweetComboBox.qml b/atomic_defi_design/Dex/Components/DexSweetComboBox.qml index cc47e64e6..cacef269b 100644 --- a/atomic_defi_design/Dex/Components/DexSweetComboBox.qml +++ b/atomic_defi_design/Dex/Components/DexSweetComboBox.qml @@ -85,7 +85,7 @@ ComboBox { anchors.rightMargin: 5 - DefaultTextField + DexTextField { id: input_coin_filter placeholderText: qsTr("Search") diff --git a/atomic_defi_design/Dex/Components/SearchField.qml b/atomic_defi_design/Dex/Components/SearchField.qml index 40bcae12d..34353082e 100644 --- a/atomic_defi_design/Dex/Components/SearchField.qml +++ b/atomic_defi_design/Dex/Components/SearchField.qml @@ -37,7 +37,7 @@ Rectangle } } - DefaultTextField + DexTextField { id: _textField diff --git a/atomic_defi_design/Dex/Components/TextFieldWithTitle.qml b/atomic_defi_design/Dex/Components/TextFieldWithTitle.qml index e0bd8aae3..4d74cd8f3 100644 --- a/atomic_defi_design/Dex/Components/TextFieldWithTitle.qml +++ b/atomic_defi_design/Dex/Components/TextFieldWithTitle.qml @@ -51,7 +51,7 @@ ColumnLayout } } - DefaultTextField + DexTextField { id: input_field diff --git a/atomic_defi_design/Dex/Components/qmldir b/atomic_defi_design/Dex/Components/qmldir index dab879424..248066ada 100644 --- a/atomic_defi_design/Dex/Components/qmldir +++ b/atomic_defi_design/Dex/Components/qmldir @@ -16,7 +16,7 @@ Rectangle 1.0 DefaultRectangle.qml ScrollView 1.0 DefaultScrollView.qml SearchField 1.0 SearchField.qml Text 1.0 DefaultText.qml -TextField 1.0 DefaultTextField.qml +TextField 1.0 DexTextField.qml TextFieldWithTitle 1.0 TextFieldWithTitle.qml ToolTip 1.0 DefaultTooltip.qml UserIcon 1.0 UserIcon.qml diff --git a/atomic_defi_design/Dex/Screens/Startup/WalletsView.qml b/atomic_defi_design/Dex/Screens/Startup/WalletsView.qml index 32b0c8592..e0b0fff90 100644 --- a/atomic_defi_design/Dex/Screens/Startup/WalletsView.qml +++ b/atomic_defi_design/Dex/Screens/Startup/WalletsView.qml @@ -79,7 +79,7 @@ SetupPage visible: wallet_count > 0 // Searchbar - DefaultTextField + DexTextField { id: wallet_search visible: wallet_count > 5 diff --git a/atomic_defi_design/Dex/Settings/RecoverSeedModal.qml b/atomic_defi_design/Dex/Settings/RecoverSeedModal.qml index 39253a778..967b29227 100644 --- a/atomic_defi_design/Dex/Settings/RecoverSeedModal.qml +++ b/atomic_defi_design/Dex/Settings/RecoverSeedModal.qml @@ -262,7 +262,7 @@ MultipageModal HorizontalLine { Layout.fillWidth: true } - DefaultTextField + DexTextField { visible: coinsList.visible enabled: coinsList.enabled diff --git a/atomic_defi_design/Dex/Wallet/SendModal.qml b/atomic_defi_design/Dex/Wallet/SendModal.qml index aedb0f768..0b19e98ee 100644 --- a/atomic_defi_design/Dex/Wallet/SendModal.qml +++ b/atomic_defi_design/Dex/Wallet/SendModal.qml @@ -47,7 +47,7 @@ MultipageModal function getCryptoAmount() { return _preparePage.cryptoSendMode ? input_amount.text : equivalentAmount.value } - function prepareSendCoin(address, amount, with_fees, fees_amount, is_special_token, gas_limit, gas_price, memo="") { + function prepareSendCoin(address, amount, with_fees, fees_amount, is_special_token, gas_limit, gas_price, memo="", ibc_source_channel="") { let max = parseFloat(current_ticker_infos.balance) === parseFloat(amount) // Save for later check async_param_max = max @@ -60,7 +60,7 @@ MultipageModal gas_price, gas_limit: gas_limit === "" ? 0 : parseInt(gas_limit) } - api_wallet_page.send(address, amount, max, with_fees, fees_info, memo) + api_wallet_page.send(address, amount, max, with_fees, fees_info, memo, ibc_source_channel) } function sendCoin() { @@ -263,7 +263,7 @@ MultipageModal color: input_address.background.color radius: input_address.background.radius - DefaultTextField + DexTextField { id: input_address @@ -567,7 +567,7 @@ MultipageModal color: input_memo.background.color radius: input_memo.background.radius - DefaultTextField + DexTextField { id: input_memo @@ -579,6 +579,31 @@ MultipageModal } } + // IBC channel ID + DefaultRectangle + { + visible: (["TENDERMINT", "TENDERMINTTOKEN"].includes(current_ticker_infos.type)) + enabled: !root.is_send_busy + + Layout.preferredWidth: 500 + Layout.preferredHeight: 44 + Layout.alignment: Qt.AlignHCenter + + color: input_memo.background.color + radius: input_memo.background.radius + + DexTextField + { + id: input_ibc_channel_id + + width: 470 + height: 44 + placeholderText: qsTr("Enter IBC channel ID") + forceFocus: true + font: DexTypo.body3 + } + } + ColumnLayout { visible: General.getCustomFeeType(current_ticker_infos) @@ -786,7 +811,8 @@ MultipageModal General.isSpecialToken(current_ticker_infos), input_custom_fees_gas.text, input_custom_fees_gas_price.text, - input_memo.text + input_memo.text, + input_ibc_channel_id.text ) } } diff --git a/atomic_defi_design/Dex/Wallet/SendModalContactList.qml b/atomic_defi_design/Dex/Wallet/SendModalContactList.qml index 9e9bbd58a..ac9ace2a7 100644 --- a/atomic_defi_design/Dex/Wallet/SendModalContactList.qml +++ b/atomic_defi_design/Dex/Wallet/SendModalContactList.qml @@ -26,7 +26,7 @@ MultipageModal titleText: qsTr("Select a contact with an %1 address").arg(ticker) // Searchbar - DefaultTextField + DexTextField { Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true diff --git a/src/app/app.cpp b/src/app/app.cpp index f2bf63ef1..f4e2ed216 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -721,7 +721,7 @@ namespace atomic_dex { SPDLOG_DEBUG("on_fiat_rate_updated"); this->dispatcher_.trigger(); - // this->dispatcher_.trigger(); + this->dispatcher_.trigger(); } void diff --git a/src/core/atomicdex/api/checksum/checksum.api.cpp b/src/core/atomicdex/api/checksum/checksum.api.cpp index 5fa888f2c..989044e20 100644 --- a/src/core/atomicdex/api/checksum/checksum.api.cpp +++ b/src/core/atomicdex/api/checksum/checksum.api.cpp @@ -1,5 +1,6 @@ //! 3rdParty #include +#include //! Project #include "checksum.api.hpp" @@ -7,35 +8,94 @@ namespace atomic_dex::checksum::api { const auto api_client = std::make_unique(FROM_STD_STR(DEX_CHECKSUM_API_URL)); - - pplx::task + + // Helper function for platform-specific file identifiers + std::string get_platform_file_identifier() + { +#ifdef __APPLE__ + return "osx.dmg"; +#elif __linux__ + return "linux.AppImage"; +#elif _WIN32 + return "windows.zip"; +#else + return ""; +#endif + } + + pplx::task> get_latest_checksum() { return api_client->request(web::http::methods::GET) - .then([](web::http::http_response resp) + .then([](web::http::http_response resp) -> std::optional { if (resp.status_code() != 200) { - return TO_STD_STR(resp.extract_string(true).get()); + SPDLOG_ERROR("Failed to fetch checksum. HTTP status: {}", resp.status_code()); + return std::nullopt; } - - const auto json_answer = nlohmann::json::parse(TO_STD_STR(resp.extract_string(true).get())); - - for (auto it = json_answer.begin(); it != json_answer.end(); ++it) + + // Cache the response body to avoid multiple calls to extract_string + std::string body = TO_STD_STR(resp.extract_string(true).get()); + + try { - if (it.key().find( -#ifdef __APPLE__ - "osx.dmg" -#elif __linux__ - "linux.AppImage" -#elif _WIN32 - "windows.zip" -#endif - ) != std::string::npos) - return it.value().get(); + SPDLOG_DEBUG("Checksum API response: {}", body); + const auto json_answer = nlohmann::json::parse(body); + + std::string platform_identifier = get_platform_file_identifier(); + + if (platform_identifier.empty()) + { + SPDLOG_ERROR("Unknown platform! Unable to fetch the correct checksum."); + return std::nullopt; + } + + // Look for the platform-specific checksum + for (const auto& item : json_answer.items()) + { + if (item.key().find(platform_identifier) != std::string::npos) + { + SPDLOG_DEBUG("Found checksum for platform: {}", item.key()); + return item.value().get(); + } + } + + SPDLOG_WARN("Valid checksum not found in the response."); + return std::nullopt; + } + catch (const nlohmann::json::exception& e) + { + SPDLOG_ERROR("JSON parsing error: {}", e.what()); + return std::nullopt; + } + catch (const std::exception& e) + { + SPDLOG_ERROR("Exception while processing checksum: {}", e.what()); + return std::nullopt; + } + }) + .then([](pplx::task> previous_task) -> std::optional + { + try + { + return previous_task.get(); // Get the result of the task, or throw if it failed + } + catch (const web::http::http_exception& e) + { + SPDLOG_ERROR("HTTP exception in get_latest_checksum: {}", e.what()); + return std::nullopt; + } + catch (const std::exception& e) + { + SPDLOG_ERROR("Standard exception in get_latest_checksum: {}", e.what()); + return std::nullopt; + } + catch (...) + { + SPDLOG_ERROR("Unknown exception in get_latest_checksum."); + return std::nullopt; } - - return std::string{"Valid checksum not found!"}; }); } -} \ No newline at end of file +} diff --git a/src/core/atomicdex/api/checksum/checksum.api.hpp b/src/core/atomicdex/api/checksum/checksum.api.hpp index f57840dfe..87eb73d27 100644 --- a/src/core/atomicdex/api/checksum/checksum.api.hpp +++ b/src/core/atomicdex/api/checksum/checksum.api.hpp @@ -6,6 +6,6 @@ namespace atomic_dex::checksum::api { // Returns the checksum of the latest - [[nodiscard]] pplx::task + [[nodiscard]] pplx::task> get_latest_checksum(); } \ No newline at end of file diff --git a/src/core/atomicdex/api/kdf/balance_info.cpp b/src/core/atomicdex/api/kdf/balance_info.cpp deleted file mode 100644 index d300f6a1b..000000000 --- a/src/core/atomicdex/api/kdf/balance_info.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include "atomicdex/api/kdf/balance_info.hpp" - -namespace atomic_dex::kdf -{ - void from_json(const nlohmann::json& j, balance_info& in) - { - j.at("spendable").get_to(in.spendable); - j.at("unspendable").get_to(in.unspendable); - } -} \ No newline at end of file diff --git a/src/core/atomicdex/api/kdf/balance_info.hpp b/src/core/atomicdex/api/kdf/balance_info.hpp deleted file mode 100644 index 94f896197..000000000 --- a/src/core/atomicdex/api/kdf/balance_info.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -#include //> nlohmann::json - -namespace atomic_dex::kdf -{ - struct balance_info - { - std::string spendable; - std::string unspendable; - }; - - void from_json(const nlohmann::json& j, balance_info& in); -} \ No newline at end of file diff --git a/src/core/atomicdex/api/kdf/kdf.client.cpp b/src/core/atomicdex/api/kdf/kdf.client.cpp index 0d01945d3..8e2118c7f 100644 --- a/src/core/atomicdex/api/kdf/kdf.client.cpp +++ b/src/core/atomicdex/api/kdf/kdf.client.cpp @@ -71,45 +71,44 @@ namespace json_req.insert(json_req.end(), json_data); } request.set_body(json_req.dump()); + // SPDLOG_DEBUG("request: {}", json_req.dump()); return request; } + + template Rpc process_rpc_answer(const web::http::http_response& answer) { - std::string body = TO_STD_STR(answer.extract_string(true).get()); - // SPDLOG_DEBUG("body: {}", body); - nlohmann::json json_answer; Rpc rpc; + std::string body; + try { - json_answer = nlohmann::json::parse(body); - // SPDLOG_DEBUG("rpc answer: {}", json_answer.dump(4)); - } - catch (const nlohmann::json::parse_error& error) - { - SPDLOG_ERROR("rpc answer error: {}", error.what()); - } + body = TO_STD_STR(answer.extract_string(true).get()); + nlohmann::json json_answer = nlohmann::json::parse(body); + // SPDLOG_DEBUG("json_answer: {}", json_answer.dump(4)); - if (Rpc::is_v2) - { - if (answer.status_code() == 200) + if (Rpc::is_v2) { - rpc.result = json_answer.at("result").get(); - rpc.raw_result = json_answer.at("result").dump(); + handle_v2_response(answer, json_answer, rpc); } else { - SPDLOG_DEBUG("rpc2 answer: error"); - rpc.error = json_answer.get(); - rpc.raw_result = json_answer.dump(); - SPDLOG_DEBUG("rpc.raw_result: {}", rpc.raw_result); + handle_v1_response(json_answer, rpc); } } - else + catch (const nlohmann::json::parse_error& error) { - rpc.result = json_answer.get(); + SPDLOG_ERROR("RPC answer JSON parsing error: {}", error.what()); + rpc.raw_result = body; + } + catch (const std::exception& e) + { + SPDLOG_ERROR("Exception in RPC processing: {}", e.what()); + rpc.raw_result = body; } + return rpc; } } // namespace @@ -172,11 +171,55 @@ namespace atomic_dex::kdf pplx::task kdf_client::async_rpc_batch_standalone(nlohmann::json batch_array) { + // Create the HTTP request object web::http::http_request request; request.set_method(web::http::methods::POST); - request.set_body(batch_array.dump()); - auto resp = generate_client().request(request, m_token_source.get_token()); - return resp; + + try + { + // Serialize the batch array to a string and set it as the body + std::string body = batch_array.dump(); + request.set_body(body); + + // Log the outgoing request for debugging + // auto json_copy = batch_array[0]; + // json_copy["userpass"] = "*******"; + // SPDLOG_INFO("Sending RPC batch request with {} commands. Body of first request: {}", batch_array.size(), json_copy.dump()); + + // Generate the client and send the request, passing the cancellation token + auto resp = generate_client().request(request, m_token_source.get_token()); + + // Return the response task to the caller, handling errors asynchronously + return resp.then([body](pplx::task previousTask) + { + try + { + // Retrieve the HTTP response from the task + auto response = previousTask.get(); + // Extract the response body as a string + // auto resp_status = response.status_code(); + // Log the response body + // SPDLOG_INFO("Body: {} \nHTTP status: {}", body, resp_status); + return response; + } + catch (const web::http::http_exception& e) + { + SPDLOG_ERROR("HTTP exception caught during batch request: {}", e.what()); + throw; // Re-throw or return an appropriate error response + } + catch (const std::exception& e) + { + SPDLOG_ERROR("General exception caught during batch request: {}", e.what()); + throw; // Re-throw to allow the caller to handle it + } + }); + } + catch (const std::exception& e) + { + // Catch any exceptions from batch_array.dump() or request creation + SPDLOG_ERROR("Exception caught during batch request creation: {}", e.what()); + throw; // Re-throw or handle the exception appropriately + } } template @@ -266,6 +309,51 @@ namespace atomic_dex::kdf return process_rpc( std::forward(request), "recover_funds_of_swap"); } + + // Helper functions to reduce code duplication and improve clarity + template + void handle_v2_response(const web::http::http_response& answer, const nlohmann::json& json_answer, Rpc& rpc) + { + if (answer.status_code() == 200) + { + if (json_answer.contains("result")) + { + rpc.result = json_answer.at("result").get(); + rpc.raw_result = json_answer.at("result").dump(); + } + else + { + SPDLOG_WARN("Expected 'result' field missing in v2 response"); + } + } + else + { + if (json_answer.contains("error")) + { + rpc.error = json_answer.get(); + } + else + { + SPDLOG_WARN("Expected 'error' field missing in v2 error response"); + } + rpc.raw_result = json_answer.dump(); + SPDLOG_DEBUG("RPC v2 error {} answer: {}", answer.status_code(), rpc.raw_result); + } + } + + template + void handle_v1_response(const nlohmann::json& json_answer, Rpc& rpc) + { + if (json_answer.contains("result")) + { + rpc.result = json_answer.get(); + } + else + { + SPDLOG_DEBUG("RPC v1 error: {}", json_answer.dump()); + SPDLOG_WARN("Expected 'result' field missing in v1 response"); + } + } } // namespace atomic_dex template atomic_dex::kdf::tx_history_answer atomic_dex::kdf::kdf_client::rpc_process_answer(const web::http::http_response& resp, const std::string& rpc_command); diff --git a/src/core/atomicdex/api/kdf/kdf.client.hpp b/src/core/atomicdex/api/kdf/kdf.client.hpp index 1ea8a0507..fe8175a8b 100644 --- a/src/core/atomicdex/api/kdf/kdf.client.hpp +++ b/src/core/atomicdex/api/kdf/kdf.client.hpp @@ -40,6 +40,10 @@ namespace atomic_dex::kdf template RpcReturnType rpc_process_answer(const web::http::http_response& resp, const std::string& rpc_command); + template + void handle_v2_response(const web::http::http_response& answer, const nlohmann::json& json_answer, Rpc& rpc); + template + void handle_v1_response(const nlohmann::json& json_answer, Rpc& rpc); t_disable_coin_answer rpc_disable_coin(t_disable_coin_request&& request); t_recover_funds_of_swap_answer rpc_recover_funds(t_recover_funds_of_swap_request&& request); diff --git a/src/core/atomicdex/api/kdf/kdf.cpp b/src/core/atomicdex/api/kdf/kdf.cpp index 25a73d6b8..443d4e3e9 100644 --- a/src/core/atomicdex/api/kdf/kdf.cpp +++ b/src/core/atomicdex/api/kdf/kdf.cpp @@ -711,11 +711,31 @@ namespace atomic_dex::kdf web::http::http_request req; req.set_method(web::http::methods::GET); - if (not url.empty()) + if (!url.empty()) { req.set_request_uri(FROM_STD_STR(url)); } - return client->request(req); + + // Return the task and handle exceptions + return client->request(req) + .then([](pplx::task previousTask) + { + try + { + return previousTask.get(); // Retrieve the HTTP response + } + catch (const web::http::http_exception& e) + { + SPDLOG_ERROR("HTTP exception caught: {}", e.what()); + // Handle the HTTP exception or propagate an error response + throw; // Re-throw or create a failed http_response + } + catch (const std::exception& e) + { + SPDLOG_ERROR("General exception caught: {}", e.what()); + throw; // Re-throw to ensure it's handled by the caller + } + }); } template diff --git a/src/core/atomicdex/api/kdf/kdf.error.code.cpp b/src/core/atomicdex/api/kdf/kdf.error.code.cpp index f55675890..569500449 100644 --- a/src/core/atomicdex/api/kdf/kdf.error.code.cpp +++ b/src/core/atomicdex/api/kdf/kdf.error.code.cpp @@ -40,6 +40,8 @@ namespace { case dextop_error::success: return ""; + case dextop_error::balance_info_exception: + return "There was a problem with the balance info request"; case dextop_error::balance_of_a_non_enabled_coin: return "You try to retrieve the balance of an unactivated coin"; case dextop_error::unknown_error: diff --git a/src/core/atomicdex/api/kdf/kdf.error.code.hpp b/src/core/atomicdex/api/kdf/kdf.error.code.hpp index bfad61b77..2be121ca8 100644 --- a/src/core/atomicdex/api/kdf/kdf.error.code.hpp +++ b/src/core/atomicdex/api/kdf/kdf.error.code.hpp @@ -19,6 +19,7 @@ enum class dextop_error { success, + balance_info_exception, balance_of_a_non_enabled_coin, tx_history_of_a_non_enabled_coin, rpc_withdraw_error, diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_bch_with_tokens_rpc.hpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_bch_with_tokens_rpc.hpp index 038902918..658bc96f7 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_bch_with_tokens_rpc.hpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_bch_with_tokens_rpc.hpp @@ -20,7 +20,7 @@ #include "atomicdex/config/electrum.cfg.hpp" #include "atomicdex/api/kdf/address_format.hpp" -#include "atomicdex/api/kdf/balance_info.hpp" +#include "atomicdex/api/kdf/balance_infos.hpp" #include "atomicdex/api/kdf/rpc.hpp" #include "atomicdex/api/kdf/utxo_merge_params.hpp" @@ -65,13 +65,13 @@ namespace atomic_dex::kdf { derivation_method_t derivation_method; std::string pubkey; - balance_info balances; + balance_infos balances; }; struct slp_address_infos_t { derivation_method_t derivation_method; std::string pubkey; - std::unordered_map balances; + std::unordered_map balances; }; std::size_t current_block; diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_erc20.cpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_erc20.cpp index b36673023..0587f0305 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_erc20.cpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_erc20.cpp @@ -21,6 +21,6 @@ namespace atomic_dex::kdf { j.at("platform_coin").get_to(in.platform_coin); j.at("required_confirmations").get_to(in.required_confirmations); - j.at("balances").get_to>(in.balances); + j.at("balances").get_to>(in.balances); } } \ No newline at end of file diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_erc20.hpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_erc20.hpp index ae2d22a80..c99b07fe7 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_erc20.hpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_erc20.hpp @@ -21,7 +21,7 @@ #include //> nlohmann::json #include "atomicdex/api/kdf/rpc.hpp" -#include "atomicdex/api/kdf/balance_info.hpp" +#include "atomicdex/api/kdf/balance_infos.hpp" namespace atomic_dex::kdf { @@ -40,7 +40,7 @@ namespace atomic_dex::kdf { std::string platform_coin; int required_confirmations; - std::unordered_map balances; + std::unordered_map balances; }; using expected_error_type = rpc_basic_error_type; diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_eth_with_tokens.hpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_eth_with_tokens.hpp index 3b40f1748..7e378aac2 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_eth_with_tokens.hpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_eth_with_tokens.hpp @@ -19,7 +19,7 @@ #include #include "atomicdex/api/kdf/rpc.hpp" -#include "atomicdex/api/kdf/balance_info.hpp" +#include "atomicdex/api/kdf/balance_infos.hpp" #include "atomicdex/config/enable.cfg.hpp" #include "atomicdex/constants/qt.coins.enums.hpp" @@ -60,13 +60,13 @@ namespace atomic_dex::kdf { derivation_method_t derivation_method; std::string pubkey; - balance_info balances; + balance_infos balances; }; struct erc20_address_infos_t { derivation_method_t derivation_method; std::string pubkey; - std::unordered_map balances; + std::unordered_map balances; }; std::size_t current_block; diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_slp_rpc.cpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_slp_rpc.cpp index 9906fae3c..0422df437 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_slp_rpc.cpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_slp_rpc.cpp @@ -22,6 +22,6 @@ namespace atomic_dex::kdf j.at("platform_coin").get_to(in.platform_coin); j.at("required_confirmations").get_to(in.required_confirmations); j.at("token_id").get_to(in.token_id); - j.at("balances").get_to>(in.balances); + j.at("balances").get_to>(in.balances); } } \ No newline at end of file diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_slp_rpc.hpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_slp_rpc.hpp index 1fbe40301..4ed2462a1 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_slp_rpc.hpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_slp_rpc.hpp @@ -23,7 +23,7 @@ #include //> nlohmann::json #include "atomicdex/api/kdf/rpc.hpp" -#include "atomicdex/api/kdf/balance_info.hpp" +#include "atomicdex/api/kdf/balance_infos.hpp" namespace atomic_dex::kdf { @@ -43,7 +43,7 @@ namespace atomic_dex::kdf std::string token_id; std::string platform_coin; int required_confirmations; - std::unordered_map balances; + std::unordered_map balances; }; using expected_error_type = rpc_basic_error_type; diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_token.cpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_token.cpp index 81bafbc1b..cbdf20206 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_token.cpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_token.cpp @@ -20,6 +20,6 @@ namespace atomic_dex::kdf void from_json(const nlohmann::json& j, enable_tendermint_token_rpc_result& in) { j.at("platform_coin").get_to(in.platform_coin); - j.at("balances").get_to>(in.balances); + j.at("balances").get_to>(in.balances); } } \ No newline at end of file diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_token.hpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_token.hpp index 9597d1a38..fcf180a9f 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_token.hpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_token.hpp @@ -22,7 +22,7 @@ #include //> nlohmann::json #include "atomicdex/api/kdf/rpc.hpp" -#include "atomicdex/api/kdf/balance_info.hpp" +#include "atomicdex/api/kdf/balance_infos.hpp" namespace atomic_dex::kdf { @@ -40,7 +40,7 @@ namespace atomic_dex::kdf struct expected_result_type { std::string platform_coin; - std::unordered_map balances; + std::unordered_map balances; }; using expected_error_type = rpc_basic_error_type; @@ -48,7 +48,7 @@ namespace atomic_dex::kdf expected_request_type request; std::optional result; std::optional error; - std::string raw_result; + std::string raw_result; }; using enable_tendermint_token_rpc_request = enable_tendermint_token_rpc::expected_request_type; diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_with_assets.cpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_with_assets.cpp index cc28a43f4..95c578ad9 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_with_assets.cpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_with_assets.cpp @@ -25,6 +25,7 @@ namespace atomic_dex::kdf void from_json(const nlohmann::json& json, enable_tendermint_with_assets_result_rpc& out) { + out.ticker = json["ticker"]; out.address = json["address"]; out.current_block = json["current_block"]; out.tendermint_balances_infos = json["balance"].get(); diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_with_assets.hpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_with_assets.hpp index 6bad7330a..05325f95a 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_with_assets.hpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.enable_tendermint_with_assets.hpp @@ -3,7 +3,7 @@ #include #include "atomicdex/api/kdf/rpc.hpp" -#include "atomicdex/api/kdf/balance_info.hpp" +#include "atomicdex/api/kdf/balance_infos.hpp" #include "atomicdex/config/electrum.cfg.hpp" namespace atomic_dex::kdf @@ -33,13 +33,14 @@ namespace atomic_dex::kdf { struct tendermint_balance_infos_t { - balance_info balances; + balance_infos balances; }; + std::string ticker; std::string address; std::size_t current_block; tendermint_balance_infos_t tendermint_balances_infos; - std::unordered_map tendermint_token_balances_infos; + std::unordered_map tendermint_token_balances_infos; }; using expected_error_type = rpc_basic_error_type; diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.withdraw.cpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.withdraw.cpp index ac0b96a04..d0a9d1a34 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.withdraw.cpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.withdraw.cpp @@ -47,6 +47,10 @@ namespace atomic_dex::kdf obj["params"]["to"] = cfg.to; obj["params"]["max"] = cfg.max; if (cfg.memo.has_value()) + { + obj["params"]["ibc_source_channel"] = cfg.ibc_source_channel.value(); + } + if (cfg.memo.has_value()) { obj["params"]["memo"] = cfg.memo.value(); } diff --git a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.withdraw.hpp b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.withdraw.hpp index 76b4b74bd..2bd780fae 100644 --- a/src/core/atomicdex/api/kdf/rpc_v2/rpc2.withdraw.hpp +++ b/src/core/atomicdex/api/kdf/rpc_v2/rpc2.withdraw.hpp @@ -30,6 +30,7 @@ namespace atomic_dex::kdf std::string amount; ///< ignored if max is true std::optional fees{std::nullopt}; ///< ignored if std::nullopt std::optional memo; ///< memo for tendermint + std::optional ibc_source_channel; ///< ibc_source_channel for tendermint bool max{false}; }; diff --git a/src/core/atomicdex/api/kdf/transaction.data.cpp b/src/core/atomicdex/api/kdf/transaction.data.cpp index c9975f984..f8b97c6cb 100644 --- a/src/core/atomicdex/api/kdf/transaction.data.cpp +++ b/src/core/atomicdex/api/kdf/transaction.data.cpp @@ -114,7 +114,24 @@ namespace atomic_dex::kdf if (j.contains("transaction_type")) { - cfg.transaction_type = j.at("transaction_type").get(); + // Check if the "transaction_type" is an object for tendermint + if (j.at("transaction_type").is_object()) + { + for (auto& [k, v] : j.at("transaction_type").items()) { + cfg.tendermint_transaction_type = k; + cfg.tendermint_transaction_type_hash = v; + } + } + // Check if "transaction_type" is a string + else if (j.at("transaction_type").is_string()) + { + cfg.transaction_type = j.at("transaction_type").get(); + } + else + { + // Handle the case where "transaction_type" is neither a string nor an object + SPDLOG_ERROR("Unexpected type for transaction_type in JSON"); + } } // transaction_fee only in ZHTLC response diff --git a/src/core/atomicdex/api/kdf/transaction.data.hpp b/src/core/atomicdex/api/kdf/transaction.data.hpp index f25c9e9bf..296c89efe 100644 --- a/src/core/atomicdex/api/kdf/transaction.data.hpp +++ b/src/core/atomicdex/api/kdf/transaction.data.hpp @@ -74,6 +74,8 @@ namespace atomic_dex::kdf std::optional internal_id; std::optional confirmations; std::optional transaction_type; + std::optional tendermint_transaction_type; + std::optional tendermint_transaction_type_hash; std::optional memo; std::string timestamp_as_date; ///< human readeable timestamp }; diff --git a/src/core/atomicdex/models/qt.portfolio.model.cpp b/src/core/atomicdex/models/qt.portfolio.model.cpp index 850241aed..2d5118163 100644 --- a/src/core/atomicdex/models/qt.portfolio.model.cpp +++ b/src/core/atomicdex/models/qt.portfolio.model.cpp @@ -56,6 +56,7 @@ namespace atomic_dex atomic_dex::portfolio_model::initialize_portfolio(const std::vector& tickers) { QVector datas; + SPDLOG_INFO("Inserting rows. Current model data count: {}, Tickers size: {}", this->m_model_data.count(), tickers.size()); for (auto&& ticker: tickers) { @@ -106,37 +107,6 @@ namespace atomic_dex } } - bool - portfolio_model::update_activation_status() - { - // This feels a bit heavy handed. There should be a better way to do this. - // Function may be unused. - const auto& kdf_system = this->m_system_manager.get_system(); - const auto coins = this->m_system_manager.get_system().get_global_cfg()->get_enabled_coins(); - - for (auto&& [_, coin]: coins) - { - if (m_ticker_registry.find(coin.ticker) == m_ticker_registry.end()) - { - SPDLOG_WARN("[update_activation_status] ticker: {} not inserted yet in the model, skipping", coin.ticker); - return false; - } - const std::string& ticker = coin.ticker; - if (const auto res = this->match(this->index(0, 0), TickerRole, QString::fromStdString(ticker), 1, Qt::MatchFlag::MatchExactly); - not res.isEmpty()) - { - std::error_code ec; - const QModelIndex& idx = res.at(0); - auto coin_info = kdf_system.get_coin_info(ticker); - QJsonObject status = nlohmann_json_object_to_qt_json_object(coin_info.activation_status); - update_value(ActivationStatus, status, idx, *this); - SPDLOG_DEBUG("updated activation status of: {}", ticker); - return true; - } - return false; - } - } - bool portfolio_model::update_currency_values() { diff --git a/src/core/atomicdex/models/qt.portfolio.model.hpp b/src/core/atomicdex/models/qt.portfolio.model.hpp index f92c6bff2..c6c4e8fe1 100644 --- a/src/core/atomicdex/models/qt.portfolio.model.hpp +++ b/src/core/atomicdex/models/qt.portfolio.model.hpp @@ -97,7 +97,6 @@ namespace atomic_dex //! Public api void initialize_portfolio(const std::vector& tickers); - bool update_activation_status(); bool update_currency_values(); bool update_balance_values(const std::vector& tickers); void adjust_percent_current_currency(QString balance_all); diff --git a/src/core/atomicdex/models/qt.portfolio.proxy.filter.model.cpp b/src/core/atomicdex/models/qt.portfolio.proxy.filter.model.cpp index aba4a97cd..a494f4979 100644 --- a/src/core/atomicdex/models/qt.portfolio.proxy.filter.model.cpp +++ b/src/core/atomicdex/models/qt.portfolio.proxy.filter.model.cpp @@ -76,58 +76,66 @@ namespace atomic_dex } } - bool - portfolio_proxy_model::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const + bool portfolio_proxy_model::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { - QModelIndex idx = this->sourceModel()->index(source_row, 0, source_parent); - assert(this->sourceModel()->hasIndex(idx.row(), 0)); - QString ticker = this->sourceModel()->data(idx, atomic_dex::portfolio_model::TickerRole).toString(); - QString type = this->sourceModel()->data(idx, atomic_dex::portfolio_model::CoinType).toString(); + // Get a pointer to the source model to avoid repeated calls. + const QAbstractItemModel* model = this->sourceModel(); + QModelIndex idx = model->index(source_row, 0, source_parent); - if (this->filterRole() == atomic_dex::portfolio_model::MultiTickerCurrentlyEnabled) - { - bool is_enabled = this->sourceModel()->data(idx, atomic_dex::portfolio_model::MultiTickerCurrentlyEnabled).toBool(); - if (not is_enabled) - { + // Gracefully handle invalid index with logging and early exit. + if (!model->hasIndex(idx.row(), 0)) { + SPDLOG_ERROR("Invalid index at row {}. Model row count: {}", source_row, model->rowCount()); + return false; // Fail gracefully and continue execution. + } + + // Fetch ticker and type data. + QString ticker = model->data(idx, atomic_dex::portfolio_model::TickerRole).toString(); + QString type = model->data(idx, atomic_dex::portfolio_model::CoinType).toString(); + + // Handle filter role for currently enabled multi-tickers. + if (this->filterRole() == atomic_dex::portfolio_model::MultiTickerCurrentlyEnabled) { + bool is_enabled = model->data(idx, atomic_dex::portfolio_model::MultiTickerCurrentlyEnabled).toBool(); + if (!is_enabled) { return false; } } // Filter by ticker name if `m_search_exp` is not empty. - if (!m_search_exp.isEmpty()) - { - if (not ticker.contains(m_search_exp, Qt::CaseInsensitive)) - { - return false; - } + if (!m_search_exp.isEmpty() && !ticker.contains(m_search_exp, Qt::CaseInsensitive)) { + return false; } - if (am_i_a_market_selector && m_system_mgr.get_system().get_global_cfg()->get_coin_info(ticker.toStdString()).wallet_only) - { + // Filter out market selector if the coin is wallet-only. + if (am_i_a_market_selector && + m_system_mgr.get_system() + .get_global_cfg() + ->get_coin_info(ticker.toStdString()) + .wallet_only) { return false; } - if (m_excluded_coin == ticker) - { + // Exclude specific coins. + if (m_excluded_coin == ticker) { return false; } - if (m_with_balance) - { - if (this->sourceModel()->data(idx, portfolio_model::BalanceRole).toString().toFloat() == 0.F) - { + // Filter by balance if required. + if (m_with_balance) { + float balance = model->data(idx, portfolio_model::BalanceRole).toString().toFloat(); + if (balance == 0.F) { return false; } } - if (m_with_fiat_balance) - { - if (this->sourceModel()->data(idx, portfolio_model::MainCurrencyBalanceRole).toFloat() == 0.F) - { + // Filter by fiat balance if required. + if (m_with_fiat_balance) { + float fiat_balance = model->data(idx, portfolio_model::MainCurrencyBalanceRole).toFloat(); + if (fiat_balance == 0.F) { return false; } } + // If all conditions pass, delegate to the base class. return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } diff --git a/src/core/atomicdex/pages/qt.portfolio.page.cpp b/src/core/atomicdex/pages/qt.portfolio.page.cpp index 4e4a227d0..87d6ae024 100644 --- a/src/core/atomicdex/pages/qt.portfolio.page.cpp +++ b/src/core/atomicdex/pages/qt.portfolio.page.cpp @@ -15,6 +15,7 @@ ******************************************************************************/ //! Qt +#include #include //! Project Headers @@ -76,24 +77,50 @@ namespace atomic_dex void portfolio_page::on_update_portfolio_values_event(const update_portfolio_values& evt) { - // SPDLOG_DEBUG("Updating portfolio values with model: {}", evt.with_update_model); + if (m_portfolio_update_cooldown) + { + SPDLOG_DEBUG("Portfolio update cooldown active, skipping update."); + return; + } + + // Set the cooldown timer + m_portfolio_update_cooldown = true; + QTimer::singleShot(3000, [this]() { m_portfolio_update_cooldown = false; }); + + SPDLOG_DEBUG("Updating portfolio values with model: {}", evt.with_update_model); - bool res = true; + bool update_success = true; if (evt.with_update_model) { - res = m_portfolio_mdl->update_currency_values(); + SPDLOG_DEBUG("Updating currency values and ticker information..."); + update_success = m_portfolio_mdl->update_currency_values(); m_system_manager.get_system().refresh_ticker_infos(); } + // Fetch config and price service data + const auto& config = m_system_manager.get_system().get_cfg(); + const auto& price_service = m_system_manager.get_system(); + std::error_code ec; - const auto& config = m_system_manager.get_system().get_cfg(); - const auto& price_service = m_system_manager.get_system(); - auto fiat_balance_std = price_service.get_price_in_fiat_all(config.current_currency, ec); - if (!ec && res) + const auto fiat_balance_std = price_service.get_price_in_fiat_all(config.current_currency, ec); + + if (ec) { - set_current_balance_fiat_all(QString::fromStdString(fiat_balance_std)); - m_portfolio_mdl->adjust_percent_current_currency(QString::fromStdString(fiat_balance_std)); + SPDLOG_ERROR("Failed to retrieve fiat balance: {}", ec.message()); } + + if (update_success) + { + SPDLOG_DEBUG("Updating fiat balance display: {}", fiat_balance_std); + update_portfolio_balance(QString::fromStdString(fiat_balance_std)); + } + } + + void + portfolio_page::update_portfolio_balance(const QString& fiat_balance) + { + set_current_balance_fiat_all(fiat_balance); + m_portfolio_mdl->adjust_percent_current_currency(fiat_balance); } QStringList diff --git a/src/core/atomicdex/pages/qt.portfolio.page.hpp b/src/core/atomicdex/pages/qt.portfolio.page.hpp index 5a39a2ba1..c87bf24de 100644 --- a/src/core/atomicdex/pages/qt.portfolio.page.hpp +++ b/src/core/atomicdex/pages/qt.portfolio.page.hpp @@ -49,6 +49,7 @@ namespace atomic_dex ag::ecs::system_manager& m_system_manager; portfolio_model* m_portfolio_mdl; global_coins_cfg_model* m_global_cfg_mdl; + bool m_portfolio_update_cooldown{false}; QString m_current_balance_all{"0"}; QString m_main_current_balance_all{"0"}; WalletChartsCategories m_current_chart_category{WalletChartsCategories::OneMonth}; @@ -64,6 +65,7 @@ namespace atomic_dex //! CPP API void initialize_portfolio(const std::vector& tickers); void disable_coins(const QStringList& coins); + void update_portfolio_balance(const QString& fiat_balance); [[nodiscard]] portfolio_model* get_portfolio() const; [[nodiscard]] global_coins_cfg_model* get_global_cfg() const; @@ -80,9 +82,10 @@ namespace atomic_dex [[nodiscard]] bool is_chart_busy() const; [[nodiscard]] QVariant get_charts() const; [[nodiscard]] QVariant get_wallet_stats() const; - ; + [[nodiscard]] QString get_min_total_chart() const; [[nodiscard]] QString get_max_total_chart() const; + //! Events void on_update_portfolio_values_event(const update_portfolio_values&); diff --git a/src/core/atomicdex/pages/qt.wallet.page.cpp b/src/core/atomicdex/pages/qt.wallet.page.cpp index 97fe85771..62c4cd1a2 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.cpp +++ b/src/core/atomicdex/pages/qt.wallet.page.cpp @@ -508,7 +508,7 @@ namespace atomic_dex } void - wallet_page::send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data, const QString& memo) + wallet_page::send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data, const QString& memo, const QString& ibc_source_channel) { //! Preparation this->set_send_busy(true); @@ -693,6 +693,7 @@ namespace atomic_dex .to = address.toStdString(), .amount = max ? "0" : amount.toStdString(), .memo = memo.toStdString(), + .ibc_source_channel = ibc_source_channel.toStdString(), .max = max }; @@ -735,6 +736,7 @@ namespace atomic_dex nlohmann::json json_data = kdf::template_request("withdraw", true); kdf::to_json(json_data, withdraw_req); + SPDLOG_DEBUG("withdraw request: {}", json_data.dump(4)); batch.push_back(json_data); @@ -884,7 +886,7 @@ namespace atomic_dex { kdf_system.decrease_fake_balance(ticker, amount.toStdString()); } - kdf_system.fetch_infos_thread(); + kdf_system.fetch_infos_thread(true, false); } else { @@ -1007,48 +1009,54 @@ namespace atomic_dex void wallet_page::on_tx_fetch_finished(const tx_fetch_finished& evt) { - if (!evt.with_error && QString::fromStdString(evt.ticker) == get_current_ticker()) + // Ensure we're working with the correct ticker and no error occurred + if (evt.with_error || QString::fromStdString(evt.ticker) != get_current_ticker()) { - std::error_code ec; - const auto& settings = m_system_manager.get_system(); - t_transactions transactions = m_system_manager.get_system().get_tx_history(ec); - t_transactions to_init; - if (settings.is_spamfilter_enabled()) - { - for (auto&& cur_tx: transactions) - { - if (safe_float(cur_tx.total_amount) != 0) - { - to_init.push_back(cur_tx); - } - } - } - else - { - to_init = transactions; - } - if (m_transactions_mdl->rowCount() == 0) - { - //! insert all transactions - m_transactions_mdl->init_transactions(to_init); - } - else - { - //! Update tx (only unconfirmed) or insert (new tx) - m_transactions_mdl->update_or_insert_transactions(to_init); - } - if (ec) - { - this->set_tx_fetching_failed(true); - } - else - { - this->set_tx_fetching_failed(false); - } + this->m_transactions_mdl->reset(); + this->set_tx_fetching_busy(false); + return; + } + std::error_code ec; + const auto& settings = m_system_manager.get_system(); + t_transactions transactions = m_system_manager.get_system().get_tx_history(ec); + + // Apply spam filter if enabled + t_transactions filtered_transactions; + if (settings.is_spamfilter_enabled()) + { + std::copy_if(transactions.begin(), transactions.end(), std::back_inserter(filtered_transactions), + [](const auto& tx) { return safe_float(tx.total_amount) != 0; }); } else { - this->m_transactions_mdl->reset(); + filtered_transactions = std::move(transactions); // Move transactions instead of copying + } + + // Initialize or update transactions based on existing data + if (m_transactions_mdl->rowCount() == 0) + { + m_transactions_mdl->init_transactions(filtered_transactions); + } + else + { + m_transactions_mdl->update_or_insert_transactions(filtered_transactions); + } + + // Handle error status and fetching state + handle_tx_fetch_status(ec); + } + + // Helper function to handle transaction fetch status + void wallet_page::handle_tx_fetch_status(const std::error_code& ec) + { + if (ec) + { + SPDLOG_ERROR("Transaction fetching failed with error: {}", ec.message()); + this->set_tx_fetching_failed(true); + } + else + { + this->set_tx_fetching_failed(false); } this->set_tx_fetching_busy(false); } diff --git a/src/core/atomicdex/pages/qt.wallet.page.hpp b/src/core/atomicdex/pages/qt.wallet.page.hpp index 97b90f739..f1a20d820 100644 --- a/src/core/atomicdex/pages/qt.wallet.page.hpp +++ b/src/core/atomicdex/pages/qt.wallet.page.hpp @@ -54,6 +54,7 @@ namespace atomic_dex void set_tx_fetching_failed(bool status); [[nodiscard]] bool is_tx_fetching_busy() const; void set_tx_fetching_busy(bool status); + void handle_tx_fetch_status(const std::error_code& ec); [[nodiscard]] bool is_convert_address_busy() const; void set_convert_address_busy(bool status); [[nodiscard]] bool is_validate_address_busy() const; @@ -88,7 +89,7 @@ namespace atomic_dex 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, const QString& memo); + Q_INVOKABLE void send(const QString& address, const QString& amount, bool max, bool with_fees, QVariantMap fees_data, const QString& memo, const QString& ibc_source_channel); // QML API Properties Q_PROPERTY(transactions_model* transactions_mdl READ get_transactions_mdl NOTIFY transactionsMdlChanged) diff --git a/src/core/atomicdex/services/kdf/kdf.service.cpp b/src/core/atomicdex/services/kdf/kdf.service.cpp index 71469bd2e..cc021dbf8 100644 --- a/src/core/atomicdex/services/kdf/kdf.service.cpp +++ b/src/core/atomicdex/services/kdf/kdf.service.cpp @@ -287,55 +287,90 @@ namespace atomic_dex const auto s_orderbook = std::chrono::duration_cast(now - m_orderbook_clock); const auto s_info = std::chrono::duration_cast(now - m_info_clock); const auto s_activation = std::chrono::duration_cast(now - m_activation_clock); + const auto orderbook_loop_delay_sec = std::chrono::seconds(5); + const auto activation_loop_delay_sec = std::chrono::seconds(13); + const auto info_loop_delay_sec = std::chrono::seconds(17); - if (s_orderbook >= 5s) + if (s_orderbook >= orderbook_loop_delay_sec) { fetch_current_orderbook_thread(false); // process_orderbook (not a reset) if on trading page batch_fetch_orders_and_swap(); // gets 'my_orders', 'my_recent_swaps' & 'active_swaps' m_orderbook_clock = std::chrono::high_resolution_clock::now(); } - - if (s_activation >= 7s) + if (s_activation >= orderbook_loop_delay_sec) { - auto coins = this->get_enabled_coins(); - std::vector tickers; - for (auto&& coin: coins) + // Get coins that are not active and prepare to mark them as active + auto coins = this->get_enabled_coins(); + std::vector inactive_tickers; + + for (auto&& coin : coins) { if (!coin.active) { - tickers.push_back(coin.ticker); + inactive_tickers.push_back(coin.ticker); } } - if (!tickers.empty()) + + if (!inactive_tickers.empty()) + { + SPDLOG_DEBUG("Making sure {} enabled coins are marked as active", inactive_tickers.size()); + update_coin_status(this->m_current_wallet_name, inactive_tickers, true, m_coins_informations, m_coin_cfg_mutex); + } + + t_coins to_enable; + + // Ensure the primary and secondary DEX coins are in the activation queue + const auto& primary_coin = get_coin_info(g_primary_dex_coin); + if (!primary_coin.currently_enabled) + { + SPDLOG_WARN("Primary coin {} not fully enabled! Adding to front of the queue", g_primary_dex_coin); + to_enable.push_back(primary_coin); + } + + const auto& secondary_coin = get_coin_info(g_second_primary_dex_coin); + if (!secondary_coin.currently_enabled) { - // Mark coins as active internally, and updates the coins file - SPDLOG_DEBUG("Making sure {} enabled coins are marked as active", tickers.size()); - update_coin_status(this->m_current_wallet_name, tickers, true, m_coins_informations, m_coin_cfg_mutex); + SPDLOG_WARN("Secondary coin {} not fully enabled! Adding to front of the queue", g_second_primary_dex_coin); + to_enable.push_back(secondary_coin); } - if (!m_activation_queue.empty()) + // Protect m_activation_queue using a unique_lock { std::unique_lock lock(m_activation_mutex); + + if (!m_activation_queue.empty()) + { SPDLOG_DEBUG("{} coins in the activation queue", m_activation_queue.size()); - t_coins to_enable; + size_t max_coins_to_activate = std::min(10, m_activation_queue.size()); - for (size_t i = 0; i < 20 && i < m_activation_queue.size(); ++i) { - to_enable.push_back(m_activation_queue[i]); + for (size_t i = 0; i < max_coins_to_activate; ++i) + { + to_enable.push_back(std::move(m_activation_queue[i])); + } + m_activation_queue.erase(m_activation_queue.begin(), m_activation_queue.begin() + max_coins_to_activate); + } + else + { + SPDLOG_DEBUG("Coins activation queue is empty."); } - activate_coins(to_enable); - m_activation_queue.erase(m_activation_queue.begin(), m_activation_queue.begin() + to_enable.size()); m_activation_clock = std::chrono::high_resolution_clock::now(); } - else { - SPDLOG_DEBUG("Coins activation queue is empty."); - m_activation_clock = std::chrono::high_resolution_clock::now() + std::chrono::duration_cast(std::chrono::seconds(6)); + + if (!to_enable.empty()) + { + activate_coins(to_enable); } } - if (s_info >= 29s) + if (s_info >= info_loop_delay_sec) { - fetch_infos_thread(); // leads to batch_balance_and_tx - m_info_clock = std::chrono::high_resolution_clock::now(); + // std::shared_lock lock(m_activation_mutex); + //if (m_activation_queue.empty()) + { + // lock.unlock(); + fetch_infos_thread(); // leads to batch_balance_and_tx + m_info_clock = std::chrono::high_resolution_clock::now(); + } } } @@ -484,6 +519,27 @@ namespace atomic_dex return true; } + + // Function to add a coin to the activation queue + bool kdf_service::add_coin_to_activation_queue(const coin_config_t& new_coin) + { + std::unique_lock lock(m_activation_mutex); + auto it = std::find_if(m_activation_queue.begin(), m_activation_queue.end(), + [&new_coin](const coin_config_t& coin) { + return coin.ticker == new_coin.ticker; + }); + + if (it != m_activation_queue.end()) + { + SPDLOG_WARN("Coin with ticker {} already exists in the queue.)", new_coin.ticker); + return false; // Duplicate found, do not add. + } + + m_activation_queue.push_back(new_coin); + return true; // Successfully added. + } + + bool kdf_service::enable_default_coins() { std::atomic result{1}; @@ -526,8 +582,7 @@ namespace atomic_dex SPDLOG_WARN("{} cannot be enabled because it already is or is being enabled.", coin.ticker); continue; } - std::unique_lock lock(m_activation_mutex); - m_activation_queue.push_back(coin); + add_coin_to_activation_queue(coin); } m_activation_clock = std::chrono::high_resolution_clock::now() - std::chrono::duration_cast(std::chrono::seconds(13)); } @@ -896,45 +951,29 @@ namespace atomic_dex SPDLOG_ERROR("{} {}: ", rpc.request.ticker, rpc.error->error_type); if (rpc.error->error_type.find("PlatformIsAlreadyActivated") != std::string::npos) { - SPDLOG_ERROR("{} {}: ", rpc.request.ticker, rpc.error->error_type); - fetch_single_balance(get_coin_info(rpc.request.ticker)); - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[rpc.request.ticker].currently_enabled = true; - dispatcher_.trigger(coin_fully_initialized{.tickers = {rpc.request.ticker}}); + mark_coin_enabled(rpc.request.ticker); + } + else if (rpc.error->error_type.find("TokenIsAlreadyActivated") != std::string::npos) + { if constexpr (std::is_same_v) { SPDLOG_ERROR("{} {}: ", rpc.request.ticker, rpc.error->error_type); for (const auto& erc20_coin_info : rpc.request.erc20_tokens_requests) { - SPDLOG_ERROR("{} {}: ", erc20_coin_info.ticker, rpc.error->error_type); - fetch_single_balance(get_coin_info(erc20_coin_info.ticker)); - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[erc20_coin_info.ticker].currently_enabled = true; - dispatcher_.trigger(coin_fully_initialized{.tickers = {erc20_coin_info.ticker}}); + mark_coin_enabled(erc20_coin_info.ticker); } } } - else if (rpc.error->error_type.find("TokenIsAlreadyActivated") != std::string::npos) - { - SPDLOG_ERROR("{} {}: ", rpc.request.ticker, rpc.error->error_type); - } else { - SPDLOG_ERROR("marking {} as inactive: {}", rpc.request.ticker, rpc.error->error_type); - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[rpc.request.ticker].currently_enabled = false; - update_coin_active({rpc.request.ticker}, false); + mark_coin_failed(rpc.request.ticker); this->dispatcher_.trigger(rpc.request.ticker, rpc.error->error); } } else { - dispatcher_.trigger(coin_fully_initialized{.tickers = {rpc.request.ticker}}); - fetch_single_balance(get_coin_info(rpc.request.ticker)); - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[rpc.request.ticker].currently_enabled = true; - SPDLOG_DEBUG("marking {} as active", rpc.request.ticker); + mark_coin_enabled(rpc.request.ticker); if constexpr (std::is_same_v) { for (const auto& erc20_address_info : rpc.result->erc20_addresses_infos) @@ -948,12 +987,7 @@ namespace atomic_dex { for (const auto& balance : erc20_address_info.second.balances) { - SPDLOG_DEBUG("marking token {} as active", balance.first); - dispatcher_.trigger(coin_fully_initialized{.tickers = {balance.first}}); - //process_balance_answer(rpc); - fetch_single_balance(get_coin_info(balance.first)); - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[balance.first].currently_enabled = true; + mark_coin_enabled(balance.first); } } } @@ -1013,57 +1047,58 @@ namespace atomic_dex enable_tendermint_coins(t_coins{std::move(coin_config)}, parent_ticker); } + void kdf_service::mark_coin_enabled(const std::string& ticker) + { + std::unique_lock lock(m_coin_cfg_mutex); + m_coins_informations[ticker].currently_enabled = true; + fetch_single_balance(get_coin_info(ticker)); + dispatcher_.trigger(coin_fully_initialized{.tickers = {ticker}}); + } + + void kdf_service::mark_coin_failed(const std::string& ticker) + { + std::unique_lock lock(m_coin_cfg_mutex); + m_coins_informations[ticker].currently_enabled = false; + update_coin_active({ticker}, false); + } + void kdf_service::enable_tendermint_coins(const t_coins& coins, const std::string parent_ticker) { auto callback = [this](RpcRequest rpc) { if (rpc.error) { - if (rpc.error->error_type.find("PlatformIsAlreadyActivated") != std::string::npos - || rpc.error->error_type.find("TokenIsAlreadyActivated") != std::string::npos) + SPDLOG_DEBUG("{} failed to activate: {}", rpc.request.ticker, rpc.error->error_type); + if (rpc.error->error_type.find("PlatformIsAlreadyActivated") != std::string::npos) + { + mark_coin_enabled(rpc.request.ticker); + } + else if (rpc.error->error_type.find("TokenIsAlreadyActivated") != std::string::npos) { - SPDLOG_ERROR("{} {}: ", rpc.request.ticker, rpc.error->error_type); - fetch_single_balance(get_coin_info(rpc.request.ticker)); - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[rpc.request.ticker].currently_enabled = true; - dispatcher_.trigger(coin_fully_initialized{.tickers = {rpc.request.ticker}}); if constexpr (std::is_same_v) { for (const auto& tendermint_coin_info : rpc.request.tokens_params) { - SPDLOG_ERROR("{} {}: ", tendermint_coin_info.ticker, rpc.error->error_type); - fetch_single_balance(get_coin_info(tendermint_coin_info.ticker)); - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[tendermint_coin_info.ticker].currently_enabled = true; - dispatcher_.trigger(coin_fully_initialized{.tickers = {tendermint_coin_info.ticker}}); + mark_coin_enabled(tendermint_coin_info.ticker); } } } else { - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[rpc.request.ticker].currently_enabled = false; - update_coin_active({rpc.request.ticker}, false); + mark_coin_failed(rpc.request.ticker); this->dispatcher_.trigger(rpc.request.ticker, rpc.error->error); } } else { - dispatcher_.trigger(coin_fully_initialized{.tickers = {rpc.request.ticker}}); - fetch_single_balance(get_coin_info(rpc.request.ticker)); - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[rpc.request.ticker].currently_enabled = true; + mark_coin_enabled(rpc.request.ticker); if constexpr (std::is_same_v) { - for (const auto& tendermint_token_addresses_info : rpc.result->tendermint_token_balances_infos) + for (const auto& tendermint_coin_info : rpc.request.tokens_params) { - dispatcher_.trigger(coin_fully_initialized{.tickers = {tendermint_token_addresses_info.first}}); - process_balance_answer(rpc); - std::unique_lock lock(m_coin_cfg_mutex); - m_coins_informations[tendermint_token_addresses_info.first].currently_enabled = true; + mark_coin_enabled(tendermint_coin_info.ticker); } } - process_balance_answer(rpc); } }; @@ -1071,6 +1106,7 @@ namespace atomic_dex { static constexpr auto error = "{} is not present in the config. Cannot enable TENDERMINT tokens."; SPDLOG_ERROR(error); + mark_coin_failed(parent_ticker); this->dispatcher_.trigger(parent_ticker, fmt::format(error, parent_ticker)); return; } @@ -1087,6 +1123,7 @@ namespace atomic_dex { continue; } + SPDLOG_DEBUG("Enabling {} token: {}", parent_ticker, coin_config.ticker); m_kdf_client.process_rpc_async(rpc.request, callback); } } @@ -1104,6 +1141,8 @@ namespace atomic_dex } rpc.request.tokens_params.push_back({.ticker = coin_config.ticker}); } + + SPDLOG_DEBUG("Enabling {} with {} tokens", parent_ticker, coins.size() - 1); m_kdf_client.process_rpc_async(rpc.request, callback); } } @@ -1176,7 +1215,6 @@ namespace atomic_dex { const auto& answer = rpc.result.value(); kdf::balance_answer balance_answer; - balance_answer.address = answer.balances.begin()->first; balance_answer.balance = answer.balances.begin()->second.spendable; balance_answer.coin = answer.platform_coin; @@ -1193,7 +1231,7 @@ namespace atomic_dex { kdf::balance_answer balance_answer; - balance_answer.coin = rpc.request.ticker; + balance_answer.coin = answer.ticker; balance_answer.balance = answer.tendermint_balances_infos.balances.spendable; balance_answer.address = answer.address; { @@ -1498,6 +1536,24 @@ namespace atomic_dex update_coin_status(this->m_current_wallet_name, tickers, false, m_coins_informations, m_coin_cfg_mutex); } + + std::string + kdf_service::resolve_ticker(const nlohmann::json& j) const { + if (j.contains("mmrpc") && j.at("mmrpc") == "2.0") { + if (j.at("params").contains("coin")) { + return j.at("params").at("coin").get(); + } + if (j.at("params").contains("ticker")) { + return j.at("params").at("ticker").get(); + } + } + if (j.contains("coin")) { + return j.at("coin").get(); + } + SPDLOG_ERROR("Could not resolve ticker from json: {}", j.dump(4)); + return ""; + } + auto kdf_service::batch_balance_and_tx(bool is_a_reset, std::vector tickers, bool is_during_enabling, bool only_tx) { @@ -1512,34 +1568,32 @@ namespace atomic_dex try { auto answers = kdf::basic_batch_answer(resp); + if (answers.empty()) { + SPDLOG_WARN("Empty answers received, no processing done."); + return; + } if (not answers.contains("error")) { for (auto i = 0ul; i < answers.size(); i++) { auto& answer = answers[i]; - std::string ticker; - - if (batch_array[i].contains("mmrpc") && batch_array[i].at("mmrpc") == "2.0") - { - ticker = batch_array[i].at("params").at("coin"); - } - else - { - ticker = batch_array[i].at("coin"); - } - - if (answer.contains("balance")) + const std::string ticker = resolve_ticker(batch_array[i]); + if (ticker.empty()) { + SPDLOG_ERROR("Could not resolve ticker from json: {}", batch_array[i].dump(4)); + continue; + } + // SPDLOG_DEBUG("{} batch_balance_and_tx answer: {}", ticker, answer.dump(4)); + if (answer.contains("balance")) { this->process_balance_answer(answer); } - else if (answer.contains("result")) - { + else if (answer.contains("result")) { this->process_tx_answer(answer, ticker); } else { const std::string error = answer.dump(4); - SPDLOG_ERROR("error answer for tx or my_balance: {}", error); + SPDLOG_WARN("error answer for tx or my_balance: {}", error); this->dispatcher_.trigger(true); if (error.find("future timed out") != std::string::npos) { @@ -1554,8 +1608,12 @@ namespace atomic_dex } catch (const std::exception& error) { - SPDLOG_ERROR("exception in batch_balance_and_tx: {}", error.what()); + SPDLOG_ERROR("Exception in batch_balance_and_tx: {}", error.what()); this->dispatcher_.trigger(true); + throw; + } + for (const auto& coin : tokens_to_fetch) { + process_tx_tokenscan(coin, is_a_reset); } }) .then([this, batch = batch_array](pplx::task previous_task) @@ -2148,7 +2206,8 @@ namespace atomic_dex .then([this, batch](pplx::task previous_task) { this->handle_exception_pplx_task(previous_task, "process_orderbook_extras", batch); }); } - void kdf_service::fetch_current_orderbook_thread(bool is_a_reset) + void + kdf_service::fetch_current_orderbook_thread(bool is_a_reset) { //! If thread is not active ex: we are not on the trading page anymore, we continue sleeping. if (!m_orderbook_thread_active) @@ -2158,7 +2217,8 @@ namespace atomic_dex process_orderbook(is_a_reset); } - void kdf_service::fetch_single_balance(const coin_config_t& cfg_infos) + void + kdf_service::fetch_single_balance(const coin_config_t& cfg_infos) { nlohmann::json batch_array = nlohmann::json::array(); if (is_pin_cfg_enabled()) @@ -2172,8 +2232,7 @@ namespace atomic_dex } t_balance_request balance_request{.coin = cfg_infos.ticker}; - // SPDLOG_DEBUG("Getting balance from kdf for {} ", cfg_infos.ticker); - nlohmann::json j = kdf::template_request("my_balance"); + nlohmann::json j = kdf::template_request("my_balance"); kdf::to_json(j, balance_request); batch_array.push_back(j); @@ -2181,37 +2240,41 @@ namespace atomic_dex { try { - auto answers = kdf::basic_batch_answer(resp); - if (!answers.contains("error") && !answers[0].contains("error")) + // SPDLOG_DEBUG("HTTP response status: {}", resp.status_code()); + // for (const auto& header : resp.headers()) + // { + // SPDLOG_DEBUG("Header: {} = {}", header.first, header.second); + // } + + if (resp.status_code() == web::http::status_codes::OK) + { + auto answers = kdf::basic_batch_answer(resp); + if (!answers.contains("error") && !answers[0].contains("error")) + { + this->process_balance_answer(answers[0]); + } + } + else { - this->process_balance_answer(answers[0]); + SPDLOG_WARN("Unexpected HTTP status code: {}", resp.status_code()); } } catch (const std::exception& error) { - SPDLOG_ERROR("exception in fetch_single_balance: {}", error.what()); + SPDLOG_ERROR("Exception in fetch_single_balance: {}", error.what()); } }; auto error_functor = [this, batch = batch_array](pplx::task previous_task) { this->handle_exception_pplx_task(previous_task, "fetch_single_balance", batch); }; + m_kdf_client.async_rpc_batch_standalone(batch_array).then(answer_functor).then(error_functor); } void kdf_service::fetch_infos_thread(bool is_a_refresh, bool only_tx) { - if (only_tx) - { - batch_balance_and_tx(is_a_refresh, {}, false, only_tx); - } - else - { - const auto& enabled_coins = get_enabled_coins(); - SPDLOG_DEBUG("Running [fetch_infos_thread] for {} enabled coins", enabled_coins.size()); - for (auto&& coin: enabled_coins) { fetch_single_balance(coin); } - batch_balance_and_tx(is_a_refresh, {}, false, true); - } + batch_balance_and_tx(is_a_refresh, {}, false, only_tx); } void kdf_service::spawn_kdf_instance(std::string wallet_name, std::string passphrase, bool with_pin_cfg, std::string rpcpass) @@ -2331,6 +2394,7 @@ namespace atomic_dex kdf_service::get_balance_info_f(const std::string& ticker) const { std::error_code ec; + SPDLOG_DEBUG("{} l{}", __FUNCTION__, __LINE__); std::string balance_str = get_balance_info(ticker, ec); t_float_50 balance_f = safe_float(balance_str); // SPDLOG_DEBUG("get_balance for {}: [{}]", ticker, balance_str); @@ -2340,32 +2404,48 @@ namespace atomic_dex std::string kdf_service::get_balance_info(const std::string& ticker, t_kdf_ec& ec) const { - // This happens quite often - std::shared_lock lock(m_balance_mutex); ///! read - auto it = m_balance_informations.find(ticker); + try + { + // Acquire shared lock for reading balance info + std::shared_lock lock(m_balance_mutex); + + // Lookup coin information and balance + const auto& coin_info_it = m_coins_informations.find(ticker); + const auto& balance_it = m_balance_informations.find(ticker); - if (m_coins_informations[ticker].currently_enabled) + // Check if the coin is enabled + if (coin_info_it != m_coins_informations.end() && coin_info_it->second.currently_enabled) { - if (it == m_balance_informations.cend()) + // Balance not found for the enabled coin + if (balance_it == m_balance_informations.cend()) { + // Check if the coin is ready (optional safeguard) if (!is_zhtlc_coin_ready(ticker)) { + SPDLOG_INFO("Coin {} is not yet ready, returning balance 0.", ticker); return "0"; } - SPDLOG_ERROR("get_balance_info not found for enabled coin: {}", ticker); + + // Warn about missing balance for enabled coin + SPDLOG_WARN("Balance info not found for enabled coin: {}", ticker); ec = dextop_error::balance_of_a_non_enabled_coin; return "0"; } - else - { - // SPDLOG_DEBUG("get_balance_info for {}: [{}]", ticker, it->second.balance); - return it->second.balance; + + // Successfully retrieved balance + SPDLOG_DEBUG("Retrieved balance for {}: {}", ticker, balance_it->second.balance); + return balance_it->second.balance; } + + // Coin is not enabled, skip balance check + SPDLOG_DEBUG("Balance request skipped for disabled coin: {}", ticker); + ec = dextop_error::balance_of_a_non_enabled_coin; + return "0"; } - else + catch (const std::exception& error) { - SPDLOG_DEBUG("get_balance_info request skipped for not enabled coin: {}", ticker); - ec = dextop_error::balance_of_a_non_enabled_coin; + SPDLOG_ERROR("exception in get_balance_info: {}", error.what()); + ec = dextop_error::balance_info_exception; return "0"; } } @@ -2690,7 +2770,7 @@ namespace atomic_dex if (it == m_balance_informations.cend()) { ec = dextop_error::unknown_ticker; - SPDLOG_INFO("Invalid Ticker {}", ticker); + SPDLOG_INFO("{} not in m_balance_informations", ticker); return "Invalid Ticker"; } @@ -3060,7 +3140,7 @@ namespace atomic_dex } void - kdf_service::handle_exception_pplx_task(pplx::task previous_task, const std::string& from, nlohmann::json request) + kdf_service::handle_exception_pplx_task(pplx::task previous_task, const std::string& from, const nlohmann::json& request) { try { @@ -3068,12 +3148,30 @@ namespace atomic_dex } catch (const std::exception& e) { - if (std::string(e.what()).find("mutex lock failed") != std::string::npos) + const std::string error_message = e.what(); + SPDLOG_ERROR("pplx exception: {} from: {}", error_message, from); + + nlohmann::json sanitized_request = request; + for (auto& cur : sanitized_request) cur["userpass"] = ""; + SPDLOG_ERROR("pplx exception request: {}", error_message, from, sanitized_request.dump(4)); + + if (error_message.find("mutex lock failed") != std::string::npos) { return; } - for (auto&& cur: request) cur["userpass"] = ""; - SPDLOG_ERROR("pplx task error: {} from: {}, request: {}", e.what(), from, request.dump(4)); + + if (is_mutex_lock_error(error_message)) + { + handle_mutex_lock_error(error_message); + } + else if (is_transient_error(error_message)) + { + handle_transient_error(); + } + else if (is_timeout_error(error_message)) + { + handle_timeout_error(); + } if (std::string(e.what()).find("Failed to read HTTP status line") != std::string::npos || std::string(e.what()).find("WinHttpReceiveResponse: 12002: The operation timed out") != std::string::npos) @@ -3088,4 +3186,47 @@ namespace atomic_dex } } + // Helper function to check for transient errors (like network issues) + bool kdf_service::is_transient_error(const std::string& error_message) const + { + return error_message.find("Failed to read HTTP status line") != std::string::npos; + } + + // Helper function to handle transient errors (like network issues) + void kdf_service::handle_transient_error() + { + SPDLOG_DEBUG("Checking internet connection..."); + const auto& internet_service = this->m_system_manager.get_system(); + if (!internet_service.is_internet_alive()) + { + SPDLOG_WARN("Internet connection lost. Triggering connection reset..."); + this->dispatcher_.trigger("connection dropped"); + } + } + + // Helper function to check for timeout errors + bool kdf_service::is_timeout_error(const std::string& error_message) const + { + return error_message.find("WinHttpReceiveResponse: 12002: The operation timed out") != std::string::npos; + } + + // Helper function to handle timeout errors + void kdf_service::handle_timeout_error() + { + SPDLOG_WARN("Operation timed out. Consider retrying the request or resetting the connection."); + } + + // Helper function to check for mutex lock errors + bool kdf_service::is_mutex_lock_error(const std::string& error_message) const + { + return error_message.find("mutex lock failed") != std::string::npos; + } + + // Helper function to mutex lock errors + void kdf_service::handle_mutex_lock_error(const std::string& error_message) const + { + SPDLOG_WARN("Mutex lock failed: {}", error_message); + } + + } // namespace atomic_dex diff --git a/src/core/atomicdex/services/kdf/kdf.service.hpp b/src/core/atomicdex/services/kdf/kdf.service.hpp index 131d54226..0c2d8963c 100644 --- a/src/core/atomicdex/services/kdf/kdf.service.hpp +++ b/src/core/atomicdex/services/kdf/kdf.service.hpp @@ -138,7 +138,14 @@ namespace atomic_dex 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); + void handle_exception_pplx_task(pplx::task previous_task, const std::string& from, const nlohmann::json& request); + bool is_transient_error(const std::string& error_message) const; + void handle_transient_error(); + bool is_timeout_error(const std::string& error_message) const; + void handle_timeout_error(); + bool is_mutex_lock_error(const std::string& error_message) const; + void handle_mutex_lock_error(const std::string& error_message) const; + public: //! Constructor @@ -173,6 +180,10 @@ namespace atomic_dex void enable_coins(const t_coins& coins); void enable_coin(const std::string& ticker); void enable_coin(const coin_config_t& coin_config); + bool add_coin_to_activation_queue(const coin_config_t& new_coin); + void mark_coin_enabled(const std::string& ticker); + void mark_coin_failed(const std::string& ticker); + private: void update_coin_active(const std::vector& tickers, bool status); void enable_erc_family_coin(const coin_config_t& coin_config); @@ -206,6 +217,7 @@ namespace atomic_dex [[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; [[nodiscard]] nlohmann::json get_zhtlc_status(const std::string coin) const; + std::string resolve_ticker(const nlohmann::json& j) const; //! Cancel zhtlc activation @@ -280,6 +292,16 @@ namespace atomic_dex void decrease_fake_balance(const std::string& ticker, const std::string& amount); void batch_fetch_orders_and_swap(bool after_manual_reset = false); + [[nodiscard]] std::tuple + get_order_and_swap_info(); + [[nodiscard]] nlohmann::json + prepare_my_recent_swaps_request(std::size_t current_page, std::size_t limit, const t_filtering_infos& filter_infos); + [[nodiscard]] nlohmann::json + prepare_active_swaps_request(); + void + process_orders_and_swaps(const nlohmann::json& answers, orders_and_swaps& result, std::size_t limit, const t_filtering_infos& filter_infos); + + //! Async API kdf::kdf_client& get_kdf_client(); diff --git a/src/core/atomicdex/services/price/global.provider.cpp b/src/core/atomicdex/services/price/global.provider.cpp index 772577cc0..10690c088 100644 --- a/src/core/atomicdex/services/price/global.provider.cpp +++ b/src/core/atomicdex/services/price/global.provider.cpp @@ -303,7 +303,7 @@ namespace atomic_dex { // Runs often to update fiat values for all enabled coins. // fetch ticker infos loop and on_update_portfolio_values_event triggers this. - // SPDLOG_INFO("get_price_in_fiat [{}] [{}]", fiat, ticker); + SPDLOG_INFO("get_price_in_fiat [{}] [{}]", fiat, ticker); try { auto& kdf_instance = m_system_manager.get_system(); diff --git a/src/core/atomicdex/utilities/cpprestsdk.utilities.cpp b/src/core/atomicdex/utilities/cpprestsdk.utilities.cpp index a7740282b..cfdfd5b99 100644 --- a/src/core/atomicdex/utilities/cpprestsdk.utilities.cpp +++ b/src/core/atomicdex/utilities/cpprestsdk.utilities.cpp @@ -30,15 +30,21 @@ #include "atomicdex/utilities/cpprestsdk.utilities.hpp" t_http_request -create_json_post_request(nlohmann::json&& json_data) +create_json_post_request(nlohmann::json&& json_data, const std::string& content_type) { t_http_request req; req.set_method(web::http::methods::POST); - req.headers().set_content_type(FROM_STD_STR("application/json")); - req.set_body(json_data.dump()); + req.headers().set_content_type(content_type); + try { + req.set_body(json_data.dump()); + } catch (const std::exception& e) { + SPDLOG_ERROR("Error serializing JSON data: {}", e.what()); + throw; // rethrow the exception if needed + } return req; } + void handle_exception_pplx_task(pplx::task previous_task) { @@ -49,6 +55,7 @@ handle_exception_pplx_task(pplx::task previous_task) catch (const std::exception& e) { SPDLOG_ERROR("pplx task error: {}", e.what()); + throw; // rethrow the exception if needed //#if defined(linux) || defined(__APPLE__) // SPDLOG_ERROR("stacktrace: {}", boost::stacktrace::to_string(boost::stacktrace::stacktrace())); //#endif diff --git a/src/core/atomicdex/utilities/cpprestsdk.utilities.hpp b/src/core/atomicdex/utilities/cpprestsdk.utilities.hpp index 5ce28c47d..2093bca26 100644 --- a/src/core/atomicdex/utilities/cpprestsdk.utilities.hpp +++ b/src/core/atomicdex/utilities/cpprestsdk.utilities.hpp @@ -34,5 +34,5 @@ using t_http_client_ptr = std::unique_ptr; using t_http_client = web::http::client::http_client; using t_http_request = web::http::http_request; -t_http_request create_json_post_request(nlohmann::json&& json_data); +t_http_request create_json_post_request(nlohmann::json&& json_data, const std::string& content_type = "application/json"); void handle_exception_pplx_task(pplx::task previous_task); \ No newline at end of file