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;
|
|
|
|
|
2023-03-30 16:49:07 +00:00
|
|
|
/// special zero case with [fractionDigits] set to 0
|
|
|
|
static Amount get zero => Amount(
|
|
|
|
rawValue: BigInt.zero,
|
|
|
|
fractionDigits: 0,
|
|
|
|
);
|
|
|
|
|
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();
|
|
|
|
|
2023-06-16 19:42:37 +00:00
|
|
|
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);
|
2023-06-16 19:42:37 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2023-06-16 19:42:37 +00:00
|
|
|
// get number symbols for decimal place and group separator
|
2023-07-12 22:06:06 +00:00
|
|
|
final numberSymbols = Util.getSymbolsFor(locale: locale);
|
2023-06-16 19:42:37 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
2023-06-16 19:42:37 +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,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-03-30 16:49:07 +00:00
|
|
|
// ===========================================================================
|
|
|
|
// ======= operators =========================================================
|
|
|
|
|
2023-04-06 20:21:49 +00:00
|
|
|
bool operator >(Amount other) => decimal > other.decimal;
|
2023-03-30 16:49:07 +00:00
|
|
|
|
2023-04-06 20:21:49 +00:00
|
|
|
bool operator <(Amount other) => decimal < other.decimal;
|
2023-03-30 16:49:07 +00:00
|
|
|
|
2023-04-06 20:21:49 +00:00
|
|
|
bool operator >=(Amount other) => decimal >= other.decimal;
|
2023-03-30 16:49:07 +00:00
|
|
|
|
2023-04-06 20:21:49 +00:00
|
|
|
bool operator <=(Amount other) => decimal <= other.decimal;
|
2023-03-30 16:49:07 +00:00
|
|
|
|
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,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|