amount unit + tests

This commit is contained in:
julian 2023-04-06 17:27:42 -06:00
parent 25ff880280
commit ae51ce61c3
2 changed files with 264 additions and 0 deletions

View file

@ -0,0 +1,120 @@
import 'dart:math' as math;
import 'package:decimal/decimal.dart';
import 'package:intl/number_symbols.dart';
import 'package:intl/number_symbols_data.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
enum AmountUnit {
normal(0),
milli(3),
micro(6),
nano(9),
pico(12),
femto(15),
atto(18),
;
const AmountUnit(this.shift);
final int shift;
}
extension AmountUnitExt on AmountUnit {
String unitForCoin(Coin coin) {
switch (this) {
case AmountUnit.normal:
return coin.ticker;
case AmountUnit.milli:
return "m${coin.ticker}";
case AmountUnit.micro:
return "µ${coin.ticker}";
case AmountUnit.nano:
if (coin == Coin.ethereum) {
return "gwei";
} else if (coin == Coin.wownero || coin == Coin.monero) {
return "n${coin.ticker}";
} else {
return "sats";
}
case AmountUnit.pico:
if (coin == Coin.ethereum) {
return "mwei";
} else if (coin == Coin.wownero || coin == Coin.monero) {
return "p${coin.ticker}";
} else {
return "invalid";
}
case AmountUnit.femto:
if (coin == Coin.ethereum) {
return "kwei";
} else {
return "invalid";
}
case AmountUnit.atto:
if (coin == Coin.ethereum) {
return "wei";
} else {
return "invalid";
}
}
}
String displayAmount({
required Amount amount,
required String locale,
required Coin coin,
required int maxDecimalPlaces,
}) {
assert(maxDecimalPlaces >= 0);
// ensure we don't shift past minimum atomic value
final realShift = math.min(shift, amount.fractionDigits);
// shifted to unit
final Decimal shifted = amount.decimal.shift(realShift);
// get shift int value without fractional value
final BigInt wholeNumber = shifted.toBigInt();
// get decimal places to display
final int places = math.max(0, amount.fractionDigits - realShift);
// start building the return value with just the whole value
String returnValue = wholeNumber.toString();
// if any decimal places should be shown continue building the return value
if (places > 0) {
// get the fractional value
final Decimal fraction = shifted - shifted.truncate();
// get final decimal based on max precision wanted
final int actualDecimalPlaces = math.min(places, maxDecimalPlaces);
// get remainder string without the prepending "0."
String remainder = fraction.toString().substring(2);
if (remainder.length > actualDecimalPlaces) {
// trim unwanted trailing digits
remainder = remainder.substring(0, actualDecimalPlaces);
} else if (remainder.length < actualDecimalPlaces) {
// pad with zeros to achieve requested precision
for (int i = remainder.length; i < actualDecimalPlaces; i++) {
remainder += "0";
}
}
// get decimal separator based on locale
final String separator =
(numberFormatSymbols[locale] as NumberSymbols?)?.DECIMAL_SEP ??
(numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?)
?.DECIMAL_SEP ??
".";
// append separator and fractional amount
returnValue += "$separator$remainder";
}
// return the value with the proper unit symbol
return "$returnValue ${unitForCoin(coin)}";
}
}

View file

@ -0,0 +1,144 @@
import 'package:decimal/decimal.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_unit.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
void main() {
test("displayAmount BTC", () {
final Amount amount = Amount(
rawValue: BigInt.from(1012345678),
fractionDigits: 8,
);
expect(
AmountUnit.normal.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.bitcoin,
maxDecimalPlaces: 8,
),
"10.12345678 BTC",
);
expect(
AmountUnit.milli.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.bitcoin,
maxDecimalPlaces: 8,
),
"10123.45678 mBTC",
);
expect(
AmountUnit.micro.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.bitcoin,
maxDecimalPlaces: 8,
),
"10123456.78 µBTC",
);
expect(
AmountUnit.nano.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.bitcoin,
maxDecimalPlaces: 8,
),
"1012345678 sats",
);
final dec = Decimal.parse("10.123456789123456789");
expect(dec.toString(), "10.123456789123456789");
});
test("displayAmount ETH", () {
final Amount amount = Amount.fromDecimal(
Decimal.parse("10.123456789123456789"),
fractionDigits: Coin.ethereum.decimals,
);
expect(
AmountUnit.normal.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.ethereum,
maxDecimalPlaces: 8,
),
"10.12345678 ETH",
);
expect(
AmountUnit.normal.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.ethereum,
maxDecimalPlaces: 18,
),
"10.123456789123456789 ETH",
);
expect(
AmountUnit.milli.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.ethereum,
maxDecimalPlaces: 9,
),
"10123.456789123 mETH",
);
expect(
AmountUnit.micro.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.ethereum,
maxDecimalPlaces: 8,
),
"10123456.78912345 µETH",
);
expect(
AmountUnit.nano.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.ethereum,
maxDecimalPlaces: 1,
),
"10123456789.1 gwei",
);
expect(
AmountUnit.pico.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.ethereum,
maxDecimalPlaces: 18,
),
"10123456789123.456789 mwei",
);
expect(
AmountUnit.femto.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.ethereum,
maxDecimalPlaces: 4,
),
"10123456789123456.789 kwei",
);
expect(
AmountUnit.atto.displayAmount(
amount: amount,
locale: "en_US",
coin: Coin.ethereum,
maxDecimalPlaces: 1,
),
"10123456789123456789 wei",
);
});
}