diff --git a/packages/dragon_charts_flutter/example/pubspec.lock b/packages/dragon_charts_flutter/example/pubspec.lock index 3400241f..93965946 100644 --- a/packages/dragon_charts_flutter/example/pubspec.lock +++ b/packages/dragon_charts_flutter/example/pubspec.lock @@ -102,26 +102,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -227,18 +227,18 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" vector_math: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -248,5 +248,5 @@ packages: source: hosted version: "15.0.0" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/dragon_charts_flutter/example/pubspec_overrides.yaml b/packages/dragon_charts_flutter/example/pubspec_overrides.yaml new file mode 100644 index 00000000..40d95c58 --- /dev/null +++ b/packages/dragon_charts_flutter/example/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: dragon_charts_flutter +dependency_overrides: + dragon_charts_flutter: + path: .. diff --git a/packages/dragon_logs/example/pubspec.lock b/packages/dragon_logs/example/pubspec.lock index 886ae595..7d0df99a 100644 --- a/packages/dragon_logs/example/pubspec.lock +++ b/packages/dragon_logs/example/pubspec.lock @@ -136,26 +136,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -341,10 +341,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" typed_data: dependency: transitive description: @@ -397,10 +397,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: diff --git a/packages/dragon_logs/example/pubspec_overrides.yaml b/packages/dragon_logs/example/pubspec_overrides.yaml new file mode 100644 index 00000000..4caa48fc --- /dev/null +++ b/packages/dragon_logs/example/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: dragon_logs +dependency_overrides: + dragon_logs: + path: .. diff --git a/packages/komodo_defi_local_auth/lib/src/trezor/trezor_initialization_state.freezed.dart b/packages/komodo_defi_local_auth/lib/src/trezor/trezor_initialization_state.freezed.dart index 885dd250..ec3d31c2 100644 --- a/packages/komodo_defi_local_auth/lib/src/trezor/trezor_initialization_state.freezed.dart +++ b/packages/komodo_defi_local_auth/lib/src/trezor/trezor_initialization_state.freezed.dart @@ -50,7 +50,7 @@ $Res call({ }); - +$TrezorDeviceInfoCopyWith<$Res>? get deviceInfo; } /// @nodoc @@ -73,7 +73,19 @@ as String?,taskId: freezed == taskId ? _self.taskId : taskId // ignore: cast_nul as int?, )); } - +/// Create a copy of TrezorInitializationState +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$TrezorDeviceInfoCopyWith<$Res>? get deviceInfo { + if (_self.deviceInfo == null) { + return null; + } + + return $TrezorDeviceInfoCopyWith<$Res>(_self.deviceInfo!, (value) { + return _then(_self.copyWith(deviceInfo: value)); + }); +} } @@ -124,7 +136,7 @@ $Res call({ }); - +@override $TrezorDeviceInfoCopyWith<$Res>? get deviceInfo; } /// @nodoc @@ -148,7 +160,19 @@ as int?, )); } - +/// Create a copy of TrezorInitializationState +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$TrezorDeviceInfoCopyWith<$Res>? get deviceInfo { + if (_self.deviceInfo == null) { + return null; + } + + return $TrezorDeviceInfoCopyWith<$Res>(_self.deviceInfo!, (value) { + return _then(_self.copyWith(deviceInfo: value)); + }); +} } // dart format on diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/activation/activation_params/activation_params.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/activation/activation_params/activation_params.dart index ae1b44d7..7935a2e8 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/activation/activation_params/activation_params.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/activation/activation_params/activation_params.dart @@ -382,14 +382,13 @@ class ActivationRpcData { /// Creates [ActivationRpcData] from JSON configuration factory ActivationRpcData.fromJson(JsonMap json) { return ActivationRpcData( - lightWalletDServers: - json - .valueOrNull>('light_wallet_d_servers') - ?.cast(), + lightWalletDServers: json.valueOrNull>( + 'light_wallet_d_servers', + ), electrum: json - .valueOrNull>('electrum') - ?.map((e) => ActivationServers.fromJsonConfig(e as JsonMap)) + .valueOrNull('electrum') + ?.map(ActivationServers.fromJsonConfig) .toList(), syncParams: json.valueOrNull('sync_params'), ); diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/activation/activation_params/tendermint_activation_params.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/activation/activation_params/tendermint_activation_params.dart index e31009d4..bf9634c3 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/activation/activation_params/tendermint_activation_params.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/activation/activation_params/tendermint_activation_params.dart @@ -24,8 +24,8 @@ class TendermintActivationParams extends ActivationParams { .toList(); final tokensParams = json - .valueOrNull>('tokens_params') - ?.map((e) => TokensRequest.fromJson(e as JsonMap)) + .valueOrNull('tokens_params') + ?.map(TokensRequest.fromJson) .toList() ?? []; final getBalances = json.valueOrNull('get_balances') ?? true; diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/common_structures.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/common_structures.dart index 883a98f8..7662744f 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/common_structures.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/common_structures.dart @@ -52,10 +52,15 @@ export 'nft/nft_transfer_filter.dart'; export 'nft/withdraw_nft_data.dart'; export 'orderbook/order_info.dart'; export 'orderbook/order_type.dart'; +export 'orderbook/request_by.dart'; export 'pagination/history_target.dart'; export 'pagination/pagination.dart'; +export 'primitive/fraction.dart'; +export 'primitive/mm2_rational.dart'; export 'primitive/numeric_value.dart'; +export 'trading/match_by.dart'; export 'trading/order_status.dart'; +export 'trading/recent_swaps_filter.dart'; export 'trading/swap_info.dart'; export 'trading/swap_method.dart'; export 'transaction_history/transaction_info.dart'; diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/networks/lightning/channel/channels_index.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/networks/lightning/channel/channels_index.dart index 8b137891..08a06e49 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/networks/lightning/channel/channels_index.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/networks/lightning/channel/channels_index.dart @@ -1 +1,4 @@ - +export 'lightning_closed_channels_filter.dart'; +export 'lightning_open_channels_filter.dart'; +export 'lightning_channel_amount.dart'; +export 'lightning_channel_options.dart'; diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/orderbook/order_info.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/orderbook/order_info.dart index 236a229a..1f6bf326 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/orderbook/order_info.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/orderbook/order_info.dart @@ -1,4 +1,7 @@ import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; +import 'package:rational/rational.dart'; +import '../primitive/mm2_rational.dart'; +import '../primitive/fraction.dart'; /// Represents information about an order in the orderbook. /// @@ -28,6 +31,12 @@ class OrderInfo { required this.zcredits, required this.coin, required this.address, + this.priceFraction, + this.priceRat, + this.maxVolumeFraction, + this.maxVolumeRat, + this.minVolumeFraction, + this.minVolumeRat, }); /// Creates an [OrderInfo] instance from a JSON map. @@ -53,6 +62,30 @@ class OrderInfo { zcredits: json.value('zcredits'), coin: json.value('coin'), address: json.value('address'), + priceFraction: + json.valueOrNull('price_fraction') != null + ? Fraction.fromJson(json.value('price_fraction')) + : null, + priceRat: + json.valueOrNull>('price_rat') != null + ? rationalFromMm2(json.value>('price_rat')) + : null, + maxVolumeFraction: + json.valueOrNull('max_volume_fraction') != null + ? Fraction.fromJson(json.value('max_volume_fraction')) + : null, + maxVolumeRat: + json.valueOrNull>('max_volume_rat') != null + ? rationalFromMm2(json.value>('max_volume_rat')) + : null, + minVolumeFraction: + json.valueOrNull('min_volume_fraction') != null + ? Fraction.fromJson(json.value('min_volume_fraction')) + : null, + minVolumeRat: + json.valueOrNull>('min_volume_rat') != null + ? rationalFromMm2(json.value>('min_volume_rat')) + : null, ); } @@ -110,6 +143,24 @@ class OrderInfo { /// involving this order. final String address; + /// Optional fractional representation of the price + final Fraction? priceFraction; + + /// Optional rational representation of the price + final Rational? priceRat; + + /// Optional fractional representation of the maximum volume + final Fraction? maxVolumeFraction; + + /// Optional rational representation of the maximum volume + final Rational? maxVolumeRat; + + /// Optional fractional representation of the minimum volume + final Fraction? minVolumeFraction; + + /// Optional rational representation of the minimum volume + final Rational? minVolumeRat; + /// Converts this [OrderInfo] instance to a JSON map. /// /// The resulting map can be serialized to JSON and will contain all @@ -124,5 +175,13 @@ class OrderInfo { 'zcredits': zcredits, 'coin': coin, 'address': address, + if (priceFraction != null) 'price_fraction': priceFraction!.toJson(), + if (priceRat != null) 'price_rat': rationalToMm2(priceRat!), + if (maxVolumeFraction != null) + 'max_volume_fraction': maxVolumeFraction!.toJson(), + if (maxVolumeRat != null) 'max_volume_rat': rationalToMm2(maxVolumeRat!), + if (minVolumeFraction != null) + 'min_volume_fraction': minVolumeFraction!.toJson(), + if (minVolumeRat != null) 'min_volume_rat': rationalToMm2(minVolumeRat!), }; } \ No newline at end of file diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/orderbook/request_by.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/orderbook/request_by.dart new file mode 100644 index 00000000..094b6634 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/orderbook/request_by.dart @@ -0,0 +1,25 @@ +/// Defines the request_by object for best_orders. +/// +/// Mirrors the KDF API structure: +/// - type: "volume" | "number" +/// - value: Decimal (as string) when type == volume, Unsigned int when type == number +class RequestBy { + RequestBy._(this.type, this.volumeValue, this.numberValue); + + /// Create a volume-based request_by with a decimal value represented as string. + factory RequestBy.volume(String value) => RequestBy._('volume', value, null); + + /// Create a number-based request_by with an unsigned integer value. + factory RequestBy.number(int value) => RequestBy._('number', null, value); + + final String type; + final String? volumeValue; + final int? numberValue; + + Map toJson() { + return { + 'type': type, + 'value': type == 'volume' ? volumeValue! : numberValue!, + }; + } +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/primitive/fraction.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/primitive/fraction.dart new file mode 100644 index 00000000..922b8b8c --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/primitive/fraction.dart @@ -0,0 +1,20 @@ +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +class Fraction { + Fraction({required this.numer, required this.denom}); + + factory Fraction.fromJson(JsonMap json) { + return Fraction( + numer: json.value('numer'), + denom: json.value('denom'), + ); + } + + /// Numerator of the fraction + final String numer; + + /// Denominator of the fraction + final String denom; + + Map toJson() => {'numer': numer, 'denom': denom}; +} \ No newline at end of file diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/primitive/mm2_rational.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/primitive/mm2_rational.dart new file mode 100644 index 00000000..6f44aa49 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/primitive/mm2_rational.dart @@ -0,0 +1,53 @@ +import 'package:rational/rational.dart'; + +/// Signed big integer parts used by MM2 rational encoding +const int mm2LimbBase = 4294967296; // 2^32 + +BigInt bigIntFromMm2Json(List json) { + final sign = json[0] as int; + final limbs = (json[1] as List).cast(); + if (sign == 0) return BigInt.zero; + var value = BigInt.zero; + var multiplier = BigInt.one; + for (final limb in limbs) { + value += BigInt.from(limb) * multiplier; + multiplier *= BigInt.from(mm2LimbBase); + } + return sign < 0 ? -value : value; +} + +List bigIntToMm2Json(BigInt value) { + if (value == BigInt.zero) { + return [ + 0, + [0], + ]; + } + final sign = value.isNegative ? -1 : 1; + var x = value.abs(); + final limbs = []; + final base = BigInt.from(mm2LimbBase); + while (x > BigInt.zero) { + final q = x ~/ base; + final r = x - q * base; + limbs.add(r.toInt()); + x = q; + } + if (limbs.isEmpty) limbs.add(0); + return [sign, limbs]; +} + +Rational rationalFromMm2(List json) { + final numJson = (json[0] as List).cast(); + final denJson = (json[1] as List).cast(); + final num = bigIntFromMm2Json(numJson); + final den = bigIntFromMm2Json(denJson); + if (den == BigInt.zero) { + throw const FormatException('Denominator cannot be zero in MM2 rational'); + } + return Rational(num, den); +} + +List rationalToMm2(Rational r) { + return [bigIntToMm2Json(r.numerator), bigIntToMm2Json(r.denominator)]; +} \ No newline at end of file diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/match_by.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/match_by.dart new file mode 100644 index 00000000..9c948c52 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/match_by.dart @@ -0,0 +1,37 @@ +/// Match-by configuration for taker swaps. +/// +/// This structure maps to the `match_by` field accepted by KDF for taker +/// operations (e.g., `buy`, `sell`, unified `start_swap`). It allows limiting +/// order matching to particular orders or counterparties (pubkeys). +class MatchBy { + /// Creates a new [MatchBy] with a specific [type] and optional [data]. + MatchBy._(this.type, this.data); + + /// Match against any available orders (default behavior when omitted). + factory MatchBy.any() => MatchBy._('Any', null); + + /// Match only against orders created by the specified public keys. + /// + /// - [pubkeys]: List of hex-encoded public keys. + factory MatchBy.pubkeys(List pubkeys) => + MatchBy._('Pubkeys', pubkeys); + + /// Match only against specific order UUIDs. + /// + /// - [orderUuids]: List of order UUIDs. + factory MatchBy.orders(List orderUuids) => + MatchBy._('Orders', orderUuids); + + /// Matching strategy type. Accepted values are `Any`, `Orders`, `Pubkeys`. + final String type; + + /// Optional strategy data. For `Orders`/`Pubkeys` this should be a list of + /// strings (UUIDs or pubkeys). For `Any` it is omitted. + final List? data; + + /// Converts this [MatchBy] into a JSON map expected by the RPC. + Map toJson() => { + 'type': type, + if (data != null) 'data': data, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/order_status.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/order_status.dart index be52e8f4..6537d1c4 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/order_status.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/order_status.dart @@ -1,22 +1,26 @@ import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; +import 'package:rational/rational.dart'; +import '../primitive/mm2_rational.dart'; +import '../primitive/fraction.dart'; /// Order status information class OrderStatus { - OrderStatus({ - required this.type, - this.data, - }); + OrderStatus({required this.type, this.data}); factory OrderStatus.fromJson(JsonMap json) { return OrderStatus( type: json.value('type'), - data: json.containsKey('data') - ? OrderStatusData.fromJson(json.value('data')) - : null, + data: + json.containsKey('data') + ? OrderStatusData.fromJson(json.value('data')) + : null, ); } + /// Status type string as returned by the node final String type; + + /// Optional structured data for the status final OrderStatusData? data; Map toJson() => { @@ -27,11 +31,7 @@ class OrderStatus { /// Order status data class OrderStatusData { - OrderStatusData({ - this.swapUuid, - this.cancelledBy, - this.errorMessage, - }); + OrderStatusData({this.swapUuid, this.cancelledBy, this.errorMessage}); factory OrderStatusData.fromJson(JsonMap json) { return OrderStatusData( @@ -41,8 +41,13 @@ class OrderStatusData { ); } + /// Related swap UUID if available final String? swapUuid; + + /// Who cancelled the order (user/system), if applicable final String? cancelledBy; + + /// Error message if the order failed final String? errorMessage; Map toJson() { @@ -56,10 +61,7 @@ class OrderStatusData { /// Order match status class OrderMatchStatus { - OrderMatchStatus({ - required this.matched, - required this.ongoing, - }); + OrderMatchStatus({required this.matched, required this.ongoing}); factory OrderMatchStatus.fromJson(JsonMap json) { return OrderMatchStatus( @@ -68,32 +70,31 @@ class OrderMatchStatus { ); } + /// True if order has been matched final bool matched; + + /// True if matching is currently in progress final bool ongoing; - Map toJson() => { - 'matched': matched, - 'ongoing': ongoing, - }; + Map toJson() => {'matched': matched, 'ongoing': ongoing}; } /// Order match settings class OrderMatchBy { - OrderMatchBy({ - required this.type, - this.data, - }); + OrderMatchBy({required this.type, this.data}); factory OrderMatchBy.fromJson(JsonMap json) { + final dataJson = json.valueOrNull('data'); return OrderMatchBy( type: json.value('type'), - data: json.valueOrNull('data') != null - ? OrderMatchByData.fromJson(json.value('data')) - : null, + data: dataJson != null ? OrderMatchByData.fromJson(dataJson) : null, ); } + /// Matching strategy type final String type; + + /// Additional parameters for the strategy final OrderMatchByData? data; Map toJson() => { @@ -104,10 +105,7 @@ class OrderMatchBy { /// Order match by data class OrderMatchByData { - OrderMatchByData({ - this.coin, - this.value, - }); + OrderMatchByData({this.coin, this.value}); factory OrderMatchByData.fromJson(JsonMap json) { return OrderMatchByData( @@ -116,7 +114,10 @@ class OrderMatchByData { ); } + /// Coin ticker if the strategy is coin-specific final String? coin; + + /// Strategy parameter value final String? value; Map toJson() { @@ -145,9 +146,16 @@ class OrderConfirmationSettings { ); } + /// Required confirmations for the base coin final int baseConfs; + + /// Whether notarization is required for the base coin final bool baseNota; + + /// Required confirmations for the rel coin final int relConfs; + + /// Whether notarization is required for the rel coin final bool relNota; Map toJson() => { @@ -173,6 +181,10 @@ class MyOrderInfo { required this.status, this.matchBy, this.confSettings, + this.priceFraction, + this.priceRat, + this.volumeFraction, + this.volumeRat, }); factory MyOrderInfo.fromJson(JsonMap json) { @@ -187,28 +199,83 @@ class MyOrderInfo { lastUpdated: json.value('last_updated'), wasTimedOut: json.value('was_timed_out'), status: OrderStatus.fromJson(json.value('status')), - matchBy: json.containsKey('match_by') - ? OrderMatchBy.fromJson(json.value('match_by')) - : null, - confSettings: json.containsKey('conf_settings') - ? OrderConfirmationSettings.fromJson(json.value('conf_settings')) - : null, + matchBy: + json.containsKey('match_by') + ? OrderMatchBy.fromJson(json.value('match_by')) + : null, + confSettings: + json.containsKey('conf_settings') + ? OrderConfirmationSettings.fromJson( + json.value('conf_settings'), + ) + : null, + priceFraction: + json.valueOrNull('price_fraction') != null + ? Fraction.fromJson(json.value('price_fraction')) + : null, + priceRat: + json.valueOrNull>('price_rat') != null + ? rationalFromMm2(json.value>('price_rat')) + : null, + volumeFraction: + json.valueOrNull('volume_fraction') != null + ? Fraction.fromJson(json.value('volume_fraction')) + : null, + volumeRat: + json.valueOrNull>('volume_rat') != null + ? rationalFromMm2(json.value>('volume_rat')) + : null, ); } + /// Order UUID final String uuid; + + /// Order type (maker/taker) final String orderType; + + /// Base coin ticker final String base; + + /// Rel/quote coin ticker final String rel; + + /// Price per unit of base in rel (string numeric) final String price; + + /// Volume in base units (string numeric) final String volume; + + /// Creation timestamp (unix seconds) final int createdAt; + + /// Last updated timestamp (unix seconds) final int lastUpdated; + + /// True if the order timed out final bool wasTimedOut; + + /// Current status details final OrderStatus status; + + /// Matching strategy used for this order final OrderMatchBy? matchBy; + + /// Confirmation settings applied to this order final OrderConfirmationSettings? confSettings; + /// Optional fractional representation of the price + final Fraction? priceFraction; + + /// Optional rational representation of the price + final Rational? priceRat; + + /// Optional fractional representation of the volume + final Fraction? volumeFraction; + + /// Optional rational representation of the volume + final Rational? volumeRat; + Map toJson() => { 'uuid': uuid, 'order_type': orderType, @@ -222,5 +289,9 @@ class MyOrderInfo { 'status': status.toJson(), if (matchBy != null) 'match_by': matchBy!.toJson(), if (confSettings != null) 'conf_settings': confSettings!.toJson(), + if (priceFraction != null) 'price_fraction': priceFraction!.toJson(), + if (priceRat != null) 'price_rat': rationalToMm2(priceRat!), + if (volumeFraction != null) 'volume_fraction': volumeFraction!.toJson(), + if (volumeRat != null) 'volume_rat': rationalToMm2(volumeRat!), }; -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/recent_swaps_filter.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/recent_swaps_filter.dart new file mode 100644 index 00000000..11572ca9 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/recent_swaps_filter.dart @@ -0,0 +1,34 @@ +/// Filter for my_recent_swaps in KDF v2. +/// +/// All fields are optional and will be omitted from the payload when null. +class RecentSwapsFilter { + RecentSwapsFilter({ + this.limit, + this.pageNumber, + this.fromUuid, + this.myCoin, + this.otherCoin, + this.fromTimestamp, + this.toTimestamp, + }); + + final int? limit; + final int? pageNumber; + final String? fromUuid; + final String? myCoin; + final String? otherCoin; + final int? fromTimestamp; + final int? toTimestamp; + + Map toJson() { + return { + if (limit != null) 'limit': limit, + if (pageNumber != null) 'page_number': pageNumber, + if (fromUuid != null) 'from_uuid': fromUuid, + if (myCoin != null) 'my_coin': myCoin, + if (otherCoin != null) 'other_coin': otherCoin, + if (fromTimestamp != null) 'from_timestamp': fromTimestamp, + if (toTimestamp != null) 'to_timestamp': toTimestamp, + }; + } +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/swap_info.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/swap_info.dart index 374bb78d..b4fd5f3b 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/swap_info.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/swap_info.dart @@ -1,29 +1,32 @@ import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; +import 'package:rational/rational.dart'; +import '../primitive/mm2_rational.dart'; +import '../primitive/fraction.dart'; /// Comprehensive information about an atomic swap. -/// +/// /// This class represents the complete state and history of an atomic swap, /// including the involved coins, amounts, timeline, and event log. It's used /// across various RPC responses to provide detailed swap information. -/// +/// /// ## Swap Lifecycle: -/// +/// /// 1. **Initiation**: Swap is created with initial parameters /// 2. **Negotiation**: Peers exchange required information /// 3. **Payment**: Maker and taker send their payments /// 4. **Claiming**: Recipients claim their payments /// 5. **Completion**: Swap completes successfully or fails -/// +/// /// ## Event Tracking: -/// +/// /// The swap tracks two types of events: /// - **Success Events**: Milestones achieved during normal execution /// - **Error Events**: Problems encountered during the swap class SwapInfo { /// Creates a new [SwapInfo] instance. - /// + /// /// All parameters except [startedAt] and [finishedAt] are required. - /// + /// /// - [uuid]: Unique identifier for the swap /// - [myOrderUuid]: UUID of the order that initiated this swap /// - [takerAmount]: Amount of taker coin in the swap @@ -51,10 +54,14 @@ class SwapInfo { required this.errorEvents, this.startedAt, this.finishedAt, + this.takerAmountFraction, + this.takerAmountRat, + this.makerAmountFraction, + this.makerAmountRat, }); /// Creates a [SwapInfo] instance from a JSON map. - /// + /// /// Parses the swap information from the API response format. factory SwapInfo.fromJson(JsonMap json) { return SwapInfo( @@ -67,67 +74,79 @@ class SwapInfo { type: json.value('type'), gui: json.valueOrNull('gui'), mmVersion: json.valueOrNull('mm_version'), - successEvents: (json.value>('success_events')) - .map((e) => e as String) - .toList(), - errorEvents: (json.value>('error_events')) - .map((e) => e as String) - .toList(), + successEvents: json.value>('success_events'), + errorEvents: json.value>('error_events'), startedAt: json.valueOrNull('started_at'), finishedAt: json.valueOrNull('finished_at'), + takerAmountFraction: + json.valueOrNull('taker_amount_fraction') != null + ? Fraction.fromJson(json.value('taker_amount_fraction')) + : null, + takerAmountRat: + json.valueOrNull>('taker_amount_rat') != null + ? rationalFromMm2(json.value>('taker_amount_rat')) + : null, + makerAmountFraction: + json.valueOrNull('maker_amount_fraction') != null + ? Fraction.fromJson(json.value('maker_amount_fraction')) + : null, + makerAmountRat: + json.valueOrNull>('maker_amount_rat') != null + ? rationalFromMm2(json.value>('maker_amount_rat')) + : null, ); } /// Unique identifier for this swap. - /// + /// /// This UUID is used to track and reference the swap throughout its lifecycle. final String uuid; /// UUID of the order that initiated this swap. - /// + /// /// Links this swap to the original maker order that was matched. final String myOrderUuid; /// Amount of the taker coin involved in the swap. - /// + /// /// Expressed as a string to maintain precision. This is the amount /// the taker is sending in the swap. final String takerAmount; /// Ticker of the taker coin. - /// + /// /// Identifies which coin the taker is sending in the swap. final String takerCoin; /// Amount of the maker coin involved in the swap. - /// + /// /// Expressed as a string to maintain precision. This is the amount /// the maker is sending in the swap. final String makerAmount; /// Ticker of the maker coin. - /// + /// /// Identifies which coin the maker is sending in the swap. final String makerCoin; /// The type of swap from the user's perspective. - /// + /// /// Either "Maker" if the user created the initial order, or "Taker" /// if the user is taking an existing order. final String type; /// Optional identifier of the GUI that initiated the swap. - /// + /// /// Used for tracking which interface or bot created the swap. final String? gui; /// Version information of the market maker software. - /// + /// /// Helps with debugging and compatibility tracking. final String? mmVersion; /// List of successfully completed swap events. - /// + /// /// Events are added as the swap progresses through its lifecycle. /// Examples include: /// - "Started" @@ -139,7 +158,7 @@ class SwapInfo { final List successEvents; /// List of error events encountered during the swap. - /// + /// /// If the swap fails, this list contains information about what went wrong. /// Examples include: /// - "NegotiationFailed" @@ -148,17 +167,29 @@ class SwapInfo { final List errorEvents; /// Unix timestamp of when the swap started. - /// + /// /// Recorded when the swap is first initiated. final int? startedAt; /// Unix timestamp of when the swap finished. - /// + /// /// Recorded when the swap completes (successfully or with failure). final int? finishedAt; + /// Optional fractional representation of the taker amount + final Fraction? takerAmountFraction; + + /// Optional rational representation of the taker amount + final Rational? takerAmountRat; + + /// Optional fractional representation of the maker amount + final Fraction? makerAmountFraction; + + /// Optional rational representation of the maker amount + final Rational? makerAmountRat; + /// Converts this [SwapInfo] instance to a JSON map. - /// + /// /// The resulting map can be serialized to JSON and follows the /// expected API format. Map toJson() => { @@ -175,23 +206,31 @@ class SwapInfo { 'error_events': errorEvents, if (startedAt != null) 'started_at': startedAt, if (finishedAt != null) 'finished_at': finishedAt, + if (takerAmountFraction != null) + 'taker_amount_fraction': takerAmountFraction!.toJson(), + if (takerAmountRat != null) + 'taker_amount_rat': rationalToMm2(takerAmountRat!), + if (makerAmountFraction != null) + 'maker_amount_fraction': makerAmountFraction!.toJson(), + if (makerAmountRat != null) + 'maker_amount_rat': rationalToMm2(makerAmountRat!), }; /// Whether this swap has completed (successfully or with failure). - /// + /// /// A swap is considered complete if it has a [finishedAt] timestamp. bool get isComplete => finishedAt != null; /// Whether this swap completed successfully. - /// + /// /// A swap is successful if it's complete and has no error events. bool get isSuccessful => isComplete && errorEvents.isEmpty; /// Duration of the swap in seconds. - /// + /// /// Returns `null` if the swap hasn't started or finished yet. int? get durationSeconds { if (startedAt == null || finishedAt == null) return null; return finishedAt! - startedAt!; } -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/swap_method.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/swap_method.dart index 1e50686f..75dfa467 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/swap_method.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/trading/swap_method.dart @@ -1,48 +1,48 @@ /// Defines the method types available for swap operations. -/// +/// /// This enum represents the different ways a swap can be initiated /// in the Komodo DeFi Framework, determining the role and behavior /// of the participant in the swap. enum SwapMethod { /// Sets a maker order at a specific price. - /// + /// /// When using this method, the user becomes a maker, placing an order /// on the orderbook that waits to be matched by a taker. The order /// remains active until it's either matched, cancelled, or expires. setPrice, /// Initiates a buy swap as a taker. - /// + /// /// When using this method, the user becomes a taker, immediately /// attempting to match with the best available sell orders on the /// orderbook. The swap executes at the best available price. buy, /// Initiates a sell swap as a taker. - /// + /// /// When using this method, the user becomes a taker, immediately /// attempting to match with the best available buy orders on the /// orderbook. The swap executes at the best available price. sell; /// Converts this [SwapMethod] to its JSON representation. - /// + /// /// Returns a map with a single key corresponding to the method type, /// containing an empty map as its value. This format matches the /// expected API structure. - /// + /// /// Example outputs: /// - `setPrice` → `{"set_price": {}}` /// - `buy` → `{"buy": {}}` /// - `sell` → `{"sell": {}}` - Map toJson() { + Map> toJson() { switch (this) { case SwapMethod.setPrice: - return {'set_price': {}}; + return >{'set_price': {}}; case SwapMethod.buy: - return {'buy': {}}; + return >{'buy': {}}; case SwapMethod.sell: - return {'sell': {}}; + return >{'sell': {}}; } } -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/close_channel.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/close_channel.dart new file mode 100644 index 00000000..85a9a910 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/close_channel.dart @@ -0,0 +1,81 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to close a Lightning channel +class CloseChannelRequest + extends BaseRequest { + CloseChannelRequest({ + required String rpcPass, + required this.coin, + required this.rpcChannelId, + this.forceClose = false, + }) : super( + method: 'lightning::channels::close_channel', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + /// Coin ticker for the Lightning-enabled asset (e.g. 'BTC') + final String coin; + + /// RPC identifier of the channel to close (integer id) + final int rpcChannelId; + + /// If true, attempts an uncooperative force-close + final bool forceClose; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': { + 'coin': coin, + 'rpc_channel_id': rpcChannelId, + if (forceClose) 'force_close': forceClose, + }, + }); + + @override + CloseChannelResponse parse(Map json) => + CloseChannelResponse.parse(json); +} + +/// Response from closing a Lightning channel +class CloseChannelResponse extends BaseResponse { + CloseChannelResponse({ + required super.mmrpc, + required this.channelId, + this.closingTxId, + this.forceClosed, + }); + + factory CloseChannelResponse.parse(JsonMap json) { + final result = json.value('result'); + + return CloseChannelResponse( + mmrpc: json.value('mmrpc'), + channelId: result.valueOrNull('channel_id') ?? '', + closingTxId: + result.valueOrNull('closing_tx_id') ?? + result.valueOrNull('tx_id'), + forceClosed: result.valueOrNull('force_closed'), + ); + } + + /// Identifier of the channel that was closed + final String channelId; + + /// On-chain transaction ID used to close the channel, if applicable + final String? closingTxId; + + /// True if the channel was force-closed + final bool? forceClosed; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': { + 'channel_id': channelId, + if (closingTxId != null) 'closing_tx_id': closingTxId, + if (forceClosed != null) 'force_closed': forceClosed, + }, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/generate_invoice.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/generate_invoice.dart new file mode 100644 index 00000000..9d9705b4 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/generate_invoice.dart @@ -0,0 +1,84 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to generate a Lightning invoice +class GenerateInvoiceRequest + extends BaseRequest { + GenerateInvoiceRequest({ + required String rpcPass, + required this.coin, + required this.description, + this.amountMsat, + this.expiry, + }) : super( + method: 'lightning::payments::generate_invoice', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + /// Coin ticker for the Lightning-enabled asset (e.g. 'BTC') + final String coin; + + /// Human-readable description to embed in the invoice + final String description; + + /// Payment amount in millisatoshis; if null, invoice is amount-less + final int? amountMsat; + + /// Expiry time in seconds; implementation default is used when null + final int? expiry; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': { + 'coin': coin, + 'description': description, + if (amountMsat != null) 'amount_in_msat': amountMsat, + if (expiry != null) 'expiry': expiry, + }, + }); + + @override + GenerateInvoiceResponse parse(Map json) => + GenerateInvoiceResponse.parse(json); +} + +/// Response from generating a Lightning invoice +class GenerateInvoiceResponse extends BaseResponse { + GenerateInvoiceResponse({ + required super.mmrpc, + required this.invoice, + required this.paymentHash, + this.expiry, + }); + + factory GenerateInvoiceResponse.parse(JsonMap json) { + final result = json.value('result'); + + return GenerateInvoiceResponse( + mmrpc: json.value('mmrpc'), + invoice: result.value('invoice'), + paymentHash: result.value('payment_hash'), + expiry: result.valueOrNull('expiry'), + ); + } + + /// Encoded BOLT 11 invoice string + final String invoice; + + /// Payment hash associated with the invoice + final String paymentHash; + + /// Expiry time in seconds, if provided by the node + final int? expiry; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': { + 'invoice': invoice, + 'payment_hash': paymentHash, + if (expiry != null) 'expiry': expiry, + }, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_channel_details.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_channel_details.dart new file mode 100644 index 00000000..f351b203 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_channel_details.dart @@ -0,0 +1,47 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +class GetChannelDetailsRequest + extends BaseRequest { + GetChannelDetailsRequest({ + required String rpcPass, + required this.coin, + required this.rpcChannelId, + }) : super( + method: 'lightning::channels::get_channel_details', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + final String coin; + final int rpcChannelId; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': {'coin': coin, 'rpc_channel_id': rpcChannelId}, + }); + + @override + GetChannelDetailsResponse parse(Map json) => + GetChannelDetailsResponse.parse(json); +} + +class GetChannelDetailsResponse extends BaseResponse { + GetChannelDetailsResponse({required super.mmrpc, required this.channel}); + + factory GetChannelDetailsResponse.parse(JsonMap json) { + final result = json.value('result'); + return GetChannelDetailsResponse( + mmrpc: json.value('mmrpc'), + channel: ChannelInfo.fromJson(result.value('channel')), + ); + } + + final ChannelInfo channel; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'channel': channel.toJson()}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_channels.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_channels.dart index af7b3212..4629c543 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_channels.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_channels.dart @@ -2,14 +2,14 @@ import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; /// Request to get Lightning channels information. -/// +/// /// This RPC method retrieves information about both open and closed Lightning /// channels for a specified coin. Optionally, filters can be applied to /// narrow down the results. class GetChannelsRequest extends BaseRequest { /// Creates a new [GetChannelsRequest]. - /// + /// /// - [rpcPass]: RPC password for authentication /// - [coin]: The coin/ticker for which to retrieve channel information /// - [openFilter]: Optional filter for open channels @@ -20,38 +20,28 @@ class GetChannelsRequest this.openFilter, this.closedFilter, }) : super( - method: 'lightning::channels', + method: 'lightning::channels::list_open_channels_by_filter', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0, ); /// The coin/ticker for which to retrieve channel information. final String coin; - + /// Optional filter to apply to open channels. final LightningOpenChannelsFilter? openFilter; - + /// Optional filter to apply to closed channels. final LightningClosedChannelsFilter? closedFilter; @override Map toJson() { - final params = { - 'coin': coin, - }; - - if (openFilter != null) { - params['filter'] = { - 'open': openFilter!.toJson(), - }; - } else if (closedFilter != null) { - params['filter'] = { - 'closed': closedFilter!.toJson(), - }; - } - + // This request now targets open channels list; use open filter if provided. return super.toJson().deepMerge({ - 'params': params, + 'params': { + 'coin': coin, + if (openFilter != null) 'filter': openFilter!.toJson(), + }, }); } @@ -61,12 +51,12 @@ class GetChannelsRequest } /// Response containing Lightning channels information. -/// +/// /// This response provides lists of both open and closed channels, /// allowing for comprehensive channel management and monitoring. class GetChannelsResponse extends BaseResponse { /// Creates a new [GetChannelsResponse]. - /// + /// /// - [mmrpc]: The RPC version /// - [openChannels]: List of currently open channels /// - [closedChannels]: List of closed channels @@ -82,23 +72,20 @@ class GetChannelsResponse extends BaseResponse { return GetChannelsResponse( mmrpc: json.value('mmrpc'), - openChannels: (result.valueOrNull>('open_channels') ?? []) - .map((e) => ChannelInfo.fromJson(e as JsonMap)) - .toList(), - closedChannels: (result.valueOrNull>('closed_channels') ?? []) - .map((e) => ChannelInfo.fromJson(e as JsonMap)) - .toList(), + openChannels: + (result.valueOrNull('channels') ?? []) + .map(ChannelInfo.fromJson) + .toList(), + closedChannels: const [], ); } /// List of currently open Lightning channels. - /// + /// /// These channels are active and can be used for sending and receiving payments. final List openChannels; - /// List of closed Lightning channels. - /// - /// These channels have been closed and include closure reasons when available. + /// List of closed Lightning channels (not populated by this request). final List closedChannels; @override @@ -109,4 +96,4 @@ class GetChannelsResponse extends BaseResponse { 'closed_channels': closedChannels.map((e) => e.toJson()).toList(), }, }; -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_claimable_balances.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_claimable_balances.dart new file mode 100644 index 00000000..f06feb0d --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_claimable_balances.dart @@ -0,0 +1,51 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +class GetClaimableBalancesRequest + extends BaseRequest { + GetClaimableBalancesRequest({ + required String rpcPass, + required this.coin, + this.includeOpenChannelsBalances, + }) : super( + method: 'lightning::channels::get_claimable_balances', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + final String coin; + final bool? includeOpenChannelsBalances; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': { + 'coin': coin, + if (includeOpenChannelsBalances != null) + 'include_open_channels_balances': includeOpenChannelsBalances, + }, + }); + + @override + GetClaimableBalancesResponse parse(Map json) => + GetClaimableBalancesResponse.parse(json); +} + +class GetClaimableBalancesResponse extends BaseResponse { + GetClaimableBalancesResponse({required super.mmrpc, required this.balances}); + + factory GetClaimableBalancesResponse.parse(JsonMap json) { + final result = json.value('result'); + return GetClaimableBalancesResponse( + mmrpc: json.value('mmrpc'), + balances: result.value('balances'), + ); + } + + final Map balances; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'balances': balances}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_payment_history.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_payment_history.dart new file mode 100644 index 00000000..dca51917 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/get_payment_history.dart @@ -0,0 +1,65 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to get Lightning payments history +class GetPaymentHistoryRequest + extends BaseRequest { + GetPaymentHistoryRequest({ + required String rpcPass, + required this.coin, + this.filter, + this.pagination, + }) : super( + method: 'lightning::payments::list_payments_by_filter', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + /// Coin ticker to query payment history for + final String coin; + + /// Optional filter to restrict returned payments + final LightningPaymentFilter? filter; + + /// Optional pagination parameters + final Pagination? pagination; + + @override + Map toJson() { + final params = {'coin': coin}; + if (filter != null) params['filter'] = filter!.toJson(); + if (pagination != null) params['pagination'] = pagination!.toJson(); + + return super.toJson().deepMerge({'params': params}); + } + + @override + GetPaymentHistoryResponse parse(Map json) => + GetPaymentHistoryResponse.parse(json); +} + +/// Response containing Lightning payments history +class GetPaymentHistoryResponse extends BaseResponse { + GetPaymentHistoryResponse({required super.mmrpc, required this.payments}); + + factory GetPaymentHistoryResponse.parse(JsonMap json) { + final result = json.value('result'); + + return GetPaymentHistoryResponse( + mmrpc: json.value('mmrpc'), + payments: + (result.valueOrNull('payments') ?? []) + .map(LightningPayment.fromJson) + .toList(), + ); + } + + /// List of Lightning payments matching the query + final List payments; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'payments': payments.map((e) => e.toJson()).toList()}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/lightning_rpc_namespace.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/lightning_rpc_namespace.dart index 9e69d2f4..9f941874 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/lightning_rpc_namespace.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/lightning_rpc_namespace.dart @@ -1,28 +1,28 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; /// RPC namespace for Lightning Network operations. -/// +/// /// This namespace provides methods for managing Lightning Network functionality -/// within the Komodo DeFi Framework. It includes operations for channel management, -/// payment processing, and Lightning Network node administration. -/// +/// within the Komodo DeFi Framework. It includes operations for channel +/// management, payment processing, and Lightning Network node administration. +/// /// ## Key Features: -/// +/// /// - **Channel Management**: Open, close, and monitor Lightning channels /// - **Payment Operations**: Generate invoices and send payments /// - **Network Participation**: Enable Lightning functionality for supported coins -/// +/// /// ## Usage Example: -/// +/// /// ```dart /// final lightning = client.lightning; -/// +/// /// // Enable Lightning for a coin /// final response = await lightning.enableLightning( /// ticker: 'BTC', /// activationParams: LightningActivationParams(...), /// ); -/// +/// /// // Open a channel /// final channel = await lightning.openChannel( /// coin: 'BTC', @@ -32,23 +32,23 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; /// ``` class LightningMethodsNamespace extends BaseRpcMethodNamespace { /// Creates a new [LightningMethodsNamespace] instance. - /// + /// /// This is typically called internally by the [KomodoDefiRpcMethods] class. LightningMethodsNamespace(super.client); /// Enables Lightning Network functionality for a specific coin. - /// + /// /// This method initializes the Lightning Network daemon for the specified /// coin, setting up the necessary infrastructure for channel operations /// and payment processing. - /// + /// /// - [ticker]: The coin ticker to enable Lightning for (e.g., 'BTC') /// - [activationParams]: Configuration parameters for Lightning activation /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with an [EnableLightningResponse] /// containing the node ID and configuration details. - /// + /// /// Throws an exception if: /// - The coin doesn't support Lightning Network /// - Lightning is already enabled for the coin @@ -68,19 +68,19 @@ class LightningMethodsNamespace extends BaseRpcMethodNamespace { } /// Retrieves information about Lightning channels. - /// + /// /// This method fetches detailed information about both open and closed /// Lightning channels for the specified coin. Filters can be applied /// to narrow down the results. - /// + /// /// - [coin]: The coin ticker to query channels for /// - [openFilter]: Optional filter for open channels /// - [closedFilter]: Optional filter for closed channels /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [GetChannelsResponse] /// containing lists of open and closed channels. - /// + /// /// Note: Only one filter (open or closed) can be applied at a time. Future getChannels({ required String coin, @@ -98,29 +98,75 @@ class LightningMethodsNamespace extends BaseRpcMethodNamespace { ); } + /// Lists closed channels by optional filter. + Future listClosedChannelsByFilter({ + required String coin, + LightningClosedChannelsFilter? filter, + String? rpcPass, + }) { + return execute( + ListClosedChannelsByFilterRequest( + rpcPass: rpcPass ?? this.rpcPass ?? '', + coin: coin, + filter: filter, + ), + ); + } + + /// Gets a specific channel details by rpc_channel_id. + Future getChannelDetails({ + required String coin, + required int rpcChannelId, + String? rpcPass, + }) { + return execute( + GetChannelDetailsRequest( + rpcPass: rpcPass ?? this.rpcPass ?? '', + coin: coin, + rpcChannelId: rpcChannelId, + ), + ); + } + + /// Gets claimable balances (optionally including open channels balances). + Future getClaimableBalances({ + required String coin, + bool? includeOpenChannelsBalances, + String? rpcPass, + }) { + return execute( + GetClaimableBalancesRequest( + rpcPass: rpcPass ?? this.rpcPass ?? '', + coin: coin, + includeOpenChannelsBalances: includeOpenChannelsBalances, + ), + ); + } + /// Opens a new Lightning channel with a specified node. - /// + /// /// This method initiates the opening of a Lightning channel by creating /// an on-chain funding transaction. The channel becomes usable after /// sufficient blockchain confirmations. - /// + /// /// - [coin]: The coin ticker for the channel /// - [nodeId]: The public key of the node to open a channel with /// - [amountSat]: The channel capacity in satoshis /// - [options]: Optional channel configuration options /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with an [OpenChannelResponse] /// containing the channel ID and funding transaction details. - /// + /// /// Throws an exception if: /// - Insufficient balance for channel funding /// - Target node is unreachable /// - Channel amount is below minimum requirements Future openChannel({ required String coin, - required String nodeId, - required int amountSat, + required String nodeAddress, + required LightningChannelAmount amount, + int? pushMsat, LightningChannelOptions? options, String? rpcPass, }) { @@ -128,31 +174,32 @@ class LightningMethodsNamespace extends BaseRpcMethodNamespace { OpenChannelRequest( rpcPass: rpcPass ?? this.rpcPass ?? '', coin: coin, - nodeId: nodeId, - amountSat: amountSat, + nodeAddress: nodeAddress, + amount: amount, + pushMsat: pushMsat, options: options, ), ); } /// Closes an existing Lightning channel. - /// + /// /// This method initiates the closing of a Lightning channel. Channels /// can be closed cooperatively (mutual close) or unilaterally (force close). - /// + /// /// - [coin]: The coin ticker for the channel - /// - [channelId]: The ID of the channel to close + /// - [rpcChannelId]: The ID of the channel to close /// - [forceClose]: Whether to force close the channel unilaterally /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [CloseChannelResponse] /// containing the closing transaction details. - /// + /// /// Note: Force closing a channel may result in funds being locked /// for a timeout period and higher on-chain fees. Future closeChannel({ required String coin, - required String channelId, + required int rpcChannelId, bool forceClose = false, String? rpcPass, }) { @@ -160,26 +207,26 @@ class LightningMethodsNamespace extends BaseRpcMethodNamespace { CloseChannelRequest( rpcPass: rpcPass ?? this.rpcPass ?? '', coin: coin, - channelId: channelId, + rpcChannelId: rpcChannelId, forceClose: forceClose, ), ); } /// Generates a Lightning invoice for receiving payments. - /// + /// /// This method creates a BOLT 11 invoice that can be shared with /// payers to receive Lightning payments. - /// + /// /// - [coin]: The coin ticker for the invoice /// - [amountMsat]: The invoice amount in millisatoshis /// - [description]: Human-readable description for the invoice /// - [expiry]: Optional expiry time in seconds (default varies by implementation) /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [GenerateInvoiceResponse] /// containing the encoded invoice string and payment hash. - /// + /// /// The generated invoice includes: /// - Payment amount /// - Recipient node information @@ -187,8 +234,8 @@ class LightningMethodsNamespace extends BaseRpcMethodNamespace { /// - Expiry timestamp Future generateInvoice({ required String coin, - required int amountMsat, required String description, + int? amountMsat, int? expiry, String? rpcPass, }) { @@ -196,27 +243,27 @@ class LightningMethodsNamespace extends BaseRpcMethodNamespace { GenerateInvoiceRequest( rpcPass: rpcPass ?? this.rpcPass ?? '', coin: coin, - amountMsat: amountMsat, description: description, + amountMsat: amountMsat, expiry: expiry, ), ); } /// Pays a Lightning invoice. - /// + /// /// This method attempts to send a payment for the specified Lightning /// invoice. The payment is routed through the Lightning Network to /// reach the recipient. - /// + /// /// - [coin]: The coin ticker for the payment /// - [invoice]: The BOLT 11 invoice string to pay /// - [maxFeeMsat]: Optional maximum fee willing to pay in millisatoshis /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [PayInvoiceResponse] /// containing the payment preimage and route details. - /// + /// /// Throws an exception if: /// - Invoice is invalid or expired /// - No route to recipient is found @@ -224,33 +271,31 @@ class LightningMethodsNamespace extends BaseRpcMethodNamespace { /// - Payment fails after retries Future payInvoice({ required String coin, - required String invoice, - int? maxFeeMsat, + required LightningPayment payment, String? rpcPass, }) { return execute( PayInvoiceRequest( rpcPass: rpcPass ?? this.rpcPass ?? '', coin: coin, - invoice: invoice, - maxFeeMsat: maxFeeMsat, + payment: payment, ), ); } /// Retrieves Lightning payment history. - /// + /// /// This method fetches the history of Lightning payments (both sent /// and received) with optional filtering and pagination support. - /// + /// /// - [coin]: The coin ticker to query payment history for /// - [filter]: Optional filter to narrow down results /// - [pagination]: Optional pagination parameters /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [GetPaymentHistoryResponse] /// containing a list of payment records. - /// + /// /// Payment records include: /// - Payment hash and preimage /// - Amount and fees @@ -272,4 +317,4 @@ class LightningMethodsNamespace extends BaseRpcMethodNamespace { ), ); } -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/list_closed_channels_by_filter.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/list_closed_channels_by_filter.dart new file mode 100644 index 00000000..9d3dabf7 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/list_closed_channels_by_filter.dart @@ -0,0 +1,53 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +class ListClosedChannelsByFilterRequest + extends + BaseRequest { + ListClosedChannelsByFilterRequest({ + required String rpcPass, + required this.coin, + this.filter, + }) : super( + method: 'lightning::channels::list_closed_channels_by_filter', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + final String coin; + final LightningClosedChannelsFilter? filter; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': {'coin': coin, if (filter != null) 'filter': filter!.toJson()}, + }); + + @override + ListClosedChannelsByFilterResponse parse(Map json) => + ListClosedChannelsByFilterResponse.parse(json); +} + +class ListClosedChannelsByFilterResponse extends BaseResponse { + ListClosedChannelsByFilterResponse({ + required super.mmrpc, + required this.channels, + }); + + factory ListClosedChannelsByFilterResponse.parse(JsonMap json) { + final result = json.value('result'); + return ListClosedChannelsByFilterResponse( + mmrpc: json.value('mmrpc'), + channels: + (result.valueOrNull('channels') ?? []) + .map(ChannelInfo.fromJson) + .toList(), + ); + } + final List channels; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'channels': channels.map((e) => e.toJson()).toList()}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/open_channel.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/open_channel.dart index 94521b26..8b7d8f78 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/open_channel.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/open_channel.dart @@ -7,34 +7,32 @@ class OpenChannelRequest OpenChannelRequest({ required String rpcPass, required this.coin, - required this.nodeId, - required this.amountSat, + required this.nodeAddress, + required this.amount, + this.pushMsat, this.options, }) : super( - method: 'lightning::open_channel', + method: 'lightning::channels::open_channel', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0, ); final String coin; - final String nodeId; - final int amountSat; + final String nodeAddress; + final LightningChannelAmount amount; + final int? pushMsat; final LightningChannelOptions? options; @override Map toJson() { - final params = { - 'coin': coin, - 'node_id': nodeId, - 'amount_sat': amountSat, - }; - - if (options != null) { - params['options'] = options!.toJson(); - } - return super.toJson().deepMerge({ - 'params': params, + 'params': { + 'coin': coin, + 'node_address': nodeAddress, + 'amount': amount.toJson(), + if (pushMsat != null) 'push_msat': pushMsat, + if (options != null) 'channel_options': options!.toJson(), + }, }); } @@ -67,9 +65,6 @@ class OpenChannelResponse extends BaseResponse { @override Map toJson() => { 'mmrpc': mmrpc, - 'result': { - 'channel_id': channelId, - 'funding_tx_id': fundingTxId, - }, + 'result': {'channel_id': channelId, 'funding_tx_id': fundingTxId}, }; -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/pay_invoice.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/pay_invoice.dart new file mode 100644 index 00000000..a8217d15 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/lightning/pay_invoice.dart @@ -0,0 +1,71 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to pay a Lightning invoice +class PayInvoiceRequest + extends BaseRequest { + PayInvoiceRequest({ + required String rpcPass, + required this.coin, + required this.payment, + }) : super( + method: 'lightning::payments::send_payment', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + /// Coin ticker for the Lightning-enabled asset (e.g. 'BTC') + final String coin; + + /// Payment union: {type: 'invoice'|'keysend', ...} + final LightningPayment payment; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': {'coin': coin, 'payment': payment.toJson()}, + }); + + @override + PayInvoiceResponse parse(Map json) => + PayInvoiceResponse.parse(json); +} + +/// Response from paying a Lightning invoice +class PayInvoiceResponse extends BaseResponse { + PayInvoiceResponse({ + required super.mmrpc, + required this.preimage, + required this.feePaidMsat, + this.routeHops, + }); + + factory PayInvoiceResponse.parse(JsonMap json) { + final result = json.value('result'); + + return PayInvoiceResponse( + mmrpc: json.value('mmrpc'), + preimage: result.valueOrNull('preimage') ?? '', + feePaidMsat: result.valueOrNull('fee_paid_msat') ?? 0, + routeHops: result.valueOrNull?>('route_hops'), + ); + } + + /// Payment preimage proving successful payment + final String preimage; + + /// Total fee paid in millisatoshis + final int feePaidMsat; + + /// Route hop pubkeys, if available + final List? routeHops; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': { + 'preimage': preimage, + 'fee_paid_msat': feePaidMsat, + if (routeHops != null) 'route_hops': routeHops, + }, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/best_orders.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/best_orders.dart new file mode 100644 index 00000000..321d36c8 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/best_orders.dart @@ -0,0 +1,63 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to get best orders for a coin and action +class BestOrdersRequest + extends BaseRequest { + BestOrdersRequest({ + required String rpcPass, + required this.coin, + required this.action, + required this.requestBy, + this.excludeMine, + }) : super(method: 'best_orders', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0); + + /// Coin ticker to trade + final String coin; + + /// Desired trade direction + final OrderType action; + + /// Request-by selector (volume or number) + final RequestBy requestBy; + + /// Whether to exclude orders created by the current wallet. Defaults to false in API. + final bool? excludeMine; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': { + 'coin': coin, + 'action': action.toJson(), + if (excludeMine != null) 'exclude_mine': excludeMine, + 'request_by': requestBy.toJson(), + }, + }); + + @override + BestOrdersResponse parse(Map json) => + BestOrdersResponse.parse(json); +} + +/// Response containing best orders list +class BestOrdersResponse extends BaseResponse { + BestOrdersResponse({required super.mmrpc, required this.orders}); + + factory BestOrdersResponse.parse(JsonMap json) { + final result = json.value('result'); + + return BestOrdersResponse( + mmrpc: json.value('mmrpc'), + orders: result.value('orders').map(OrderInfo.fromJson).toList(), + ); + } + + /// Sorted list of best orders that can fulfill the request + final List orders; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'orders': orders.map((e) => e.toJson()).toList()}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/cancel_all_orders.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/cancel_all_orders.dart new file mode 100644 index 00000000..e6ee56f9 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/cancel_all_orders.dart @@ -0,0 +1,47 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to cancel orders by type (all or by coin) +class CancelAllOrdersRequest + extends BaseRequest { + CancelAllOrdersRequest({required String rpcPass, this.cancelType}) + : super( + method: 'cancel_all_orders', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + /// Criteria for which orders to cancel (all or by coin) + final CancelOrdersType? cancelType; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': {if (cancelType != null) 'cancel_by': cancelType!.toJson()}, + }); + + @override + CancelAllOrdersResponse parse(Map json) => + CancelAllOrdersResponse.parse(json); +} + +/// Response from cancelling orders by type +class CancelAllOrdersResponse extends BaseResponse { + CancelAllOrdersResponse({required super.mmrpc, required this.cancelled}); + + factory CancelAllOrdersResponse.parse(JsonMap json) { + final result = json.value('result'); + return CancelAllOrdersResponse( + mmrpc: json.value('mmrpc'), + cancelled: result.value('cancelled'), + ); + } + + /// True if the cancellation request succeeded + final bool cancelled; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'cancelled': cancelled}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/cancel_order.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/cancel_order.dart new file mode 100644 index 00000000..1c2daf2f --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/cancel_order.dart @@ -0,0 +1,43 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to cancel a specific order +class CancelOrderRequest + extends BaseRequest { + CancelOrderRequest({required String rpcPass, required this.uuid}) + : super(method: 'cancel_order', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0); + + /// UUID of the order to cancel + final String uuid; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': {'uuid': uuid}, + }); + + @override + CancelOrderResponse parse(Map json) => + CancelOrderResponse.parse(json); +} + +/// Response from cancelling an order +class CancelOrderResponse extends BaseResponse { + CancelOrderResponse({required super.mmrpc, required this.cancelled}); + + factory CancelOrderResponse.parse(JsonMap json) { + final result = json.value('result'); + return CancelOrderResponse( + mmrpc: json.value('mmrpc'), + cancelled: result.value('cancelled'), + ); + } + + /// True if the order was cancelled successfully + final bool cancelled; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'cancelled': cancelled}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/my_orders.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/my_orders.dart new file mode 100644 index 00000000..814a6668 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/my_orders.dart @@ -0,0 +1,37 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to get current user's orders +class MyOrdersRequest + extends BaseRequest { + MyOrdersRequest({required String rpcPass}) + : super(method: 'my_orders', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0); + + @override + MyOrdersResponse parse(Map json) => + MyOrdersResponse.parse(json); +} + +/// Response with user's orders +class MyOrdersResponse extends BaseResponse { + MyOrdersResponse({required super.mmrpc, required this.orders}); + + factory MyOrdersResponse.parse(JsonMap json) { + final result = json.value('result'); + + return MyOrdersResponse( + mmrpc: json.value('mmrpc'), + orders: + result.value('orders').map(MyOrderInfo.fromJson).toList(), + ); + } + + /// List of orders created by the current wallet + final List orders; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'orders': orders.map((e) => e.toJson()).toList()}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook.dart index 107f8e87..21d91d3f 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook.dart @@ -2,13 +2,13 @@ import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; /// Request to retrieve orderbook information for a trading pair. -/// +/// /// This RPC method fetches the current state of the orderbook for a specified /// trading pair, including all active buy and sell orders. class OrderbookRequest extends BaseRequest { /// Creates a new [OrderbookRequest]. - /// + /// /// - [rpcPass]: RPC password for authentication /// - [base]: The base coin of the trading pair /// - [rel]: The rel/quote coin of the trading pair @@ -16,29 +16,22 @@ class OrderbookRequest required String rpcPass, required this.base, required this.rel, - }) : super( - method: 'orderbook', - rpcPass: rpcPass, - mmrpc: RpcVersion.v2_0, - ); + }) : super(method: 'orderbook', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0); /// The base coin of the trading pair. - /// + /// /// This is the coin being bought or sold in orders. final String base; - + /// The rel/quote coin of the trading pair. - /// + /// /// This is the coin used to price the base coin. final String rel; @override Map toJson() { return super.toJson().deepMerge({ - 'params': { - 'base': base, - 'rel': rel, - }, + 'params': {'base': base, 'rel': rel}, }); } @@ -48,12 +41,12 @@ class OrderbookRequest } /// Response containing orderbook data for a trading pair. -/// +/// /// This response provides comprehensive orderbook information including /// all active bids and asks, along with metadata about the orderbook state. class OrderbookResponse extends BaseResponse { /// Creates a new [OrderbookResponse]. - /// + /// /// - [mmrpc]: The RPC version /// - [base]: The base coin of the trading pair /// - [rel]: The rel/quote coin of the trading pair @@ -81,12 +74,8 @@ class OrderbookResponse extends BaseResponse { mmrpc: json.value('mmrpc'), base: result.value('base'), rel: result.value('rel'), - bids: (result.value>('bids')) - .map((e) => OrderInfo.fromJson(e as JsonMap)) - .toList(), - asks: (result.value>('asks')) - .map((e) => OrderInfo.fromJson(e as JsonMap)) - .toList(), + bids: result.value('bids').map(OrderInfo.fromJson).toList(), + asks: result.value('asks').map(OrderInfo.fromJson).toList(), numBids: result.value('num_bids'), numAsks: result.value('num_asks'), timestamp: result.value('timestamp'), @@ -95,34 +84,34 @@ class OrderbookResponse extends BaseResponse { /// The base coin of the trading pair. final String base; - + /// The rel/quote coin of the trading pair. final String rel; - + /// List of buy orders (bids) in the orderbook. - /// + /// /// These are orders from users wanting to buy the base coin with the rel coin. /// Orders are typically sorted by price in descending order (best bid first). final List bids; - + /// List of sell orders (asks) in the orderbook. - /// + /// /// These are orders from users wanting to sell the base coin for the rel coin. /// Orders are typically sorted by price in ascending order (best ask first). final List asks; - + /// Total number of bid orders in the orderbook. - /// + /// /// This may be larger than the length of [bids] if pagination is applied. final int numBids; - + /// Total number of ask orders in the orderbook. - /// + /// /// This may be larger than the length of [asks] if pagination is applied. final int numAsks; - + /// Unix timestamp of when this orderbook snapshot was taken. - /// + /// /// Useful for determining the freshness of the orderbook data. final int timestamp; @@ -142,39 +131,70 @@ class OrderbookResponse extends BaseResponse { } /// Represents the type of order cancellation. -/// +/// /// This class provides factory methods to create different cancellation types /// for the cancel_all_orders RPC method. class CancelOrdersType { /// Creates a cancellation type to cancel all orders across all coins. - CancelOrdersType.all() : coin = null, _type = 'all'; - + CancelOrdersType.all() + : ticker = null, + base = null, + rel = null, + _type = 'all'; + /// Creates a cancellation type to cancel all orders for a specific coin. - /// + /// /// - [coin]: The ticker of the coin whose orders should be cancelled - CancelOrdersType.coin(this.coin) : _type = 'coin'; + CancelOrdersType.coin(String coin) + : ticker = coin, + base = null, + rel = null, + _type = 'coin'; + + /// Creates a cancellation type to cancel all orders for a specific pair. + /// + /// - [base]: The base coin ticker + /// - [rel]: The rel/quote coin ticker + CancelOrdersType.pair({required String base, required String rel}) + : base = base, + rel = rel, + ticker = null, + _type = 'pair'; + + /// The coin ticker for coin-specific cancellation (used when [_type] == 'coin'). + final String? ticker; + + /// Base coin ticker (used when [_type] == 'pair'). + final String? base; + + /// Rel/quote coin ticker (used when [_type] == 'pair'). + final String? rel; - /// The coin ticker for coin-specific cancellation. - /// - /// `null` when cancelling all orders across all coins. - final String? coin; - /// Internal type identifier. final String _type; /// Converts this [CancelOrdersType] to its JSON representation. - /// + /// /// Returns different structures based on the cancellation type: /// - For all orders: `{"type": "all"}` /// - For specific coin: `{"type": "coin", "data": {"coin": "TICKER"}}` + /// - For specific pair: `{"type": "pair", "data": {"base": "BASE", "rel": "REL"}}` Map toJson() { if (_type == 'all') { return {'type': 'all'}; - } else { + } + + if (_type == 'coin') { return { 'type': 'coin', - 'data': {'coin': coin}, + 'data': {'coin': ticker}, }; } + + // Pair + return { + 'type': 'pair', + 'data': {'base': base, 'rel': rel}, + }; } -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook_depth.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook_depth.dart new file mode 100644 index 00000000..73133249 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook_depth.dart @@ -0,0 +1,58 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to get orderbook depth for multiple pairs +class OrderbookDepthRequest + extends BaseRequest { + OrderbookDepthRequest({required String rpcPass, required this.pairs}) + : super( + method: 'orderbook_depth', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + /// List of trading pairs to query depth for + final List pairs; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': { + 'pairs': pairs.map((e) => [e.base, e.rel]).toList(), + }, + }); + + @override + OrderbookDepthResponse parse(Map json) => + OrderbookDepthResponse.parse(json); +} + +/// Response containing orderbook depth for pairs +class OrderbookDepthResponse extends BaseResponse { + OrderbookDepthResponse({required super.mmrpc, required this.depth}); + + factory OrderbookDepthResponse.parse(JsonMap json) { + final result = json.value('result'); + + return OrderbookDepthResponse( + mmrpc: json.value('mmrpc'), + depth: result.map( + (key, value) => MapEntry( + key, + OrderbookResponse.parse({ + 'mmrpc': json.value('mmrpc'), + 'result': value as JsonMap, + }), + ), + ), + ); + } + + /// Map of "BASE-REL" -> OrderbookResponse snapshot + final Map depth; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': depth.map((k, v) => MapEntry(k, v.toJson()['result'])), + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook_rpc_namespace.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook_rpc_namespace.dart index 3c54faa5..a30d575e 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook_rpc_namespace.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/orderbook_rpc_namespace.dart @@ -1,36 +1,36 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; /// RPC namespace for orderbook operations. -/// +/// /// This namespace provides methods for interacting with the decentralized /// orderbook in the Komodo DeFi Framework. It enables users to view market /// depth, place orders, and manage their trading positions. -/// +/// /// ## Key Features: -/// +/// /// - **Market Data**: View orderbook depth and best prices /// - **Order Management**: Place, cancel, and monitor orders /// - **Price Discovery**: Find the best available prices for trades /// - **Order Types**: Support for both maker and taker orders -/// +/// /// ## Order Lifecycle: -/// +/// /// 1. **Creation**: Orders are placed using `setOrder` /// 2. **Matching**: Orders wait in the orderbook for matching /// 3. **Execution**: Matched orders proceed to atomic swap /// 4. **Completion**: Orders are removed after execution or cancellation -/// +/// /// ## Usage Example: -/// +/// /// ```dart /// final orderbook = client.orderbook; -/// +/// /// // View orderbook /// final book = await orderbook.orderbook( /// base: 'BTC', /// rel: 'KMD', /// ); -/// +/// /// // Place an order /// final order = await orderbook.setOrder( /// base: 'BTC', @@ -41,22 +41,22 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; /// ``` class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { /// Creates a new [OrderbookMethodsNamespace] instance. - /// + /// /// This is typically called internally by the [KomodoDefiRpcMethods] class. OrderbookMethodsNamespace(super.client); /// Retrieves the orderbook for a specific trading pair. - /// + /// /// This method fetches the current state of the orderbook, including /// all active buy and sell orders for the specified trading pair. - /// + /// /// - [base]: The base coin of the trading pair /// - [rel]: The rel/quote coin of the trading pair /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with an [OrderbookResponse] /// containing lists of bids and asks. - /// + /// /// The orderbook includes: /// - **Bids**: Buy orders sorted by price (highest first) /// - **Asks**: Sell orders sorted by price (lowest first) @@ -77,17 +77,17 @@ class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { } /// Retrieves orderbook depth for multiple trading pairs. - /// + /// /// This method efficiently fetches depth information for multiple /// trading pairs in a single request, useful for market overview /// displays or price aggregation. - /// + /// /// - [pairs]: List of trading pairs to query /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with an [OrderbookDepthResponse] /// containing depth data for each requested pair. - /// + /// /// Depth information includes: /// - Best bid and ask prices /// - Available volume at best prices @@ -105,19 +105,19 @@ class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { } /// Finds the best orders for a specific trading action. - /// + /// /// This method searches the orderbook to find the best available /// orders that can fulfill a desired trade volume. It's useful for /// determining the expected execution price for market orders. - /// + /// /// - [coin]: The coin to trade /// - [action]: Whether to buy or sell /// - [volume]: The desired trade volume /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [BestOrdersResponse] /// containing the best available orders. - /// + /// /// The response includes: /// - Orders sorted by best price /// - Cumulative volume information @@ -125,7 +125,8 @@ class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { Future bestOrders({ required String coin, required OrderType action, - required String volume, + required RequestBy requestBy, + bool? excludeMine, String? rpcPass, }) { return execute( @@ -133,34 +134,34 @@ class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { rpcPass: rpcPass ?? this.rpcPass ?? '', coin: coin, action: action, - volume: volume, + requestBy: requestBy, + excludeMine: excludeMine, ), ); } /// Places a new maker order on the orderbook. - /// + /// /// This method creates a new limit order that will be added to the /// orderbook and wait for a matching taker. The order remains active /// until it's matched, cancelled, or expires. - /// + /// /// - [base]: The base coin to trade /// - [rel]: The rel/quote coin to trade /// - [price]: The price per unit of base coin in rel coin /// - [volume]: The amount of base coin to trade - /// - [orderType]: Optional order type specification - /// - [minVolume]: Optional minimum acceptable volume for partial fills - /// - [baseConfs]: Optional required confirmations for base coin - /// - [baseNota]: Optional nota requirement for base coin - /// - [relConfs]: Optional required confirmations for rel coin - /// - [relNota]: Optional nota requirement for rel coin + /// - [minVolume]: Optional minimum acceptable volume for partial fills (string numeric) + /// - [baseConfs]: Optional required confirmations for base coin (int) + /// - [baseNota]: Optional NOTA requirement for base coin (bool) + /// - [relConfs]: Optional required confirmations for rel coin (int) + /// - [relNota]: Optional NOTA requirement for rel coin (bool) /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [SetOrderResponse] /// containing the created order details. - /// + /// /// ## Order Configuration: - /// + /// /// - **Price**: Must be positive and reasonable for the market /// - **Volume**: Must exceed minimum trading requirements /// - **Confirmations**: Higher values increase security but slow execution @@ -170,12 +171,11 @@ class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { required String rel, required String price, required String volume, - OrderType? orderType, - bool? minVolume, - String? baseConfs, - String? baseNota, - String? relConfs, - String? relNota, + String? minVolume, + int? baseConfs, + bool? baseNota, + int? relConfs, + bool? relNota, String? rpcPass, }) { return execute( @@ -185,7 +185,6 @@ class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { rel: rel, price: price, volume: volume, - orderType: orderType, minVolume: minVolume, baseConfs: baseConfs, baseNota: baseNota, @@ -196,16 +195,16 @@ class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { } /// Cancels a specific order. - /// + /// /// This method removes an active order from the orderbook. Only orders /// that haven't been matched can be cancelled. - /// + /// /// - [uuid]: The unique identifier of the order to cancel /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [CancelOrderResponse] /// indicating the cancellation result. - /// + /// /// Note: Orders that are already matched and proceeding to swap /// cannot be cancelled. Future cancelOrder({ @@ -213,29 +212,26 @@ class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { String? rpcPass, }) { return execute( - CancelOrderRequest( - rpcPass: rpcPass ?? this.rpcPass ?? '', - uuid: uuid, - ), + CancelOrderRequest(rpcPass: rpcPass ?? this.rpcPass ?? '', uuid: uuid), ); } /// Cancels multiple orders based on the specified criteria. - /// + /// /// This method provides bulk cancellation functionality, allowing users /// to cancel all their orders or all orders for a specific coin. - /// + /// /// - [cancelType]: Specifies which orders to cancel (all or by coin) /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [CancelAllOrdersResponse] /// containing the results of the cancellation operation. - /// + /// /// ## Cancel Types: - /// + /// /// - `CancelOrdersType.all()`: Cancels all active orders /// - `CancelOrdersType.coin("BTC")`: Cancels all orders involving BTC - /// + /// /// This is useful for: /// - Emergency stops /// - Portfolio rebalancing @@ -253,27 +249,21 @@ class OrderbookMethodsNamespace extends BaseRpcMethodNamespace { } /// Retrieves all orders created by the current user. - /// + /// /// This method returns a comprehensive list of the user's orders, /// including their current status and match information. - /// + /// /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [MyOrdersResponse] /// containing all user orders. - /// + /// /// The response includes: /// - Active orders waiting for matches /// - Orders currently being matched /// - Recently completed or cancelled orders /// - Detailed status and configuration for each order - Future myOrders({ - String? rpcPass, - }) { - return execute( - MyOrdersRequest( - rpcPass: rpcPass ?? this.rpcPass ?? '', - ), - ); + Future myOrders({String? rpcPass}) { + return execute(MyOrdersRequest(rpcPass: rpcPass ?? this.rpcPass ?? '')); } -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/set_order.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/set_order.dart index c89ac4f5..d04f463b 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/set_order.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/orderbook/set_order.dart @@ -10,49 +10,54 @@ class SetOrderRequest required this.rel, required this.price, required this.volume, - this.orderType, this.minVolume, this.baseConfs, this.baseNota, this.relConfs, this.relNota, - }) : super( - method: 'setprice', - rpcPass: rpcPass, - mmrpc: RpcVersion.v2_0, - ); + }) : super(method: 'setprice', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0); + /// Base coin ticker to trade final String base; + + /// Rel/quote coin ticker to trade final String rel; + + /// Price per unit of [base] in [rel] (string numeric) final String price; + + /// Amount of [base] to trade (string numeric) final String volume; - final OrderType? orderType; - final bool? minVolume; - final String? baseConfs; - final String? baseNota; - final String? relConfs; - final String? relNota; + + /// Minimum acceptable fill amount (string numeric) + final String? minVolume; + + /// Required confirmations for base coin + final int? baseConfs; + + /// Required confirmations for rel coin + final int? relConfs; + + /// Whether notarization is required for base coin + final bool? baseNota; + + /// Whether notarization is required for rel coin + final bool? relNota; @override - Map toJson() { - final params = { + Map toJson() => super.toJson().deepMerge({ + 'params': { 'base': base, 'rel': rel, 'price': price, 'volume': volume, - }; - - if (orderType != null) params['order_type'] = orderType!.toJson(); - if (minVolume != null) params['min_volume'] = minVolume.toString(); - if (baseConfs != null) params['base_confs'] = baseConfs; - if (baseNota != null) params['base_nota'] = baseNota; - if (relConfs != null) params['rel_confs'] = relConfs; - if (relNota != null) params['rel_nota'] = relNota; - - return super.toJson().deepMerge({ - 'params': params, - }); - } + if (minVolume != null) 'min_volume': minVolume, + if (baseConfs != null) 'base_confs': baseConfs, + if (baseNota != null) 'base_nota': baseNota, + if (relConfs != null) 'rel_confs': relConfs, + if (relNota != null) 'rel_nota': relNota, + }, + }); @override SetOrderResponse parse(Map json) => @@ -61,10 +66,7 @@ class SetOrderRequest /// Response from creating an order class SetOrderResponse extends BaseResponse { - SetOrderResponse({ - required super.mmrpc, - required this.orderInfo, - }); + SetOrderResponse({required super.mmrpc, required this.orderInfo}); factory SetOrderResponse.parse(JsonMap json) { final result = json.value('result'); @@ -75,6 +77,7 @@ class SetOrderResponse extends BaseResponse { ); } + /// Information about the created order final MyOrderInfo orderInfo; @override @@ -82,4 +85,4 @@ class SetOrderResponse extends BaseResponse { 'mmrpc': mmrpc, 'result': orderInfo.toJson(), }; -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/rpc_methods.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/rpc_methods.dart index 9abad511..9319dd79 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/rpc_methods.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/rpc_methods.dart @@ -32,14 +32,26 @@ export 'hd_wallet/hd_wallet_methods.dart'; export 'hd_wallet/scan_for_new_addresses_init.dart'; export 'hd_wallet/scan_for_new_addresses_status.dart'; export 'hd_wallet/task_description_parser.dart'; +export 'lightning/close_channel.dart'; export 'lightning/enable_lightning.dart'; +export 'lightning/generate_invoice.dart'; +export 'lightning/get_channel_details.dart'; export 'lightning/get_channels.dart'; +export 'lightning/get_claimable_balances.dart'; +export 'lightning/get_payment_history.dart'; export 'lightning/lightning_rpc_namespace.dart'; +export 'lightning/list_closed_channels_by_filter.dart'; export 'lightning/open_channel.dart'; +export 'lightning/pay_invoice.dart'; export 'methods.dart'; export 'nft/enable_nft.dart'; export 'nft/nft_rpc_namespace.dart'; +export 'orderbook/best_orders.dart'; +export 'orderbook/cancel_all_orders.dart'; +export 'orderbook/cancel_order.dart'; +export 'orderbook/my_orders.dart'; export 'orderbook/orderbook.dart'; +export 'orderbook/orderbook_depth.dart'; export 'orderbook/orderbook_rpc_namespace.dart'; export 'orderbook/set_order.dart'; export 'qtum/enable_qtum.dart'; @@ -50,8 +62,13 @@ export 'tendermint/task_enable_tendermint_init.dart'; export 'tendermint/task_enable_tendermint_status.dart'; export 'tendermint/tendermind_rpc_namespace.dart'; export 'trading/active_swaps.dart'; +export 'trading/cancel_swap.dart'; +export 'trading/max_taker_volume.dart'; +export 'trading/min_trading_volume.dart'; +export 'trading/recent_swaps.dart'; export 'trading/start_swap.dart'; export 'trading/swap_status.dart'; +export 'trading/trade_preimage.dart'; export 'trading/trading_rpc_namespace.dart'; export 'transaction_history/my_tx_history.dart'; export 'transaction_history/transaction_history_namespace.dart'; diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/tendermint/enable_tendermint_with_assets.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/tendermint/enable_tendermint_with_assets.dart index 7f04e5ab..3490c162 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/tendermint/enable_tendermint_with_assets.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/tendermint/enable_tendermint_with_assets.dart @@ -65,9 +65,7 @@ class EnableTendermintWithAssetsResponse extends BaseResponse { ) : {}, tokensTickers: - !hasBalances - ? result.value>('tokens_tickers').cast() - : [], + !hasBalances ? result.value>('tokens_tickers') : [], ); } diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/tendermint/task_enable_tendermint_status.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/tendermint/task_enable_tendermint_status.dart index 3e0e7976..972ff732 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/tendermint/task_enable_tendermint_status.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/tendermint/task_enable_tendermint_status.dart @@ -168,9 +168,7 @@ class TendermintActivationResult { ) : {}, tokensTickers: - !hasBalances - ? json.value>('tokens_tickers').cast() - : [], + !hasBalances ? json.value>('tokens_tickers') : [], ); } diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/active_swaps.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/active_swaps.dart index 070d60ab..1acd3472 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/active_swaps.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/active_swaps.dart @@ -4,28 +4,22 @@ import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; /// Request to get active swaps class ActiveSwapsRequest extends BaseRequest { - ActiveSwapsRequest({ - required String rpcPass, - this.coin, - }) : super( - method: 'active_swaps', - rpcPass: rpcPass, - mmrpc: RpcVersion.v2_0, - ); + ActiveSwapsRequest({required String rpcPass, this.includeStatus, this.coin}) + : super(method: 'active_swaps', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0); + /// If true, include detailed status objects for each active swap + final bool? includeStatus; + + /// Optional coin filter to limit returned swaps final String? coin; @override - Map toJson() { - final params = {}; - if (coin != null) { - params['coin'] = coin; - } - - return super.toJson().deepMerge({ - 'params': params, - }); - } + Map toJson() => super.toJson().deepMerge({ + 'params': { + if (coin != null) 'coin': coin, + if (includeStatus != null) 'include_status': includeStatus, + }, + }); @override ActiveSwapsResponse parse(Map json) => @@ -37,32 +31,67 @@ class ActiveSwapsResponse extends BaseResponse { ActiveSwapsResponse({ required super.mmrpc, required this.uuids, - required this.swaps, + required this.statuses, }); factory ActiveSwapsResponse.parse(JsonMap json) { final result = json.value('result'); + final uuids = result.value>('uuids').map((e) => e).toList(); + + final statusesJson = result.valueOrNull('statuses'); + final statuses = {}; + if (statusesJson != null) { + for (final entry in statusesJson.entries) { + final key = entry.key; + final value = entry.value as JsonMap; + statuses[key] = ActiveSwapStatus.fromJson(value); + } + } + return ActiveSwapsResponse( mmrpc: json.value('mmrpc'), - uuids: (result.value>('uuids')) - .map((e) => e as String) - .toList(), - swaps: (result.value>('swaps')) - .map((e) => SwapInfo.fromJson(e as JsonMap)) - .toList(), + uuids: uuids, + statuses: statuses.isEmpty ? null : statuses, ); } + /// List of active swap UUIDs final List uuids; - final List swaps; + + /// Optional map of UUID -> status when [includeStatus] was requested + final Map? statuses; @override Map toJson() => { 'mmrpc': mmrpc, 'result': { 'uuids': uuids, - 'swaps': swaps.map((e) => e.toJson()).toList(), + if (statuses != null) + 'statuses': statuses!.map((k, v) => MapEntry(k, v.toJson())), }, }; -} \ No newline at end of file +} + +/// Active swap status entry as returned by active_swaps when include_status is true +class ActiveSwapStatus { + ActiveSwapStatus({required this.swapType, required this.swapData}); + + factory ActiveSwapStatus.fromJson(JsonMap json) { + return ActiveSwapStatus( + swapType: json.value('swap_type'), + swapData: SwapInfo.fromJson(json.value('swap_data')), + ); + } + + /// Swap type string (maker/taker) + final String swapType; + + /// Detailed swap information + final SwapInfo swapData; + + Map toJson() => { + 'swap_type': swapType, + 'swap_data': swapData.toJson(), + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/cancel_swap.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/cancel_swap.dart new file mode 100644 index 00000000..3e4fed6d --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/cancel_swap.dart @@ -0,0 +1,44 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to cancel a swap +class CancelSwapRequest + extends BaseRequest { + CancelSwapRequest({required String rpcPass, required this.uuid}) + : super(method: 'cancel_swap', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0); + + /// UUID of the swap to cancel + final String uuid; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': {'uuid': uuid}, + }); + + @override + CancelSwapResponse parse(Map json) => + CancelSwapResponse.parse(json); +} + +/// Response from cancelling a swap +class CancelSwapResponse extends BaseResponse { + CancelSwapResponse({required super.mmrpc, required this.cancelled}); + + factory CancelSwapResponse.parse(JsonMap json) { + final result = json.value('result'); + + return CancelSwapResponse( + mmrpc: json.value('mmrpc'), + cancelled: result.value('success'), + ); + } + + /// True if the swap was cancelled (request accepted by node) + final bool cancelled; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'success': cancelled}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/max_taker_volume.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/max_taker_volume.dart new file mode 100644 index 00000000..6d2ba141 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/max_taker_volume.dart @@ -0,0 +1,85 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; +import 'package:rational/rational.dart'; +import '../../common_structures/primitive/mm2_rational.dart'; +import '../../common_structures/primitive/fraction.dart'; + +/// Request to get the maximum taker volume for a coin/pair. +/// +/// Calculates how much of `coin` can be traded as a taker when trading against +/// the optional `trade_with` counter coin, taking balance, fees and dust limits +/// into account. +class MaxTakerVolumeRequest + extends BaseRequest { + MaxTakerVolumeRequest({ + required String rpcPass, + required this.coin, + this.tradeWith, + }) : super(method: 'max_taker_vol', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0); + + /// Coin ticker to compute max taker volume for + final String coin; + + /// Optional counter coin to trade against (`trade_with` in the API). + /// + /// This tells the API which other coin you intend to trade `coin` with, so + /// the maximum volume is computed for that specific pair. If omitted, it + /// defaults to the same value as `coin` (API default). + final String? tradeWith; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': {'coin': coin, if (tradeWith != null) 'trade_with': tradeWith}, + }); + + @override + MaxTakerVolumeResponse parse(Map json) => + MaxTakerVolumeResponse.parse(json); +} + +/// Response with maximum taker volume for the requested coin/pair. +class MaxTakerVolumeResponse extends BaseResponse { + MaxTakerVolumeResponse({ + required super.mmrpc, + required this.amount, + this.amountFraction, + this.amountRat, + }); + + factory MaxTakerVolumeResponse.parse(JsonMap json) { + final result = json.value('result'); + + return MaxTakerVolumeResponse( + mmrpc: json.value('mmrpc'), + amount: result.value('amount'), + amountFraction: + result.valueOrNull('amount_fraction') != null + ? Fraction.fromJson(result.value('amount_fraction')) + : null, + amountRat: + result.valueOrNull>('amount_rat') != null + ? rationalFromMm2(result.value>('amount_rat')) + : null, + ); + } + + /// Maximum tradable amount of `coin` as a string numeric, denominated in + /// `coin` units, computed for the (`coin`, `trade_with`) pair. + final String amount; + + /// Optional fractional representation of the amount + final Fraction? amountFraction; + + /// Optional rational representation of the amount + final Rational? amountRat; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': { + 'amount': amount, + if (amountFraction != null) 'amount_fraction': amountFraction!.toJson(), + if (amountRat != null) 'amount_rat': rationalToMm2(amountRat!), + }, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/min_trading_volume.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/min_trading_volume.dart new file mode 100644 index 00000000..9854251c --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/min_trading_volume.dart @@ -0,0 +1,74 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; +import 'package:rational/rational.dart'; +import '../../common_structures/primitive/mm2_rational.dart'; +import '../../common_structures/primitive/fraction.dart'; + +/// Request to get minimum trading volume for a coin +class MinTradingVolumeRequest + extends BaseRequest { + MinTradingVolumeRequest({required String rpcPass, required this.coin}) + : super( + method: 'min_trading_vol', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + /// Coin ticker to query minimum trading volume for + final String coin; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': {'coin': coin}, + }); + + @override + MinTradingVolumeResponse parse(Map json) => + MinTradingVolumeResponse.parse(json); +} + +/// Response with minimum trading volume +class MinTradingVolumeResponse extends BaseResponse { + MinTradingVolumeResponse({ + required super.mmrpc, + required this.amount, + this.amountFraction, + this.amountRat, + }); + + factory MinTradingVolumeResponse.parse(JsonMap json) { + final result = json.value('result'); + + return MinTradingVolumeResponse( + mmrpc: json.value('mmrpc'), + amount: result.value('amount'), + amountFraction: + result.valueOrNull('amount_fraction') != null + ? Fraction.fromJson(result.value('amount_fraction')) + : null, + amountRat: + result.valueOrNull>('amount_rat') != null + ? rationalFromMm2(result.value>('amount_rat')) + : null, + ); + } + + /// Minimum tradeable amount as a string numeric (coin units) + final String amount; + + /// Optional fractional representation of the amount + final Fraction? amountFraction; + + /// Optional rational representation of the amount + final Rational? amountRat; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': { + 'amount': amount, + if (amountFraction != null) 'amount_fraction': amountFraction!.toJson(), + if (amountRat != null) 'amount_rat': rationalToMm2(amountRat!), + }, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/recent_swaps.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/recent_swaps.dart new file mode 100644 index 00000000..ab0dd9a2 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/recent_swaps.dart @@ -0,0 +1,48 @@ +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; + +/// Request to get recent swaps (history) +class RecentSwapsRequest + extends BaseRequest { + RecentSwapsRequest({required String rpcPass, this.filter}) + : super( + method: 'my_recent_swaps', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + /// Optional typed filter + final RecentSwapsFilter? filter; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': {if (filter != null) 'filter': filter!.toJson()}, + }); + + @override + RecentSwapsResponse parse(Map json) => + RecentSwapsResponse.parse(json); +} + +/// Response containing recent swaps +class RecentSwapsResponse extends BaseResponse { + RecentSwapsResponse({required super.mmrpc, required this.swaps}); + + factory RecentSwapsResponse.parse(JsonMap json) { + final result = json.value('result'); + + return RecentSwapsResponse( + mmrpc: json.value('mmrpc'), + swaps: result.value('swaps').map(SwapInfo.fromJson).toList(), + ); + } + + /// List of recent swaps matching the filter/pagination + final List swaps; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': {'swaps': swaps.map((e) => e.toJson()).toList()}, + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/start_swap.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/start_swap.dart index c987fe15..bc1864a4 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/start_swap.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/start_swap.dart @@ -2,36 +2,28 @@ import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; /// Request to initiate a new atomic swap. -/// +/// /// This RPC method starts a new swap operation based on the provided /// swap parameters. The swap can be initiated as either a maker (set_price) /// or a taker (buy/sell) operation. class StartSwapRequest extends BaseRequest { /// Creates a new [StartSwapRequest]. - /// + /// /// - [rpcPass]: RPC password for authentication /// - [swapRequest]: The swap parameters defining the trade details - StartSwapRequest({ - required String rpcPass, - required this.swapRequest, - }) : super( - method: 'start_swap', - rpcPass: rpcPass, - mmrpc: RpcVersion.v2_0, - ); + StartSwapRequest({required String rpcPass, required this.swapRequest}) + : super(method: 'start_swap', rpcPass: rpcPass, mmrpc: RpcVersion.v2_0); /// The swap request parameters. - /// + /// /// Contains all the details needed to initiate the swap, including /// the coins involved, amounts, and swap method. final SwapRequest swapRequest; @override Map toJson() { - return super.toJson().deepMerge({ - 'params': swapRequest.toJson(), - }); + return super.toJson().deepMerge({'params': swapRequest.toJson()}); } @override @@ -40,13 +32,13 @@ class StartSwapRequest } /// Swap request parameters for initiating a new swap. -/// +/// /// This class encapsulates all the necessary information to start /// an atomic swap, including the trading pair, amounts, and optional /// parameters for advanced swap configurations. class SwapRequest { /// Creates a new [SwapRequest]. - /// + /// /// - [base]: The base coin ticker /// - [rel]: The rel/quote coin ticker /// - [baseCoinAmount]: Amount of base coin to trade @@ -62,46 +54,53 @@ class SwapRequest { required this.method, this.senderPubkey, this.destPubkey, + this.matchBy, }); /// The base coin ticker. - /// + /// /// This is the coin being bought or sold in the swap. final String base; - + /// The rel/quote coin ticker. - /// + /// /// This is the coin used as payment or received in the swap. final String rel; - + /// Amount of base coin involved in the swap. - /// + /// /// Expressed as a string to maintain precision. The exact interpretation /// depends on the swap method. final String baseCoinAmount; - + /// Amount of rel coin involved in the swap. - /// + /// /// Expressed as a string to maintain precision. The exact interpretation /// depends on the swap method. final String relCoinAmount; - + /// The method used to initiate the swap. - /// + /// /// Determines whether this is a maker order (setPrice) or a taker /// order (buy/sell). final SwapMethod method; - + /// Optional sender public key. - /// + /// /// Used for P2P communication during the swap negotiation. final String? senderPubkey; - + /// Optional destination public key. - /// + /// /// Can be used to target a specific counterparty for the swap. final String? destPubkey; + /// Optional match-by constraint to limit counterparties or orders. + /// + /// When provided, the node will attempt to match only against the given + /// counterparties (pubkeys) or order UUIDs depending on the type. + final MatchBy? matchBy; + /// Converts this [SwapRequest] to a JSON map. Map toJson() => { 'base': base, @@ -111,15 +110,16 @@ class SwapRequest { 'method': method.toJson(), if (senderPubkey != null) 'sender_pubkey': senderPubkey, if (destPubkey != null) 'dest_pubkey': destPubkey, + if (matchBy != null) 'match_by': matchBy!.toJson(), }; } /// Response from starting a swap operation. -/// +/// /// Contains the initial status and metadata about the newly created swap. class StartSwapResponse extends BaseResponse { /// Creates a new [StartSwapResponse]. - /// + /// /// - [mmrpc]: The RPC version /// - [uuid]: Unique identifier for the swap /// - [status]: Current status of the swap @@ -144,28 +144,24 @@ class StartSwapResponse extends BaseResponse { } /// Unique identifier for this swap. - /// + /// /// This UUID should be used to track the swap status and perform /// any subsequent operations on the swap. final String uuid; - + /// Current status of the swap. - /// + /// /// Indicates the initial state of the swap after creation. final String status; - + /// The type of swap that was created. - /// + /// /// Typically "Maker" or "Taker" depending on the swap method used. final String swapType; @override Map toJson() => { 'mmrpc': mmrpc, - 'result': { - 'uuid': uuid, - 'status': status, - 'swap_type': swapType, - }, + 'result': {'uuid': uuid, 'status': status, 'swap_type': swapType}, }; -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/trade_preimage.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/trade_preimage.dart new file mode 100644 index 00000000..65341738 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/trade_preimage.dart @@ -0,0 +1,281 @@ +import 'package:komodo_defi_rpc_methods/src/common_structures/primitive/mm2_rational.dart'; +import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; +import 'package:rational/rational.dart'; + +/// Request to calculate trade preimage (fees, validation) +class TradePreimageRequest + extends BaseRequest { + TradePreimageRequest({ + required String rpcPass, + required this.base, + required this.rel, + required this.swapMethod, + this.volume, + this.max, + this.price, + }) : super( + method: 'trade_preimage', + rpcPass: rpcPass, + mmrpc: RpcVersion.v2_0, + ); + + /// Base coin ticker for the potential trade + final String base; + + /// Rel/quote coin ticker for the potential trade + final String rel; + + /// Desired swap method (setprice, buy, sell) + final SwapMethod swapMethod; + + /// Trade volume as a string numeric + final String? volume; + + /// If true, compute preimage for "max" taker volume + final bool? max; + + /// Optional price for maker trades + final String? price; + + @override + Map toJson() => super.toJson().deepMerge({ + 'params': { + 'base': base, + 'rel': rel, + 'swap_method': + swapMethod == SwapMethod.setPrice ? 'setprice' : swapMethod.name, + if (volume != null) 'volume': volume, + if (max != null) 'max': max, + if (price != null) 'price': price, + }, + }); + + @override + TradePreimageResponse parse(Map json) => + TradePreimageResponse.parse(json); +} + +/// Response containing trade preimage details +class TradePreimageResponse extends BaseResponse { + TradePreimageResponse({ + required super.mmrpc, + required this.totalFees, + this.baseCoinFee, + this.relCoinFee, + this.takerFee, + this.feeToSendTakerFee, + }); + + factory TradePreimageResponse.parse(JsonMap json) { + final result = json.value('result'); + + return TradePreimageResponse( + mmrpc: json.value('mmrpc'), + baseCoinFee: + result.containsKey('base_coin_fee') + ? PreimageCoinFee.fromJson(result.value('base_coin_fee')) + : null, + relCoinFee: + result.containsKey('rel_coin_fee') + ? PreimageCoinFee.fromJson(result.value('rel_coin_fee')) + : null, + takerFee: + result.containsKey('taker_fee') + ? PreimageCoinFee.fromJson(result.value('taker_fee')) + : null, + feeToSendTakerFee: + result.containsKey('fee_to_send_taker_fee') + ? PreimageCoinFee.fromJson( + result.value('fee_to_send_taker_fee'), + ) + : null, + totalFees: + (result.valueOrNull('total_fees') ?? []) + .map(PreimageTotalFee.fromJson) + .toList(), + ); + } + + /// Estimated fee for the base coin leg + final PreimageCoinFee? baseCoinFee; + + /// Estimated fee for the rel/quote coin leg + final PreimageCoinFee? relCoinFee; + + /// Estimated taker fee, if applicable + final PreimageCoinFee? takerFee; + + /// Fee required to send the taker fee, if applicable + final PreimageCoinFee? feeToSendTakerFee; + + /// Aggregated list of total fees across involved coins + final List totalFees; + + @override + Map toJson() => { + 'mmrpc': mmrpc, + 'result': { + if (baseCoinFee != null) 'base_coin_fee': baseCoinFee!.toJson(), + if (relCoinFee != null) 'rel_coin_fee': relCoinFee!.toJson(), + if (takerFee != null) 'taker_fee': takerFee!.toJson(), + if (feeToSendTakerFee != null) + 'fee_to_send_taker_fee': feeToSendTakerFee!.toJson(), + 'total_fees': totalFees.map((e) => e.toJson()).toList(), + }, + }; +} + +/// Signed big integer parts used by MM2 rational encoding +const _mm2LimbBase = 1 << 32; // 2^32 + +BigInt _bigIntFromMm2Json(List json) { + final sign = json[0] as int; + final limbs = (json[1] as List).cast(); + if (sign == 0) return BigInt.zero; + var value = BigInt.zero; + var multiplier = BigInt.one; + for (final limb in limbs) { + value += BigInt.from(limb) * multiplier; + multiplier *= BigInt.from(_mm2LimbBase); + } + return sign < 0 ? -value : value; +} + +List _bigIntToMm2Json(BigInt value) { + if (value == BigInt.zero) { + return [ + 0, + [0], + ]; + } + final sign = value.isNegative ? -1 : 1; + var x = value.abs(); + final limbs = []; + final base = BigInt.from(_mm2LimbBase); + while (x > BigInt.zero) { + final q = x ~/ base; + final r = x - q * base; + limbs.add(r.toInt()); + x = q; + } + if (limbs.isEmpty) limbs.add(0); + return [sign, limbs]; +} + +Rational _rationalFromMm2(List json) { + final numJson = (json[0] as List).cast(); + final denJson = (json[1] as List).cast(); + final num = _bigIntFromMm2Json(numJson); + final den = _bigIntFromMm2Json(denJson); + if (den == BigInt.zero) { + throw const FormatException('Denominator cannot be zero in MM2 rational'); + } + return Rational(num, den); +} + +List _rationalToMm2(Rational r) { + return [_bigIntToMm2Json(r.numerator), _bigIntToMm2Json(r.denominator)]; +} + +class PreimageCoinFee { + PreimageCoinFee({ + required this.coin, + required this.amount, + required this.amountFraction, + required this.amountRat, + required this.paidFromTradingVol, + }); + + factory PreimageCoinFee.fromJson(JsonMap json) { + return PreimageCoinFee( + coin: json.value('coin'), + amount: json.value('amount'), + amountFraction: Fraction.fromJson(json.value('amount_fraction')), + amountRat: rationalFromMm2(json.value>('amount_rat')), + paidFromTradingVol: json.value('paid_from_trading_vol'), + ); + } + + /// Coin ticker for which the fee applies + final String coin; + + /// Fee amount as a string numeric + final String amount; + + /// Fractional representation of the fee + final Fraction amountFraction; + + /// Rational form of the amount (as returned by API) + final Rational amountRat; + + /// True if the fee is deducted from the trading volume + final bool paidFromTradingVol; + + Map toJson() => { + 'coin': coin, + 'amount': amount, + 'amount_fraction': amountFraction.toJson(), + 'amount_rat': rationalToMm2(amountRat), + 'paid_from_trading_vol': paidFromTradingVol, + }; +} + +class PreimageTotalFee { + PreimageTotalFee({ + required this.coin, + required this.amount, + required this.amountFraction, + required this.amountRat, + required this.requiredBalance, + required this.requiredBalanceFraction, + required this.requiredBalanceRat, + }); + + factory PreimageTotalFee.fromJson(JsonMap json) { + return PreimageTotalFee( + coin: json.value('coin'), + amount: json.value('amount'), + amountFraction: Fraction.fromJson(json.value('amount_fraction')), + amountRat: rationalFromMm2(json.value>('amount_rat')), + requiredBalance: json.value('required_balance'), + requiredBalanceFraction: Fraction.fromJson( + json.value('required_balance_fraction'), + ), + requiredBalanceRat: rationalFromMm2( + json.value>('required_balance_rat'), + ), + ); + } + + /// Coin ticker for which the total fee summary applies + final String coin; + + /// Total fee amount as a string numeric + final String amount; + + /// Fractional representation of the amount + final Fraction amountFraction; + + /// Rational representation of the amount (API-specific) + final Rational amountRat; + + /// Required balance to perform the trade + final String requiredBalance; + + /// Fractional representation of the required balance + final Fraction requiredBalanceFraction; + + /// Rational representation of the required balance + final Rational requiredBalanceRat; + + Map toJson() => { + 'coin': coin, + 'amount': amount, + 'amount_fraction': amountFraction.toJson(), + 'amount_rat': rationalToMm2(amountRat), + 'required_balance': requiredBalance, + 'required_balance_fraction': requiredBalanceFraction.toJson(), + 'required_balance_rat': rationalToMm2(requiredBalanceRat), + }; +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/trading_rpc_namespace.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/trading_rpc_namespace.dart index 9745171e..3f4f019a 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/trading_rpc_namespace.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/trading/trading_rpc_namespace.dart @@ -1,28 +1,28 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; /// RPC namespace for trading and swap operations. -/// +/// /// This namespace provides methods for managing atomic swaps and trading /// operations within the Komodo DeFi Framework. It enables users to initiate, /// monitor, and manage cross-chain atomic swaps in a decentralized manner. -/// +/// /// ## Key Features: -/// +/// /// - **Swap Initiation**: Start new swaps as maker or taker /// - **Swap Monitoring**: Track active and recent swap status /// - **Trade Analysis**: Calculate fees and validate trade parameters /// - **Swap Management**: Cancel active swaps when needed -/// +/// /// ## Swap Types: -/// +/// /// - **Maker**: Sets an order at a specific price and waits for takers /// - **Taker**: Takes existing orders from the orderbook immediately -/// +/// /// ## Usage Example: -/// +/// /// ```dart /// final trading = client.trading; -/// +/// /// // Start a new swap /// final swap = await trading.startSwap( /// swapRequest: SwapRequest( @@ -33,34 +33,34 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; /// method: SwapMethod.sell, /// ), /// ); -/// +/// /// // Monitor swap status /// final status = await trading.swapStatus(uuid: swap.uuid); /// ``` class TradingMethodsNamespace extends BaseRpcMethodNamespace { /// Creates a new [TradingMethodsNamespace] instance. - /// + /// /// This is typically called internally by the [KomodoDefiRpcMethods] class. TradingMethodsNamespace(super.client); /// Initiates a new atomic swap. - /// + /// /// This method starts a new cross-chain atomic swap based on the provided /// parameters. The swap can be initiated as either a maker (placing an order) /// or a taker (taking an existing order). - /// + /// /// - [swapRequest]: The swap configuration including coins, amounts, and method /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [StartSwapResponse] containing /// the swap UUID and initial status. - /// + /// /// ## Swap Methods: - /// + /// /// - **setPrice**: Creates a maker order at a specific price /// - **buy**: Takes the best available sell orders (taker) /// - **sell**: Takes the best available buy orders (taker) - /// + /// /// Throws an exception if: /// - Insufficient balance for the swap /// - No matching orders available (for taker swaps) @@ -78,16 +78,16 @@ class TradingMethodsNamespace extends BaseRpcMethodNamespace { } /// Retrieves the status of a specific swap. - /// + /// /// This method fetches detailed information about a swap identified by /// its UUID, including current state, progress, and transaction details. - /// + /// /// - [uuid]: The unique identifier of the swap /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [SwapStatusResponse] containing /// comprehensive swap information. - /// + /// /// The status includes: /// - Current swap state and progress /// - Transaction IDs and confirmations @@ -98,24 +98,21 @@ class TradingMethodsNamespace extends BaseRpcMethodNamespace { String? rpcPass, }) { return execute( - SwapStatusRequest( - rpcPass: rpcPass ?? this.rpcPass ?? '', - uuid: uuid, - ), + SwapStatusRequest(rpcPass: rpcPass ?? this.rpcPass ?? '', uuid: uuid), ); } /// Retrieves all currently active swaps. - /// + /// /// This method returns information about all swaps that are currently /// in progress. Optionally, results can be filtered by coin. - /// + /// /// - [coin]: Optional coin ticker to filter results /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with an [ActiveSwapsResponse] /// containing lists of active swap UUIDs and their details. - /// + /// /// Active swaps include those that are: /// - Waiting for maker payment /// - Waiting for taker payment @@ -123,61 +120,73 @@ class TradingMethodsNamespace extends BaseRpcMethodNamespace { /// - In any other non-terminal state Future activeSwaps({ String? coin, + bool? includeStatus, String? rpcPass, }) { return execute( ActiveSwapsRequest( rpcPass: rpcPass ?? this.rpcPass ?? '', coin: coin, + includeStatus: includeStatus, ), ); } /// Retrieves recent swap history with pagination support. - /// + /// /// This method fetches historical swap data, including both completed /// and failed swaps. Results can be paginated and filtered by coin. - /// + /// /// - [limit]: Maximum number of swaps to return /// - [fromUuid]: Starting point for pagination (exclusive) /// - [coin]: Optional coin ticker to filter results /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [RecentSwapsResponse] /// containing swap history records. - /// + /// /// ## Pagination: - /// + /// /// To paginate through results, use the UUID of the last swap from /// the previous response as the [fromUuid] parameter. Future recentSwaps({ int? limit, - int? fromUuid, + int? pageNumber, + String? fromUuid, String? coin, + String? otherCoin, + int? fromTimestamp, + int? toTimestamp, String? rpcPass, }) { return execute( RecentSwapsRequest( rpcPass: rpcPass ?? this.rpcPass ?? '', - limit: limit, - fromUuid: fromUuid, - coin: coin, + filter: RecentSwapsFilter( + limit: limit, + pageNumber: pageNumber, + fromUuid: fromUuid, + myCoin: coin, + otherCoin: otherCoin, + fromTimestamp: fromTimestamp, + toTimestamp: toTimestamp, + ), ), ); } /// Cancels an active swap. - /// + /// /// This method attempts to cancel a swap that is currently in progress. /// Cancellation is only possible for swaps in certain states, typically /// before the payment transactions have been broadcast. - /// + /// /// - [uuid]: The unique identifier of the swap to cancel /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [CancelSwapResponse] /// indicating whether the cancellation was successful. - /// + /// /// Note: Swaps cannot be cancelled after payment transactions have /// been broadcast to prevent loss of funds. Future cancelSwap({ @@ -185,29 +194,26 @@ class TradingMethodsNamespace extends BaseRpcMethodNamespace { String? rpcPass, }) { return execute( - CancelSwapRequest( - rpcPass: rpcPass ?? this.rpcPass ?? '', - uuid: uuid, - ), + CancelSwapRequest(rpcPass: rpcPass ?? this.rpcPass ?? '', uuid: uuid), ); } /// Calculates fees and validates parameters for a potential trade. - /// + /// /// This method performs a dry-run calculation of a trade, providing /// fee estimates and validation without actually initiating the swap. /// It's useful for showing users the expected costs before confirmation. - /// + /// /// - [base]: The base coin ticker /// - [rel]: The rel/quote coin ticker /// - [swapMethod]: The intended swap method (setPrice, buy, or sell) /// - [volume]: The trade volume /// - [price]: Optional price for maker orders /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [TradePreimageResponse] /// containing fee calculations and validation results. - /// + /// /// The preimage includes: /// - Estimated transaction fees for both coins /// - Actual tradeable volume after fees @@ -217,7 +223,8 @@ class TradingMethodsNamespace extends BaseRpcMethodNamespace { required String base, required String rel, required SwapMethod swapMethod, - required String volume, + String? volume, + bool? max, String? price, String? rpcPass, }) { @@ -228,50 +235,58 @@ class TradingMethodsNamespace extends BaseRpcMethodNamespace { rel: rel, swapMethod: swapMethod, volume: volume, + max: max, price: price, ), ); } /// Calculates the maximum volume available for a taker swap. - /// - /// This method determines the maximum amount of a coin that can be - /// traded as a taker, considering available balance and required fees. - /// + /// + /// Determines the maximum amount of `coin` that can be traded as a taker for + /// the pair (`coin`, `tradeWith`), after accounting for balances and all + /// applicable fees. + /// /// - [coin]: The coin ticker to check + /// - [tradeWith]: Optional counter coin to trade against. Affects + /// pair-dependent DEX fee calculation (e.g., some pairs like KMD have + /// discounted fees) and defaults to `coin` when omitted. /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [MaxTakerVolumeResponse] - /// containing the maximum tradeable volume. - /// + /// containing the maximum tradable volume. + /// /// The calculation considers: /// - Available coin balance /// - Required transaction fees + /// - DEX fees which depend on the coin pair (`coin` vs `tradeWith`) /// - Dust limits /// - Protocol-specific constraints Future maxTakerVolume({ required String coin, + String? tradeWith, String? rpcPass, }) { return execute( MaxTakerVolumeRequest( rpcPass: rpcPass ?? this.rpcPass ?? '', coin: coin, + tradeWith: tradeWith, ), ); } /// Retrieves the minimum trading volume for a coin. - /// + /// /// This method returns the minimum amount of a coin that can be /// traded in a swap, considering dust limits and economic viability. - /// + /// /// - [coin]: The coin ticker to check /// - [rpcPass]: Optional RPC password override - /// + /// /// Returns a [Future] that completes with a [MinTradingVolumeResponse] /// containing the minimum tradeable amount. - /// + /// /// The minimum is determined by: /// - Protocol dust limits /// - Transaction fee requirements @@ -287,4 +302,4 @@ class TradingMethodsNamespace extends BaseRpcMethodNamespace { ), ); } -} \ No newline at end of file +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/transaction_history/my_tx_history.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/transaction_history/my_tx_history.dart index cd633497..58f9fdde 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/transaction_history/my_tx_history.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/transaction_history/my_tx_history.dart @@ -106,8 +106,8 @@ class MyTxHistoryResponse extends BaseResponse { pageNumber: result.valueOrNull('page_number'), transactions: result - .value>('transactions') - .map((e) => TransactionInfo.fromJson(e as JsonMap)) + .value('transactions') + .map(TransactionInfo.fromJson) .toList(), ); } diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/get_private_keys.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/get_private_keys.dart index 8b8706c2..32f61a12 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/get_private_keys.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/get_private_keys.dart @@ -100,11 +100,8 @@ class HdCoinKeyInfo { const HdCoinKeyInfo({required this.coin, required this.addresses}); factory HdCoinKeyInfo.fromJson(JsonMap json) { - final addressesJson = json.value>('addresses'); - final addresses = - addressesJson - .map((addr) => HdAddressInfo.fromJson(addr as JsonMap)) - .toList(); + final addressesJson = json.value('addresses'); + final addresses = addressesJson.map(HdAddressInfo.fromJson).toList(); return HdCoinKeyInfo( coin: json.value('coin'), diff --git a/packages/komodo_defi_rpc_methods/pubspec.yaml b/packages/komodo_defi_rpc_methods/pubspec.yaml index 7d8a61b8..e06c3abe 100644 --- a/packages/komodo_defi_rpc_methods/pubspec.yaml +++ b/packages/komodo_defi_rpc_methods/pubspec.yaml @@ -10,6 +10,7 @@ environment: dependencies: collection: ^1.18.0 decimal: ^3.2.1 + rational: ^2.2.3 equatable: ^2.0.7 freezed_annotation: ^3.0.0 json_annotation: ^4.9.0 diff --git a/packages/komodo_defi_rpc_methods/test/trade_preimage_rational_test.dart b/packages/komodo_defi_rpc_methods/test/trade_preimage_rational_test.dart new file mode 100644 index 00000000..360259c0 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/test/trade_preimage_rational_test.dart @@ -0,0 +1,78 @@ +import 'package:komodo_defi_rpc_methods/src/rpc_methods/trading/trade_preimage.dart'; +import 'package:test/test.dart'; + +void main() { + group('MM2 rational encoding', () { + test('PreimageCoinFee amount_rat round-trip preserves limbs', () { + final srcAmountRat = [ + [ + 1, + [1792496569, 37583], + ], + [ + 1, + [2808348672, 232830643], + ], + ]; + + final fee = PreimageCoinFee.fromJson({ + 'coin': 'KMD', + 'amount': '1.234', + 'amount_fraction': {'numer': '1234', 'denom': '1000'}, + 'amount_rat': srcAmountRat, + 'paid_from_trading_vol': false, + }); + + final out = fee.toJson(); + expect(out['amount_rat'], equals(srcAmountRat)); + }); + + test('PreimageTotalFee amount_rat and required_balance_rat round-trip', () { + final amountRat = [ + [ + -1, + [5], + ], + [ + 1, + [2], + ], + ]; + final reqBalRat = [ + [ + 1, + [1, 0, 0], + ], + [ + 1, + [10], + ], + ]; + + final total = PreimageTotalFee.fromJson({ + 'coin': 'BTC', + 'amount': '0.1', + 'amount_fraction': {'numer': '1', 'denom': '10'}, + 'amount_rat': amountRat, + 'required_balance': '0.1', + 'required_balance_fraction': {'numer': '1', 'denom': '10'}, + 'required_balance_rat': reqBalRat, + }); + + final out = total.toJson(); + // amount_rat has no trailing zero limbs so exact match is expected + expect(out['amount_rat'], equals(amountRat)); + // required_balance_rat may be normalized (trailing zero limbs removed) + final reparsed = PreimageTotalFee.fromJson({ + 'coin': 'BTC', + 'amount': '0.1', + 'amount_fraction': {'numer': '1', 'denom': '10'}, + 'amount_rat': out['amount_rat'], + 'required_balance': '0.1', + 'required_balance_fraction': {'numer': '1', 'denom': '10'}, + 'required_balance_rat': out['required_balance_rat'], + }); + expect(reparsed.requiredBalanceRat, equals(total.requiredBalanceRat)); + }); + }); +} diff --git a/packages/komodo_defi_sdk/example/pubspec.lock b/packages/komodo_defi_sdk/example/pubspec.lock index 442ee240..072f4174 100644 --- a/packages/komodo_defi_sdk/example/pubspec.lock +++ b/packages/komodo_defi_sdk/example/pubspec.lock @@ -404,26 +404,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -777,10 +777,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" typed_data: dependency: transitive description: @@ -833,10 +833,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" very_good_analysis: dependency: "direct dev" description: diff --git a/packages/komodo_defi_sdk/example/pubspec_overrides.yaml b/packages/komodo_defi_sdk/example/pubspec_overrides.yaml index 996f2849..0a19f6f0 100644 --- a/packages/komodo_defi_sdk/example/pubspec_overrides.yaml +++ b/packages/komodo_defi_sdk/example/pubspec_overrides.yaml @@ -1,5 +1,7 @@ -# melos_managed_dependency_overrides: dragon_logs,komodo_cex_market_data,komodo_coin_updates,komodo_coins,komodo_defi_framework,komodo_defi_local_auth,komodo_defi_rpc_methods,komodo_defi_sdk,komodo_defi_types,komodo_ui,komodo_wallet_build_transformer +# melos_managed_dependency_overrides: dragon_logs,komodo_cex_market_data,komodo_coin_updates,komodo_coins,komodo_defi_framework,komodo_defi_local_auth,komodo_defi_rpc_methods,komodo_defi_sdk,komodo_defi_types,komodo_ui,komodo_wallet_build_transformer,dragon_charts_flutter dependency_overrides: + dragon_charts_flutter: + path: ../../dragon_charts_flutter dragon_logs: path: ../../dragon_logs komodo_cex_market_data: diff --git a/packages/komodo_defi_types/lib/komodo_defi_types.dart b/packages/komodo_defi_types/lib/komodo_defi_types.dart index 726d3760..1980e308 100644 --- a/packages/komodo_defi_types/lib/komodo_defi_types.dart +++ b/packages/komodo_defi_types/lib/komodo_defi_types.dart @@ -21,6 +21,8 @@ export 'src/komodo_defi_types_base.dart'; export 'src/public_key/balance_strategy.dart'; export 'src/seed_node/seed_node.dart'; export 'src/types.dart'; +// Trading and swap related high-level types used across SDKs +export 'src/trading/swap_types.dart'; // Export activation params types // export 'packages:komodo_defi_rpc_methods/lib/src/common_structures/activation/activation_params/activation_params_index.dart diff --git a/packages/komodo_defi_types/lib/src/trading/swap_types.dart b/packages/komodo_defi_types/lib/src/trading/swap_types.dart new file mode 100644 index 00000000..55c8d8ce --- /dev/null +++ b/packages/komodo_defi_types/lib/src/trading/swap_types.dart @@ -0,0 +1,414 @@ +import 'package:decimal/decimal.dart'; + +/// Defines the side of a trade/order from the perspective of the base asset. +/// +/// - [OrderSide.buy]: Acquire the base asset by paying with the rel/quote asset +/// - [OrderSide.sell]: Sell the base asset to receive the rel/quote asset +enum OrderSide { + /// Buy the base asset using the rel/quote asset + buy, + + /// Sell the base asset for the rel/quote asset + sell, +} + +/// High-level lifecycle status for an atomic swap. +/// +/// This enum represents user-facing swap progress states that aggregate +/// the detailed engine states into a concise status for UI and flow control. +enum SwapStatus { + /// Swap has started and is in progress + inProgress, + + /// Swap finished successfully + completed, + + /// Swap failed due to an error + failed, + + /// Swap was cancelled before completion + canceled, +} + +/// Summary information about a placed maker or taker order. +/// +/// This is a lightweight snapshot designed for UI list rendering and +/// basic order tracking. For full order details and engine-specific +/// metadata, query the RPC orderbook and my-orders endpoints. +class PlacedOrderSummary { + /// Creates a new [PlacedOrderSummary]. + PlacedOrderSummary({ + required this.uuid, + required this.base, + required this.rel, + required this.side, + required this.price, + required this.volume, + required this.timestamp, + required this.isMine, + this.priceString, + this.volumeString, + }); + + /// Unique identifier of the order created by the DEX. + final String uuid; + + /// Base asset ticker (e.g. "BTC"). + final String base; + + /// Rel/quote asset ticker (e.g. "KMD"). + final String rel; + + /// Whether this is a buy or sell order. + final OrderSide side; + + /// Price per unit of [base] in [rel]. + final Decimal price; + + /// Order volume in [base] units. + final Decimal volume; + + /// Creation timestamp (local clock). + final DateTime timestamp; + + /// True if the order belongs to the current wallet. + final bool isMine; + + /// Optional exact price string as returned/accepted by API + final String? priceString; + + /// Optional exact volume string as returned/accepted by API + final String? volumeString; +} + +/// One aggregated level/entry in an orderbook snapshot. +/// +/// Amounts are provided using Decimal to preserve full precision for UI and +/// calculations without floating point rounding errors. +class OrderbookEntry { + /// Creates a new [OrderbookEntry]. + OrderbookEntry({ + required this.price, + required this.baseAmount, + required this.relAmount, + this.uuid, + this.pubkey, + this.age, + this.priceString, + this.baseAmountString, + this.relAmountString, + }); + + /// Price for this order level (base in rel). + final Decimal price; + + /// Available amount denominated in base asset units. + final Decimal baseAmount; + + /// Available amount denominated in rel asset units. + final Decimal relAmount; + + /// Unique order identifier, if known. + final String? uuid; + + /// Maker's public node key, if available. + final String? pubkey; + + /// How long the order has been on the book. + final Duration? age; + + /// Optional exact price string as returned by API + final String? priceString; + + /// Optional exact base amount string as returned by API + final String? baseAmountString; + + /// Optional exact rel amount string as returned by API + final String? relAmountString; +} + +/// Immutable snapshot of an orderbook for a trading pair. +/// +/// The lists are already sorted as commonly expected by UIs: +/// - [asks]: ascending by price (best ask first) +/// - [bids]: descending by price (best bid first) +class OrderbookSnapshot { + /// Creates a new [OrderbookSnapshot]. + OrderbookSnapshot({ + required this.base, + required this.rel, + required this.asks, + required this.bids, + required this.timestamp, + }); + + /// Base asset ticker of the pair. + final String base; + + /// Rel/quote asset ticker of the pair. + final String rel; + + /// Sorted list of sell orders (lowest price first). + final List asks; + + /// Sorted list of buy orders (highest price first). + final List bids; + + /// Snapshot timestamp (local clock). + final DateTime timestamp; +} + +/// Progress update event for an active swap, suitable for streaming to UI. +/// +/// Provides a coarse [status] plus an optional human-readable [message] and +/// structured [details] for advanced consumers. +class SwapProgress { + /// Creates a new [SwapProgress] event. + SwapProgress({ + required this.status, + this.message, + this.swapUuid, + this.details, + }); + + /// Current high-level status in the swap lifecycle. + final SwapStatus status; + + /// Human-readable progress message. + final String? message; + + /// Swap identifier, if available. + final String? swapUuid; + + /// Additional structured details (implementation-specific). + final Map? details; +} + +/// Simple coin+amount pair using Decimal precision. +class CoinAmount { + CoinAmount({required this.coin, required this.amount, this.amountString}); + + /// Coin ticker, e.g. "BTC". + final String coin; + + /// Amount in coin units using Decimal precision. + final Decimal amount; + + /// Optional exact amount string as returned/accepted by API + final String? amountString; +} + +/// Total fee entry for a coin with required balance information. +class TotalFeeEntry { + TotalFeeEntry({ + required this.coin, + required this.amount, + required this.requiredBalance, + this.amountString, + this.requiredBalanceString, + }); + + /// Coin ticker, e.g. "KMD". + final String coin; + + /// Fee amount for this coin. + final Decimal amount; + + /// Total required balance to perform the trade. + final Decimal requiredBalance; + + /// Optional exact amount string as returned by API + final String? amountString; + + /// Optional exact required balance string as returned by API + final String? requiredBalanceString; +} + +/// High-level estimate produced by a trade preimage call. +/// +/// Presents fees as Decimal amounts and omits engine-specific rationals. +class TradePreimageQuote { + TradePreimageQuote({ + this.baseCoinFee, + this.relCoinFee, + this.takerFee, + this.feeToSendTakerFee, + required this.totalFees, + }); + + /// Estimated fee taken from the base coin, if applicable. + final CoinAmount? baseCoinFee; + + /// Estimated fee taken from the rel/quote coin, if applicable. + final CoinAmount? relCoinFee; + + /// Estimated taker fee, if applicable. + final CoinAmount? takerFee; + + /// Fee required to send the taker fee, if applicable. + final CoinAmount? feeToSendTakerFee; + + /// Aggregated total fees and required balances per coin. + final List totalFees; +} + +/// Concise summary of a swap suitable for listings and history views. +class SwapSummary { + SwapSummary({ + required this.uuid, + required this.makerCoin, + required this.takerCoin, + required this.makerAmount, + required this.takerAmount, + required this.isMaker, + required this.successEvents, + required this.errorEvents, + this.startedAt, + this.finishedAt, + this.makerAmountString, + this.takerAmountString, + }); + + /// Swap UUID. + final String uuid; + + /// Maker side coin ticker. + final String makerCoin; + + /// Taker side coin ticker. + final String takerCoin; + + /// Amount sent by maker. + final Decimal makerAmount; + + /// Amount sent by taker. + final Decimal takerAmount; + + /// Whether current wallet participated as maker. + final bool isMaker; + + /// Successful lifecycle events. + final List successEvents; + + /// Error lifecycle events. + final List errorEvents; + + /// Start time, if known. + final DateTime? startedAt; + + /// Finish time, if known. + final DateTime? finishedAt; + + /// Optional exact maker amount string + final String? makerAmountString; + + /// Optional exact taker amount string + final String? takerAmountString; + + /// True if swap reached a terminal state. + bool get isComplete => finishedAt != null; + + /// True if swap completed without errors. + bool get isSuccessful => isComplete && errorEvents.isEmpty; +} + +/// Minimal representation of a best order from the DEX for a coin/action. +class BestOrder { + BestOrder({ + required this.uuid, + required this.price, + required this.maxVolume, + required this.coin, + this.pubkey, + this.age, + this.address, + this.priceString, + this.maxVolumeString, + }); + + final String uuid; + final Decimal price; + final Decimal maxVolume; + final String coin; + final String? pubkey; + final Duration? age; + final String? address; + + /// Optional exact price string as returned by API + final String? priceString; + + /// Optional exact max volume string as returned by API + final String? maxVolumeString; +} + +/// Result of aggregating best orders for a taker volume query. +class BestOrdersResult { + BestOrdersResult({required this.orders}); + + final List orders; +} + +/// One level fill of a taker orderbook sweep on a specific pair. +class LevelFill { + LevelFill({required this.price, required this.base, required this.rel, this.priceString, this.baseString, this.relString}); + + /// Price (base in rel) at this level. + final Decimal price; + + /// Base amount filled at this level. + final Decimal base; + + /// Rel amount corresponding to [base] at [price]. + final Decimal rel; + + /// Optional exact price string + final String? priceString; + + /// Optional exact base amount string + final String? baseString; + + /// Optional exact rel amount string + final String? relString; +} + +/// Estimated taker fill on a pair, including average price and slippage breakdown. +class TakerFillEstimate { + TakerFillEstimate({ + required this.totalBase, + required this.totalRel, + required this.averagePrice, + required this.fills, + this.totalBaseString, + this.totalRelString, + this.averagePriceString, + }); + + /// Total base amount to be filled. + final Decimal totalBase; + + /// Total rel amount spent/received. + final Decimal totalRel; + + /// Volume-weighted average price for the fill. + final Decimal averagePrice; + + /// Per-level fills that compose the total. + final List fills; + + /// Optional exact total base string + final String? totalBaseString; + + /// Optional exact total rel string + final String? totalRelString; + + /// Optional exact average price string + final String? averagePriceString; +} + +/// High-level taker quote that combines orderbook sweep with a fee preimage. +class TakerQuote { + TakerQuote({required this.fill, required this.preimage}); + + final TakerFillEstimate fill; + final TradePreimageQuote preimage; +} diff --git a/packages/komodo_defi_types/lib/src/types.dart b/packages/komodo_defi_types/lib/src/types.dart index 5d469920..9e8e377f 100644 --- a/packages/komodo_defi_types/lib/src/types.dart +++ b/packages/komodo_defi_types/lib/src/types.dart @@ -9,6 +9,7 @@ export 'addresses/address_conversion_result.dart'; export 'addresses/address_validation.dart'; export 'api/api_client.dart'; export 'assets/asset.dart'; +export 'assets/asset_cache_key.dart'; export 'assets/asset_id.dart'; export 'assets/asset_symbol.dart'; export 'auth/auth_options.dart'; @@ -51,6 +52,7 @@ export 'public_key/pubkey_strategy.dart'; export 'public_key/token_balance_map.dart'; export 'public_key/wallet_balance.dart'; export 'seed_node/seed_node.dart'; +export 'trading/swap_types.dart'; export 'transactions/asset_transaction_history_id.dart'; export 'transactions/balance_changes.dart'; export 'transactions/fee_info.dart'; diff --git a/playground/pubspec.lock b/playground/pubspec.lock index fe1e8ba0..cdcc40ab 100644 --- a/playground/pubspec.lock +++ b/playground/pubspec.lock @@ -414,26 +414,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -651,10 +651,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" typed_data: dependency: transitive description: @@ -731,10 +731,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" very_good_analysis: dependency: transitive description: diff --git a/products/dex_dungeon/pubspec.lock b/products/dex_dungeon/pubspec.lock index 1ba798c3..c17c372d 100644 --- a/products/dex_dungeon/pubspec.lock +++ b/products/dex_dungeon/pubspec.lock @@ -551,26 +551,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" local_auth: dependency: transitive description: @@ -996,26 +996,26 @@ packages: dependency: transitive description: name: test - sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" + sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" url: "https://pub.dev" source: hosted - version: "1.25.15" + version: "1.26.2" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" test_core: dependency: transitive description: name: test_core - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" url: "https://pub.dev" source: hosted - version: "0.6.8" + version: "0.6.11" typed_data: dependency: transitive description: @@ -1036,10 +1036,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" very_good_analysis: dependency: "direct dev" description: diff --git a/products/komodo_compliance_console/pubspec.lock b/products/komodo_compliance_console/pubspec.lock index f4c129f9..acecd57a 100644 --- a/products/komodo_compliance_console/pubspec.lock +++ b/products/komodo_compliance_console/pubspec.lock @@ -236,26 +236,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" logging: dependency: transitive description: @@ -465,26 +465,26 @@ packages: dependency: transitive description: name: test - sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" + sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" url: "https://pub.dev" source: hosted - version: "1.25.15" + version: "1.26.2" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" test_core: dependency: transitive description: name: test_core - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" url: "https://pub.dev" source: hosted - version: "0.6.8" + version: "0.6.11" typed_data: dependency: transitive description: @@ -497,10 +497,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" very_good_analysis: dependency: "direct dev" description: @@ -566,5 +566,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54"