Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/komodo_defi_types/lib/komodo_defi_type_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/// Utilities for types used throughout the Komodo DeFi Framework ecosystem.
library komodo_defi_type_utils;

export 'src/utils/backoff_strategy.dart';
export 'src/utils/iterable_type_utils.dart';
export 'src/utils/json_type_utils.dart';
export 'src/utils/live_data.dart';
Expand Down
92 changes: 76 additions & 16 deletions packages/komodo_defi_types/lib/src/transactions/fee_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
part 'fee_info.freezed.dart';
// We are doing manual fromJson/toJson, so no need for part 'fee_info.g.dart';

/// A union representing five possible fee types:
/// A union representing six possible fee types:
/// - UtxoFixed
/// - UtxoPerKbyte
/// - EthGas
/// - Qrc20Gas
/// - CosmosGas
/// - Tendermint
@Freezed()
sealed class FeeInfo with _$FeeInfo {
//////////////////////////////////////////////////////////////////////////////
Expand All @@ -35,26 +36,30 @@ sealed class FeeInfo with _$FeeInfo {
amount: Decimal.parse(json['amount'] as String),
);
case 'EthGas' || 'Eth':
final totalGasFee = json['total_fee'] != null
? Decimal.parse(json['total_fee'].toString())
: null;
return FeeInfo.ethGas(
coin: json['coin'] as String? ?? '',
// If JSON provides e.g. "0.000000003", parse to Decimal => 3e-9
gasPrice: Decimal.parse(json['gas_price'].toString()),
gas: json['gas'] as int,
totalGasFee: totalGasFee,
);
case 'Qrc20Gas':
final totalGasFee = json['total_gas_fee'] != null
? Decimal.parse(json['total_gas_fee'].toString())
: null;
return FeeInfo.qrc20Gas(
coin: json['coin'] as String? ?? '',
gasPrice: Decimal.parse(json['gas_price'].toString()),
gasLimit: json['gas_limit'] as int,
totalGasFee: totalGasFee,
);
// Legacy withdraw API returns "Tendermint" instead of "CosmosGas",
// so add this case for compatibility and as a fallback.
case 'Tendermint':
return FeeInfo.cosmosGas(
return FeeInfo.tendermint(
coin: json['coin'] as String? ?? '',
// The doc sometimes shows 0.05 as a number (double),
// so we convert it to string, then parse:
gasPrice: Decimal.parse(json['amount'].toString()),
amount: Decimal.parse(json['amount'].toString()),
gasLimit: json['gas_limit'] as int,
);
case 'CosmosGas':
Expand Down Expand Up @@ -95,10 +100,12 @@ sealed class FeeInfo with _$FeeInfo {
/// "type": "EthGas",
/// "coin": "ETH",
/// "gas_price": "0.000000003",
/// "gas": 21000
/// "gas": 21000,
/// "total_fee": "0.000021"
/// }
/// ```
/// Interpreted as: 3 Gwei -> total fee = 0.000000003 ETH * 21000 = 0.000063 ETH.
/// If `totalGasFee` is provided, it will be used directly instead of calculating from gasPrice * gas.
const factory FeeInfo.ethGas({
required String coin,

Expand All @@ -107,6 +114,10 @@ sealed class FeeInfo with _$FeeInfo {

/// Gas limit (number of gas units)
required int gas,

/// Optional total fee override. If provided, this value will be used directly
/// instead of calculating from gasPrice * gas.
Decimal? totalGasFee,
}) = FeeInfoEthGas;

/// 4) Qtum/QRC20-like gas, specifying `gasPrice` (in coin units) and `gasLimit`.
Expand All @@ -118,6 +129,10 @@ sealed class FeeInfo with _$FeeInfo {

/// Gas limit
required int gasLimit,

/// Optional total gas fee in coin units. If not provided, it will be calculated
/// as `gasPrice * gasLimit`.
Decimal? totalGasFee,
}) = FeeInfoQrc20Gas;

/// 5) Cosmos-like gas, specifying `gasPrice` (in coin units) and `gasLimit`.
Expand All @@ -141,16 +156,39 @@ sealed class FeeInfo with _$FeeInfo {
required int gasLimit,
}) = FeeInfoCosmosGas;

/// 6) Tendermint fee, with fixed `amount` and `gasLimit`.
///
/// Example JSON:
/// ```json
/// {
/// "type": "Tendermint",
/// "coin": "IRIS",
/// "amount": "0.038553",
/// "gas_limit": 100000
/// }
/// ```
/// Total fee is just the amount (not calculated from gas * price)
const factory FeeInfo.tendermint({
required String coin,

/// The fee amount in coin units
required Decimal amount,

/// Gas limit
required int gasLimit,
}) = FeeInfoTendermint;

/// A convenience getter returning the *total fee* in the coin's main units.
Decimal get totalFee => switch (this) {
FeeInfoUtxoFixed(:final amount) => amount,
FeeInfoUtxoPerKbyte(:final amount) => amount,
FeeInfoEthGas(:final gasPrice, :final gas) =>
gasPrice * Decimal.fromInt(gas),
FeeInfoQrc20Gas(:final gasPrice, :final gasLimit) =>
gasPrice * Decimal.fromInt(gasLimit),
FeeInfoEthGas(:final gasPrice, :final gas, :final totalGasFee) =>
totalGasFee ?? (gasPrice * Decimal.fromInt(gas)),
FeeInfoQrc20Gas(:final gasPrice, :final gasLimit, :final totalGasFee) =>
totalGasFee ?? (gasPrice * Decimal.fromInt(gasLimit)),
FeeInfoCosmosGas(:final gasPrice, :final gasLimit) =>
gasPrice * Decimal.fromInt(gasLimit),
FeeInfoTendermint(:final amount) => amount,
};

/// Convert this [FeeInfo] to a JSON object matching the mmRPC 2.0 docs.
Expand All @@ -165,22 +203,42 @@ sealed class FeeInfo with _$FeeInfo {
'coin': coin,
'amount': amount.toString(),
},
FeeInfoEthGas(:final coin, :final gasPrice, :final gas) => {
FeeInfoEthGas(
:final coin,
:final gasPrice,
:final gas,
:final totalGasFee
) =>
{
'type': 'Eth',
'coin': coin,
'gas_price': gasPrice.toString(),
'gas': gas,
if (totalGasFee != null) 'total_fee': totalGasFee.toString(),
},
FeeInfoQrc20Gas(:final coin, :final gasPrice, :final gasLimit) => {
FeeInfoQrc20Gas(
:final coin,
:final gasPrice,
:final gasLimit,
:final totalGasFee
) =>
{
'type': 'Qrc20Gas',
'coin': coin,
'gas_price': gasPrice.toString(),
'gas_price': gasPrice.toDouble(),
Comment thread
takenagain marked this conversation as resolved.
'gas_limit': gasLimit,
if (totalGasFee != null) 'total_gas_fee': totalGasFee.toString(),
},
FeeInfoCosmosGas(:final coin, :final gasPrice, :final gasLimit) => {
'type': 'CosmosGas',
'coin': coin,
'gas_price': gasPrice.toString(),
'gas_price': gasPrice.toDouble(),
'gas_limit': gasLimit,
},
FeeInfoTendermint(:final coin, :final amount, :final gasLimit) => {
'type': 'Tendermint',
'coin': coin,
'amount': amount.toString(),
'gas_limit': gasLimit,
},
};
Expand All @@ -197,6 +255,7 @@ extension FeeInfoMaybeMap on FeeInfo {
TResult Function(FeeInfoEthGas value)? ethGas,
TResult Function(FeeInfoQrc20Gas value)? qrc20Gas,
TResult Function(FeeInfoCosmosGas value)? cosmosGas,
TResult Function(FeeInfoTendermint value)? tendermint,
}) =>
switch (this) {
final FeeInfoUtxoFixed fee when utxoFixed != null => utxoFixed(fee),
Expand All @@ -205,6 +264,7 @@ extension FeeInfoMaybeMap on FeeInfo {
final FeeInfoEthGas fee when ethGas != null => ethGas(fee),
final FeeInfoQrc20Gas fee when qrc20Gas != null => qrc20Gas(fee),
final FeeInfoCosmosGas fee when cosmosGas != null => cosmosGas(fee),
final FeeInfoTendermint fee when tendermint != null => tendermint(fee),
_ => orElse(),
};
}
Loading
Loading