LCOV - code coverage report
Current view: top level - domain/entities - amount.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 55.9 % 127 71
Test Date: 2025-01-30 01:10:00 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              :     final formatted =
      74            0 :         string.substring(0, lastNonZeroIndex < 3 ? 3 : lastNonZeroIndex + 1);
      75              : 
      76            0 :     if (formatted.endsWith('.')) {
      77            0 :       return formatted.substring(0, formatted.length - 1);
      78              :     }
      79              : 
      80              :     return formatted;
      81              :   }
      82              : 
      83            0 :   Amount copyWith({
      84              :     BigInt? value,
      85              :     int? decimals,
      86              :   }) {
      87              :     if (value == null && decimals == null) return this;
      88              : 
      89            0 :     return Amount(
      90            0 :       value: value ?? this.value,
      91            0 :       decimals: decimals ?? this.decimals,
      92              :     );
      93              :   }
      94              : 
      95            0 :   bool isOther(Amount other) {
      96            0 :     return value == other.value && decimals == other.decimals;
      97              :   }
      98              : 
      99            0 :   @override
     100            0 :   int get hashCode => value.hashCode ^ decimals.hashCode;
     101              : 
     102            1 :   @override
     103              :   bool operator ==(Object other) {
     104              :     if (identical(this, other)) return true;
     105              : 
     106            1 :     return other is Amount &&
     107            3 :         other.value == value &&
     108            0 :         other.decimals == decimals;
     109              :   }
     110              : 
     111            2 :   @override
     112              :   String toString() {
     113            8 :     return 'Amount{value: $value, decimals: $decimals}: $displayDouble';
     114              :   }
     115              : 
     116            1 :   Map<String, dynamic> toJson() {
     117            1 :     return {
     118            2 :       'value': value.toString(),
     119            1 :       'decimals': decimals,
     120              :     };
     121              :   }
     122              : 
     123            1 :   factory Amount.fromJson(Map json) {
     124            1 :     return Amount(
     125            2 :       value: BigInt.parse(json['value']),
     126            1 :       decimals: json['decimals'],
     127              :     );
     128              :   }
     129              : 
     130            0 :   Amount convertToDecimals(int newDecimals) {
     131            0 :     if (newDecimals == decimals) return this;
     132              : 
     133            0 :     final newAmount = value * BigInt.from(10).pow(newDecimals - decimals);
     134              : 
     135            0 :     return Amount(
     136              :       value: newAmount,
     137              :       decimals: newDecimals,
     138              :     );
     139              :   }
     140              : 
     141              :   /// Operators
     142            1 :   Amount operator *(Amount other) {
     143            1 :     return Amount(
     144            3 :       value: value * other.value,
     145            3 :       decimals: decimals + other.decimals,
     146              :     );
     147              :   }
     148              : 
     149              :   // Plus operator
     150            2 :   Amount operator +(Amount other) {
     151              :     // Determine the maximum decimals between the two amounts
     152            6 :     int maxDecimals = max(decimals, other.decimals);
     153              : 
     154              :     // Scale both amounts to the same decimal level
     155              :     BigInt scaledThisValue =
     156           12 :         value * BigInt.from(pow(10, maxDecimals - decimals));
     157              :     BigInt scaledOtherValue =
     158           12 :         other.value * BigInt.from(pow(10, maxDecimals - other.decimals));
     159              : 
     160              :     // Perform the addition
     161            2 :     BigInt resultValue = scaledThisValue + scaledOtherValue;
     162              : 
     163            2 :     return Amount(
     164              :       value: resultValue,
     165              :       decimals: maxDecimals,
     166              :     );
     167              :   }
     168              : 
     169              :   // Minus operator
     170            1 :   Amount operator -(Amount other) {
     171              :     // Determine the maximum decimals between the two amounts
     172            3 :     int maxDecimals = max(decimals, other.decimals);
     173              : 
     174              :     // Scale both amounts to the same decimal level
     175              :     BigInt scaledThisValue =
     176            6 :         value * BigInt.from(pow(10, maxDecimals - decimals));
     177              :     BigInt scaledOtherValue =
     178            6 :         other.value * BigInt.from(pow(10, maxDecimals - other.decimals));
     179              : 
     180              :     // Perform the subtraction
     181            1 :     BigInt resultValue = scaledThisValue - scaledOtherValue;
     182              : 
     183            1 :     return Amount(
     184              :       value: resultValue,
     185              :       decimals: maxDecimals,
     186              :     );
     187              :   }
     188              : 
     189              :   // Division operator
     190            2 :   Amount operator /(Amount other) {
     191            6 :     if (other.value == BigInt.zero) {
     192            1 :       throw ArgumentError('Cannot divide by zero.');
     193              :     }
     194              : 
     195              :     // Determine the maximum decimals between the two amounts
     196            6 :     int maxDecimals = max(decimals, other.decimals);
     197              : 
     198              :     // Scale both amounts to the same decimal level
     199              :     BigInt scaledThisValue =
     200           12 :         value * BigInt.from(pow(10, maxDecimals - decimals));
     201              :     BigInt scaledOtherValue =
     202           12 :         other.value * BigInt.from(pow(10, maxDecimals - other.decimals));
     203              : 
     204              :     // Perform the division
     205              :     BigInt resultValue =
     206            8 :         (scaledThisValue * BigInt.from(pow(10, maxDecimals))) ~/
     207              :             scaledOtherValue;
     208              : 
     209            2 :     return Amount(
     210              :       value: resultValue,
     211              :       decimals: maxDecimals,
     212              :     );
     213              :   }
     214              : 
     215            2 :   bool operator >(Amount other) {
     216            6 :     return value > other.value;
     217              :   }
     218              : 
     219            2 :   bool operator <(Amount other) {
     220            6 :     return value < other.value;
     221              :   }
     222              : 
     223            2 :   Amount multiplyAndCeil(double multiplier) {
     224            6 :     final result = value.toInt() * multiplier;
     225            4 :     final result_int = result.ceil().toBI;
     226              : 
     227            4 :     return Amount(value: result_int, decimals: decimals);
     228              :   }
     229              : }
     230              : 
     231              : extension AmountUtil on int {
     232            0 :   Amount get asAmount {
     233            0 :     return Amount.from(value: this, decimals: 0);
     234              :   }
     235              : 
     236            0 :   Amount toAmount(int decimals) {
     237            0 :     return Amount.from(value: this, decimals: decimals);
     238              :   }
     239              : }
     240              : 
     241              : extension AmountUtilDouble on double {
     242            0 :   int get decimals {
     243            0 :     final parts = toString().split('.');
     244              : 
     245            0 :     return parts.length == 1 ? 0 : parts[1].length;
     246              :   }
     247              : 
     248              :   // operator *(Amount other) {
     249              : }
     250              : 
     251              : extension AmountUtilNum on num {
     252            2 :   String get toExactString {
     253              :     // https://stackoverflow.com/questions/62989638/convert-long-double-to-string-without-scientific-notation-dart
     254            2 :     double value = toDouble();
     255              :     var sign = "";
     256            2 :     if (value < 0) {
     257            0 :       value = -value;
     258              :       sign = "-";
     259              :     }
     260            2 :     var string = value.toString();
     261            2 :     var e = string.lastIndexOf('e');
     262            4 :     if (e < 0) return "$sign$string";
     263            2 :     var hasComma = string.indexOf('.') == 1;
     264            1 :     var offset = int.parse(
     265            4 :       string.substring(e + (string.startsWith('-', e + 1) ? 1 : 2)),
     266              :     );
     267            1 :     var digits = string.substring(0, 1);
     268              : 
     269              :     if (hasComma) {
     270            0 :       digits += string.substring(2, e);
     271              :     }
     272              : 
     273            1 :     if (offset < 0) {
     274            3 :       return "${sign}0.${"0" * ~offset}$digits";
     275              :     }
     276            0 :     if (offset > 0) {
     277            0 :       if (offset >= digits.length) {
     278            0 :         return sign + digits.padRight(offset + 1, "0");
     279              :       }
     280            0 :       return "$sign${digits.substring(0, offset + 1)}"
     281            0 :           ".${digits.substring(offset + 1)}";
     282              :     }
     283              :     return digits;
     284              :   }
     285              : }
     286              : 
     287              : extension AmountUtilBigInt on BigInt {
     288            0 :   BigInt multiply(double other) {
     289            0 :     final _other = shiftLeftBigInt(other, other.decimals);
     290            0 :     final result = this * _other;
     291              : 
     292            0 :     return discardRightBigInt(
     293              :       result,
     294            0 :       other.decimals,
     295              :     );
     296              :   }
     297              : 
     298            0 :   BigInt shiftLeft(int decimalPlaces) =>
     299            0 :       this * BigInt.from(pow(10, decimalPlaces));
     300              : 
     301            0 :   BigInt shiftRight(int decimalPlaces) =>
     302            0 :       this ~/ BigInt.from(pow(10, decimalPlaces));
     303              : }
     304              : 
     305            0 : BigInt shiftLeftBigInt(num num1, int decimalPlaces) =>
     306            0 :     BigInt.from(num1 * pow(10, decimalPlaces));
     307              : 
     308            0 : BigInt discardRightBigInt(BigInt num1, int decimalPlaces) =>
     309            0 :     num1 ~/ BigInt.from(pow(10, decimalPlaces));
     310              : 
     311            0 : double shiftRightBigInt(BigInt num1, int decimalPlaces) =>
     312            0 :     num1.toInt() / pow(10, decimalPlaces);
        

Generated by: LCOV version 2.0-1