diff --git a/lib/bloc/market_maker_bot/market_maker_bot/market_maker_bot_repository.dart b/lib/bloc/market_maker_bot/market_maker_bot/market_maker_bot_repository.dart index a328a69bf8..a4267d5e5a 100644 --- a/lib/bloc/market_maker_bot/market_maker_bot/market_maker_bot_repository.dart +++ b/lib/bloc/market_maker_bot/market_maker_bot/market_maker_bot_repository.dart @@ -158,7 +158,6 @@ class MarketMakerBotRepository { }; return MarketMakerBotParameters( botRefreshRate: mmSettings.botRefreshRate, - priceUrl: mmSettings.priceUrl, tradeCoinPairs: tradePairs, ); } diff --git a/lib/mm2/mm2_api/rpc/rpc_error.dart b/lib/mm2/mm2_api/rpc/rpc_error.dart index 27d6eb95b7..bc9600678d 100644 --- a/lib/mm2/mm2_api/rpc/rpc_error.dart +++ b/lib/mm2/mm2_api/rpc/rpc_error.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:equatable/equatable.dart'; import 'package:web_dex/mm2/mm2_api/rpc/rpc_error_type.dart'; @@ -23,15 +25,36 @@ class RpcError extends Equatable { this.id, }); - factory RpcError.fromJson(Map json) => RpcError( - mmrpc: json['mmrpc'] as String?, - error: json['error'] as String?, - errorPath: json['error_path'] as String?, - errorTrace: json['error_trace'] as String?, - errorType: RpcErrorType.fromString(json['error_type'] as String? ?? ''), - errorData: json['error_data'] as String?, - id: json['id'] as int?, - ); + factory RpcError.fromJson(Map json) { + // Handle nested error format where RPC error is in 'message' field + if (json.containsKey('message') && json['message'] is String) { + try { + final Map nestedError = + jsonDecode(json['message'] as String) as Map; + return RpcError._fromDirectJson(nestedError); + } catch (_) { + // If parsing fails, fall back to treating message as error string + return RpcError( + error: json['message'] as String?, + errorData: json['error'] as String?, + ); + } + } + + return RpcError._fromDirectJson(json); + } + + factory RpcError._fromDirectJson(Map json) => RpcError( + mmrpc: json['mmrpc'] as String?, + error: json['error'] as String?, + errorPath: json['error_path'] as String?, + errorTrace: json['error_trace'] as String?, + errorType: json['error_type'] != null + ? RpcErrorType.fromString(json['error_type'] as String) + : null, + errorData: json['error_data'] as String?, + id: json['id'] as int?, + ); final String? mmrpc; final String? error; @@ -42,14 +65,14 @@ class RpcError extends Equatable { final int? id; Map toJson() => { - 'mmrpc': mmrpc, - 'error': error, - 'error_path': errorPath, - 'error_trace': errorTrace, - 'error_type': errorType?.toString(), - 'error_data': errorData, - 'id': id, - }; + 'mmrpc': mmrpc, + 'error': error, + 'error_path': errorPath, + 'error_trace': errorTrace, + 'error_type': errorType?.toString(), + 'error_data': errorData, + 'id': id, + }; RpcError copyWith({ String? mmrpc, @@ -87,14 +110,6 @@ RpcError: { @override List get props { - return [ - mmrpc, - error, - errorPath, - errorTrace, - errorType, - errorData, - id, - ]; + return [mmrpc, error, errorPath, errorTrace, errorType, errorData, id]; } } diff --git a/lib/mm2/mm2_api/rpc/rpc_error_type.dart b/lib/mm2/mm2_api/rpc/rpc_error_type.dart index e1b133096a..4f92376e40 100644 --- a/lib/mm2/mm2_api/rpc/rpc_error_type.dart +++ b/lib/mm2/mm2_api/rpc/rpc_error_type.dart @@ -21,7 +21,7 @@ enum RpcErrorType { } } - static RpcErrorType fromString(String value) { + static RpcErrorType? fromString(String value) { switch (value) { case 'AlreadyStarted': return RpcErrorType.alreadyStarted; @@ -34,7 +34,7 @@ enum RpcErrorType { case 'InvalidRequest': return RpcErrorType.invalidRequest; default: - throw ArgumentError('Invalid value: $value'); + return null; } } } diff --git a/lib/model/settings/market_maker_bot_settings.dart b/lib/model/settings/market_maker_bot_settings.dart index 63137ecb59..8aee2a1284 100644 --- a/lib/model/settings/market_maker_bot_settings.dart +++ b/lib/model/settings/market_maker_bot_settings.dart @@ -1,13 +1,11 @@ import 'package:equatable/equatable.dart'; import 'package:web_dex/mm2/mm2_api/rpc/market_maker_bot/message_service_config/message_service_config.dart'; import 'package:web_dex/mm2/mm2_api/rpc/market_maker_bot/trade_coin_pair_config.dart'; -import 'package:web_dex/shared/constants.dart'; /// Settings for the KDF Simple Market Maker Bot. class MarketMakerBotSettings extends Equatable { const MarketMakerBotSettings({ required this.isMMBotEnabled, - required this.priceUrl, required this.botRefreshRate, required this.tradeCoinPairConfigs, this.messageServiceConfig, @@ -20,7 +18,6 @@ class MarketMakerBotSettings extends Equatable { factory MarketMakerBotSettings.initial() { return MarketMakerBotSettings( isMMBotEnabled: false, - priceUrl: pricesUrlV3.toString(), botRefreshRate: 60, tradeCoinPairConfigs: const [], messageServiceConfig: null, @@ -37,7 +34,6 @@ class MarketMakerBotSettings extends Equatable { return MarketMakerBotSettings( isMMBotEnabled: json['is_market_maker_bot_enabled'] as bool, - priceUrl: json['price_url'] as String, botRefreshRate: json['bot_refresh_rate'] as int, tradeCoinPairConfigs: (json['trade_coin_pair_configs'] as List) .map((e) => TradeCoinPairConfig.fromJson(e as Map)) @@ -53,9 +49,6 @@ class MarketMakerBotSettings extends Equatable { /// Whether the Market Maker Bot is enabled (menu item is shown or not). final bool isMMBotEnabled; - /// The URL to fetch the price data from. - final String priceUrl; - /// The refresh rate of the bot in seconds. final int botRefreshRate; @@ -70,10 +63,10 @@ class MarketMakerBotSettings extends Equatable { Map toJson() { return { 'is_market_maker_bot_enabled': isMMBotEnabled, - 'price_url': priceUrl, 'bot_refresh_rate': botRefreshRate, - 'trade_coin_pair_configs': - tradeCoinPairConfigs.map((e) => e.toJson()).toList(), + 'trade_coin_pair_configs': tradeCoinPairConfigs + .map((e) => e.toJson()) + .toList(), if (messageServiceConfig != null) 'message_service_config': messageServiceConfig?.toJson(), }; @@ -81,14 +74,12 @@ class MarketMakerBotSettings extends Equatable { MarketMakerBotSettings copyWith({ bool? isMMBotEnabled, - String? priceUrl, int? botRefreshRate, List? tradeCoinPairConfigs, MessageServiceConfig? messageServiceConfig, }) { return MarketMakerBotSettings( isMMBotEnabled: isMMBotEnabled ?? this.isMMBotEnabled, - priceUrl: priceUrl ?? this.priceUrl, botRefreshRate: botRefreshRate ?? this.botRefreshRate, tradeCoinPairConfigs: tradeCoinPairConfigs ?? this.tradeCoinPairConfigs, messageServiceConfig: messageServiceConfig ?? this.messageServiceConfig, @@ -97,10 +88,9 @@ class MarketMakerBotSettings extends Equatable { @override List get props => [ - isMMBotEnabled, - priceUrl, - botRefreshRate, - tradeCoinPairConfigs, - messageServiceConfig, - ]; + isMMBotEnabled, + botRefreshRate, + tradeCoinPairConfigs, + messageServiceConfig, + ]; } diff --git a/lib/shared/constants.dart b/lib/shared/constants.dart index 1755353875..56dc4a9edb 100644 --- a/lib/shared/constants.dart +++ b/lib/shared/constants.dart @@ -46,7 +46,7 @@ final RegExp matrixIdRegex = RegExp( r'^@[a-zA-Z0-9._=-]+:[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', ); final Uri pricesUrlV3 = Uri.parse( - 'https://defi-stats.komodo.earth/api/v3/prices/tickers_v2?expire_at=60', + 'https://prices.komodian.info/api/v2/tickers?expire_at=60', ); const int millisecondsIn24H = 86400000; diff --git a/lib/views/dex/dex_list_filter/mobile/dex_list_header_mobile.dart b/lib/views/dex/dex_list_filter/mobile/dex_list_header_mobile.dart index 33657b0278..28f9c059bb 100644 --- a/lib/views/dex/dex_list_filter/mobile/dex_list_header_mobile.dart +++ b/lib/views/dex/dex_list_filter/mobile/dex_list_header_mobile.dart @@ -3,30 +3,32 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:komodo_ui_kit/komodo_ui_kit.dart'; import 'package:web_dex/app_config/app_config.dart'; import 'package:web_dex/blocs/trading_entities_bloc.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/model/dex_list_type.dart'; import 'package:web_dex/model/my_orders/my_order.dart'; import 'package:web_dex/model/trading_entities_filter.dart'; -import 'package:komodo_ui_kit/komodo_ui_kit.dart'; class DexListHeaderMobile extends StatelessWidget { const DexListHeaderMobile({ - Key? key, + super.key, required this.listType, required this.entitiesFilterData, required this.onFilterPressed, required this.onFilterDataChange, required this.isFilterShown, this.centerWidget, - }) : super(key: key); + this.onCancelAll, + }); final DexListType listType; final TradingEntitiesFilter? entitiesFilterData; final bool isFilterShown; final VoidCallback onFilterPressed; final void Function(TradingEntitiesFilter?) onFilterDataChange; final Widget? centerWidget; + final VoidCallback? onCancelAll; @override Widget build(BuildContext context) { @@ -54,7 +56,8 @@ class DexListHeaderMobile extends StatelessWidget { text: LocaleKeys.cancelAll.tr(), width: 100, height: 30, - onPressed: () => tradingEntitiesBloc.cancelAllOrders(), + onPressed: + onCancelAll ?? () => tradingEntitiesBloc.cancelAllOrders(), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, diff --git a/lib/views/market_maker_bot/market_maker_bot_tab_content_wrapper.dart b/lib/views/market_maker_bot/market_maker_bot_tab_content_wrapper.dart index bcc6cf1cde..c14dab70b4 100644 --- a/lib/views/market_maker_bot/market_maker_bot_tab_content_wrapper.dart +++ b/lib/views/market_maker_bot/market_maker_bot_tab_content_wrapper.dart @@ -186,7 +186,10 @@ class _MobileWidget extends StatelessWidget { onFilterDataChange: onApplyFilter, onFilterPressed: onFilterTap, centerWidget: type == MarketMakerBotTabType.orders - ? _buildBotControls(context) + ? const _SimplifiedTradingBotControls() + : null, + onCancelAll: type == MarketMakerBotTabType.orders + ? () => _handleCancelAllOrders(context) : null, ), const SizedBox(height: 6), @@ -204,7 +207,23 @@ class _MobileWidget extends StatelessWidget { } } - Widget _buildBotControls(BuildContext context) { + void _handleCancelAllOrders(BuildContext context) { + final orderListBloc = context.read(); + final marketMakerBotBloc = context.read(); + final orders = orderListBloc.state.makerBotOrders; + + if (orders.isNotEmpty) { + final tradePairs = orders.map((e) => e.config).toList(); + marketMakerBotBloc.add(MarketMakerBotOrderCancelRequested(tradePairs)); + } + } +} + +class _SimplifiedTradingBotControls extends StatelessWidget { + const _SimplifiedTradingBotControls({super.key}); + + @override + Widget build(BuildContext context) { return BlocBuilder( builder: (context, botState) { return BlocBuilder(