stack_wallet/lib/utilities/amount/amount.dart

240 lines
7 KiB
Dart
Raw Normal View History

2023-05-26 21:21:16 +00:00
/*
* This file is part of Stack Wallet.
*
* Copyright (c) 2023 Cypher Stack
* All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details.
* Generated by Cypher Stack on 2023-05-26
*
*/
2023-03-24 21:30:29 +00:00
import 'dart:convert';
2023-03-24 17:08:02 +00:00
import 'package:decimal/decimal.dart';
2023-07-12 22:06:06 +00:00
import 'package:stackwallet/utilities/util.dart';
2023-03-24 17:08:02 +00:00
2023-04-04 16:47:36 +00:00
class Amount {
2023-03-24 17:08:02 +00:00
Amount({
required BigInt rawValue,
required this.fractionDigits,
}) : assert(fractionDigits >= 0),
_value = rawValue;
/// special zero case with [fractionDigits] set to 0
static Amount get zero => Amount(
rawValue: BigInt.zero,
fractionDigits: 0,
);
Amount.zeroWith({required this.fractionDigits})
: assert(fractionDigits >= 0),
_value = BigInt.zero;
2023-03-24 17:08:02 +00:00
/// truncate decimal value to [fractionDigits] places
Amount.fromDecimal(Decimal amount, {required this.fractionDigits})
: assert(fractionDigits >= 0),
_value = amount.shift(fractionDigits).toBigInt();
static Amount? tryParseFiatString(
String value, {
required String locale,
}) {
final parts = value.split(" ");
if (parts.first.isEmpty) {
return null;
}
String str = parts.first;
if (str.startsWith(RegExp(r'[+-]'))) {
str = str.substring(1);
}
if (str.isEmpty) {
return null;
}
// get number symbols for decimal place and group separator
2023-07-12 22:06:06 +00:00
final numberSymbols = Util.getSymbolsFor(locale: locale);
final groupSeparator = numberSymbols?.GROUP_SEP ?? ",";
final decimalSeparator = numberSymbols?.DECIMAL_SEP ?? ".";
str = str.replaceAll(groupSeparator, "");
final decimalString = str.replaceFirst(decimalSeparator, ".");
return Decimal.tryParse(decimalString)?.toAmount(fractionDigits: 2);
}
2023-03-24 17:08:02 +00:00
// ===========================================================================
// ======= Instance properties ===============================================
final int fractionDigits;
final BigInt _value;
// ===========================================================================
// ======= Getters ===========================================================
/// raw base value
BigInt get raw => _value;
/// actual decimal vale represented
2023-04-06 20:21:49 +00:00
Decimal get decimal => Decimal.fromBigInt(raw).shift(-1 * fractionDigits);
2023-03-24 17:08:02 +00:00
/// convenience getter
@Deprecated("provided for convenience only. Use fractionDigits instead.")
int get decimals => fractionDigits;
2023-03-24 21:30:29 +00:00
2023-03-30 18:26:19 +00:00
// ===========================================================================
// ======= Serialization =====================================================
2023-03-24 21:30:29 +00:00
2023-03-30 18:26:19 +00:00
Map<String, dynamic> toMap() {
2023-03-24 21:30:29 +00:00
return {"raw": raw.toString(), "fractionDigits": fractionDigits};
}
String toJsonString() {
return jsonEncode(toMap());
}
2023-05-29 21:10:55 +00:00
String fiatString({
2023-04-05 22:06:31 +00:00
required String locale,
}) {
2023-04-06 20:21:49 +00:00
final wholeNumber = decimal.truncate();
// get number symbols for decimal place and group separator
2023-07-12 22:06:06 +00:00
final numberSymbols = Util.getSymbolsFor(locale: locale);
final String separator = numberSymbols?.DECIMAL_SEP ?? ".";
2023-04-05 22:06:31 +00:00
2023-04-06 20:21:49 +00:00
final fraction = decimal - wholeNumber;
2023-04-05 22:06:31 +00:00
String wholeNumberString = wholeNumber.toStringAsFixed(0);
// insert group separator
final regex = RegExp(r'\B(?=(\d{3})+(?!\d))');
wholeNumberString = wholeNumberString.replaceAllMapped(
regex,
(m) => "${m.group(0)}${numberSymbols?.GROUP_SEP ?? ","}",
);
return "$wholeNumberString$separator${fraction.toStringAsFixed(2).substring(2)}";
2023-04-05 22:06:31 +00:00
}
2023-05-29 21:10:55 +00:00
// String localizedStringAsFixed({
// required String locale,
// int? decimalPlaces,
// }) {
// decimalPlaces ??= fractionDigits;
// assert(decimalPlaces >= 0);
//
// final wholeNumber = decimal.truncate();
//
// if (decimalPlaces == 0) {
// return wholeNumber.toStringAsFixed(0);
// }
//
// final String separator =
// (numberFormatSymbols[locale] as NumberSymbols?)?.DECIMAL_SEP ??
// (numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?)
// ?.DECIMAL_SEP ??
// ".";
//
// final fraction = decimal - wholeNumber;
//
// return "${wholeNumber.toStringAsFixed(0)}$separator${fraction.toStringAsFixed(decimalPlaces).substring(2)}";
// }
2023-04-05 22:06:31 +00:00
2023-03-24 21:30:29 +00:00
// ===========================================================================
// ======= Deserialization ===================================================
static Amount fromSerializedJsonString(String json) {
final map = jsonDecode(json) as Map;
return Amount(
rawValue: BigInt.parse(map["raw"] as String),
fractionDigits: map["fractionDigits"] as int,
);
}
// ===========================================================================
// ======= operators =========================================================
2023-04-06 20:21:49 +00:00
bool operator >(Amount other) => decimal > other.decimal;
2023-04-06 20:21:49 +00:00
bool operator <(Amount other) => decimal < other.decimal;
2023-04-06 20:21:49 +00:00
bool operator >=(Amount other) => decimal >= other.decimal;
2023-04-06 20:21:49 +00:00
bool operator <=(Amount other) => decimal <= other.decimal;
2023-04-04 16:47:36 +00:00
Amount operator +(Amount other) {
if (fractionDigits != other.fractionDigits) {
throw ArgumentError(
"fractionDigits do not match: this=$this, other=$other");
}
return Amount(
rawValue: raw + other.raw,
fractionDigits: fractionDigits,
);
}
Amount operator -(Amount other) {
if (fractionDigits != other.fractionDigits) {
throw ArgumentError(
"fractionDigits do not match: this=$this, other=$other");
}
return Amount(
rawValue: raw - other.raw,
fractionDigits: fractionDigits,
);
}
Amount operator *(Amount other) {
if (fractionDigits != other.fractionDigits) {
throw ArgumentError(
"fractionDigits do not match: this=$this, other=$other");
}
return Amount(
rawValue: raw * other.raw,
fractionDigits: fractionDigits,
);
}
2023-03-24 21:30:29 +00:00
// ===========================================================================
// ======= Overrides =========================================================
@override
String toString() => "Amount($raw, $fractionDigits)";
@override
2023-04-04 16:47:36 +00:00
bool operator ==(Object other) =>
identical(this, other) ||
other is Amount &&
runtimeType == other.runtimeType &&
raw == other.raw &&
fractionDigits == other.fractionDigits;
2023-03-24 21:30:29 +00:00
@override
2023-04-04 16:47:36 +00:00
int get hashCode => Object.hashAll([raw, fractionDigits]);
2023-03-24 17:08:02 +00:00
}
2023-04-05 22:06:31 +00:00
// =============================================================================
// =============================================================================
// ======= Extensions ==========================================================
extension DecimalAmountExt on Decimal {
Amount toAmount({required int fractionDigits}) {
return Amount.fromDecimal(
this,
fractionDigits: fractionDigits,
);
}
}
extension IntAmountExtension on int {
2023-04-06 20:21:49 +00:00
Amount toAmountAsRaw({required int fractionDigits}) {
2023-04-05 22:06:31 +00:00
return Amount(
rawValue: BigInt.from(this),
fractionDigits: fractionDigits,
);
}
}