LCOV - code coverage report
Current view: top level - domain/entities - amount.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 57.9 % 121 70
Test Date: 2025-06-07 01:20:49 Functions: - 0 0

            Line data    Source code
       1              : import 'dart:math';
       2              : import 'package:walletkit_dart/src/utils/int.dart';
       3              : 
       4              : class Amount {
       5              :   final BigInt value;
       6              :   final int decimals;
       7              : 
       8           16 :   const Amount({
       9              :     required this.value,
      10              :     required this.decimals,
      11              :   });
      12              : 
      13            0 :   Amount convertTo(int newDecimals) {
      14            0 :     return Amount(value: value, decimals: newDecimals);
      15              :   }
      16              : 
      17              :   /// Converts the given value to the smallest unit of the currency
      18            2 :   factory Amount.convert({
      19              :     required num value,
      20              :     required int decimals,
      21              :   }) {
      22            2 :     var value_s = value.toExactString;
      23              : 
      24            2 :     final parts = value_s.split('.');
      25              : 
      26            8 :     var dec = parts.length == 1 ? 0 : parts[1].length;
      27              : 
      28            2 :     if (dec > decimals) {
      29            0 :       value_s = value_s.substring(0, value_s.length - (dec - decimals));
      30              :       dec = decimals;
      31              :     }
      32              : 
      33            4 :     var value_int = BigInt.tryParse(value_s.replaceAll('.', ''));
      34              : 
      35              :     if (value_int == null) {
      36            0 :       throw Exception('Invalid value: $value');
      37              :     }
      38              : 
      39           10 :     value_int = value_int * BigInt.from(10).pow(decimals.toInt() - dec);
      40              : 
      41            2 :     return Amount(
      42              :       value: value_int,
      43              :       decimals: decimals,
      44              :     );
      45              :   }
      46              : 
      47              :   /// Converts the given value to a BigInt
      48              :   /// This is useful when the value is already in the smallest unit of the currency
      49            1 :   Amount.from({
      50              :     required int value,
      51              :     required this.decimals,
      52            1 :   }) : value = BigInt.from(value);
      53              : 
      54            3 :   static Amount get zero => Amount(value: BigInt.from(0), decimals: 0);
      55              : 
      56            0 :   static Amount get negativeOne => Amount(value: BigInt.from(-1), decimals: 0);
      57              : 
      58            4 :   double get displayDouble {
      59           20 :     return value / BigInt.from(10).pow(decimals);
      60              :   }
      61              : 
      62            0 :   String get displayValue {
      63            0 :     final firstBI = value ~/ BigInt.from(10).pow(decimals);
      64              : 
      65            0 :     final secondBI = value % BigInt.from(10).pow(decimals);
      66              : 
      67            0 :     final string = '$firstBI.${secondBI.toString().padLeft(decimals, '0')}';
      68              : 
      69            0 :     final lastNonZeroIndex = string.lastIndexOf(RegExp(r'[^0]'));
      70              : 
      71            0 :     if (lastNonZeroIndex == -1) return string;
      72              : 
      73            0 :     final formatted = string.substring(0, lastNonZeroIndex < 3 ? 3 : lastNonZeroIndex + 1);
      74              : 
      75            0 :     if (formatted.endsWith('.')) {
      76            0 :       return formatted.substring(0, formatted.length - 1);
      77              :     }
      78              : 
      79              :     return formatted;
      80              :   }
      81              : 
      82            0 :   Amount copyWith({
      83              :     BigInt? value,
      84              :     int? decimals,
      85              :   }) {
      86              :     if (value == null && decimals == null) return this;
      87              : 
      88            0 :     return Amount(
      89            0 :       value: value ?? this.value,
      90            0 :       decimals: decimals ?? this.decimals,
      91              :     );
      92              :   }
      93              : 
      94            0 :   bool isOther(Amount other) {
      95            0 :     return value == other.value && decimals == other.decimals;
      96              :   }
      97              : 
      98            0 :   @override
      99            0 :   int get hashCode => value.hashCode ^ decimals.hashCode;
     100              : 
     101            1 :   @override
     102              :   bool operator ==(Object other) {
     103              :     if (identical(this, other)) return true;
     104              : 
     105            4 :     return other is Amount && other.value == value && other.decimals == decimals;
     106              :   }
     107              : 
     108            2 :   @override
     109              :   String toString() {
     110            8 :     return 'Amount{value: $value, decimals: $decimals}: $displayDouble';
     111              :   }
     112              : 
     113            1 :   Map<String, dynamic> toJson() {
     114            1 :     return {
     115            2 :       'value': value.toString(),
     116            1 :       'decimals': decimals,
     117              :     };
     118              :   }
     119              : 
     120            1 :   factory Amount.fromJson(Map json) {
     121            1 :     return Amount(
     122            2 :       value: BigInt.parse(json['value']),
     123            1 :       decimals: json['decimals'],
     124              :     );
     125              :   }
     126              : 
     127            0 :   Amount convertToDecimals(int newDecimals) {
     128            0 :     if (newDecimals == decimals) return this;
     129              : 
     130            0 :     final newAmount = value * BigInt.from(10).pow(newDecimals - decimals);
     131              : 
     132            0 :     return Amount(
     133              :       value: newAmount,
     134              :       decimals: newDecimals,
     135              :     );
     136              :   }
     137              : 
     138              :   /// Operators
     139            1 :   Amount operator *(Amount other) {
     140            1 :     return Amount(
     141            3 :       value: value * other.value,
     142            3 :       decimals: decimals + other.decimals,
     143              :     );
     144              :   }
     145              : 
     146              :   // Plus operator
     147            2 :   Amount operator +(Amount other) {
     148              :     // Determine the maximum decimals between the two amounts
     149            6 :     int maxDecimals = max(decimals, other.decimals);
     150              : 
     151              :     // Scale both amounts to the same decimal level
     152           12 :     BigInt scaledThisValue = value * BigInt.from(pow(10, maxDecimals - decimals));
     153           12 :     BigInt scaledOtherValue = other.value * BigInt.from(pow(10, maxDecimals - other.decimals));
     154              : 
     155              :     // Perform the addition
     156            2 :     BigInt resultValue = scaledThisValue + scaledOtherValue;
     157              : 
     158            2 :     return Amount(
     159              :       value: resultValue,
     160              :       decimals: maxDecimals,
     161              :     );
     162              :   }
     163              : 
     164              :   // Minus operator
     165            1 :   Amount operator -(Amount other) {
     166              :     // Determine the maximum decimals between the two amounts
     167            3 :     int maxDecimals = max(decimals, other.decimals);
     168              : 
     169              :     // Scale both amounts to the same decimal level
     170            6 :     BigInt scaledThisValue = value * BigInt.from(pow(10, maxDecimals - decimals));
     171            6 :     BigInt scaledOtherValue = other.value * BigInt.from(pow(10, maxDecimals - other.decimals));
     172              : 
     173              :     // Perform the subtraction
     174            1 :     BigInt resultValue = scaledThisValue - scaledOtherValue;
     175              : 
     176            1 :     return Amount(
     177              :       value: resultValue,
     178              :       decimals: maxDecimals,
     179              :     );
     180              :   }
     181              : 
     182              :   // Division operator
     183            2 :   Amount operator /(Amount other) {
     184            6 :     if (other.value == BigInt.zero) {
     185            1 :       throw ArgumentError('Cannot divide by zero.');
     186              :     }
     187              : 
     188              :     // Determine the maximum decimals between the two amounts
     189            6 :     int maxDecimals = max(decimals, other.decimals);
     190              : 
     191              :     // Scale both amounts to the same decimal level
     192           12 :     BigInt scaledThisValue = value * BigInt.from(pow(10, maxDecimals - decimals));
     193           12 :     BigInt scaledOtherValue = other.value * BigInt.from(pow(10, maxDecimals - other.decimals));
     194              : 
     195              :     // Perform the division
     196            8 :     BigInt resultValue = (scaledThisValue * BigInt.from(pow(10, maxDecimals))) ~/ scaledOtherValue;
     197              : 
     198            2 :     return Amount(
     199              :       value: resultValue,
     200              :       decimals: maxDecimals,
     201              :     );
     202              :   }
     203              : 
     204            2 :   bool operator >(Amount other) {
     205            6 :     return value > other.value;
     206              :   }
     207              : 
     208            2 :   bool operator <(Amount other) {
     209            6 :     return value < other.value;
     210              :   }
     211              : 
     212            2 :   Amount multiplyAndCeil(double multiplier) {
     213            6 :     final result = value.toInt() * multiplier;
     214            4 :     final result_int = result.ceil().toBI;
     215              : 
     216            4 :     return Amount(value: result_int, decimals: decimals);
     217              :   }
     218              : }
     219              : 
     220              : extension AmountUtil on int {
     221            0 :   Amount get asAmount {
     222            0 :     return Amount.from(value: this, decimals: 0);
     223              :   }
     224              : 
     225            0 :   Amount toAmount(int decimals) {
     226            0 :     return Amount.from(value: this, decimals: decimals);
     227              :   }
     228              : }
     229              : 
     230              : extension AmountUtilDouble on double {
     231            0 :   int get decimals {
     232            0 :     final parts = toString().split('.');
     233              : 
     234            0 :     return parts.length == 1 ? 0 : parts[1].length;
     235              :   }
     236              : 
     237              :   // operator *(Amount other) {
     238              : }
     239              : 
     240              : extension AmountUtilNum on num {
     241            2 :   String get toExactString {
     242              :     // https://stackoverflow.com/questions/62989638/convert-long-double-to-string-without-scientific-notation-dart
     243            2 :     double value = toDouble();
     244              :     var sign = "";
     245            2 :     if (value < 0) {
     246            0 :       value = -value;
     247              :       sign = "-";
     248              :     }
     249            2 :     var string = value.toString();
     250            2 :     var e = string.lastIndexOf('e');
     251            4 :     if (e < 0) return "$sign$string";
     252            2 :     var hasComma = string.indexOf('.') == 1;
     253            1 :     var offset = int.parse(
     254            4 :       string.substring(e + (string.startsWith('-', e + 1) ? 1 : 2)),
     255              :     );
     256            1 :     var digits = string.substring(0, 1);
     257              : 
     258              :     if (hasComma) {
     259            0 :       digits += string.substring(2, e);
     260              :     }
     261              : 
     262            1 :     if (offset < 0) {
     263            3 :       return "${sign}0.${"0" * ~offset}$digits";
     264              :     }
     265            0 :     if (offset > 0) {
     266            0 :       if (offset >= digits.length) {
     267            0 :         return sign + digits.padRight(offset + 1, "0");
     268              :       }
     269            0 :       return "$sign${digits.substring(0, offset + 1)}"
     270            0 :           ".${digits.substring(offset + 1)}";
     271              :     }
     272              :     return digits;
     273              :   }
     274              : }
     275              : 
     276              : extension AmountUtilBigInt on BigInt {
     277            0 :   BigInt multiply(double other) {
     278            0 :     final _other = shiftLeftBigInt(other, other.decimals);
     279            0 :     final result = this * _other;
     280              : 
     281            0 :     return discardRightBigInt(
     282              :       result,
     283            0 :       other.decimals,
     284              :     );
     285              :   }
     286              : 
     287            0 :   BigInt shiftLeft(int decimalPlaces) => this * BigInt.from(pow(10, decimalPlaces));
     288              : 
     289            0 :   BigInt shiftRight(int decimalPlaces) => this ~/ BigInt.from(pow(10, decimalPlaces));
     290              : }
     291              : 
     292            0 : BigInt shiftLeftBigInt(num num1, int decimalPlaces) => BigInt.from(num1 * pow(10, decimalPlaces));
     293              : 
     294            0 : BigInt discardRightBigInt(BigInt num1, int decimalPlaces) =>
     295            0 :     num1 ~/ BigInt.from(pow(10, decimalPlaces));
     296              : 
     297            0 : double shiftRightBigInt(BigInt num1, int decimalPlaces) => num1.toInt() / pow(10, decimalPlaces);
        

Generated by: LCOV version 2.0-1