mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-22 19:39:22 +00:00
feat: amount string group separator based on locale and parse those formatted strings back to amounts based on localized decimal pattern
This commit is contained in:
parent
aac6c0fdb6
commit
7bcfc87f4d
3 changed files with 146 additions and 14 deletions
|
@ -63,4 +63,13 @@ class AmountFormatter {
|
||||||
tokenContract: ethContract,
|
tokenContract: ethContract,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Amount? amountFrom(
|
||||||
|
String string, {
|
||||||
|
required String locale,
|
||||||
|
required Coin coin,
|
||||||
|
EthContract? ethContract,
|
||||||
|
}) {
|
||||||
|
return unit.tryParse(string, locale: locale, coin: coin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,6 +164,51 @@ extension AmountUnitExt on AmountUnit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Amount? tryParse(
|
||||||
|
String value, {
|
||||||
|
required String locale,
|
||||||
|
required Coin coin,
|
||||||
|
EthContract? tokenContract,
|
||||||
|
}) {
|
||||||
|
final precisionLost = value.startsWith("~");
|
||||||
|
|
||||||
|
final parts = (precisionLost ? value.substring(1) : 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
|
||||||
|
final numberSymbols = numberFormatSymbols[locale] as NumberSymbols? ??
|
||||||
|
numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?;
|
||||||
|
|
||||||
|
final groupSeparator = numberSymbols?.GROUP_SEP ?? ",";
|
||||||
|
final decimalSeparator = numberSymbols?.DECIMAL_SEP ?? ".";
|
||||||
|
|
||||||
|
str = str.replaceAll(groupSeparator, "");
|
||||||
|
|
||||||
|
final decimalString = str.replaceFirst(decimalSeparator, ".");
|
||||||
|
final Decimal? decimal = Decimal.tryParse(decimalString);
|
||||||
|
|
||||||
|
if (decimal == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final decimalPlaces = tokenContract?.decimals ?? coin.decimals;
|
||||||
|
final realShift = math.min(shift, decimalPlaces);
|
||||||
|
|
||||||
|
return decimal.shift(0 - realShift).toAmount(fractionDigits: decimalPlaces);
|
||||||
|
}
|
||||||
|
|
||||||
String displayAmount({
|
String displayAmount({
|
||||||
required Amount amount,
|
required Amount amount,
|
||||||
required String locale,
|
required String locale,
|
||||||
|
@ -191,6 +236,17 @@ extension AmountUnitExt on AmountUnit {
|
||||||
// start building the return value with just the whole value
|
// start building the return value with just the whole value
|
||||||
String returnValue = wholeNumber.toString();
|
String returnValue = wholeNumber.toString();
|
||||||
|
|
||||||
|
// get number symbols for decimal place and group separator
|
||||||
|
final numberSymbols = numberFormatSymbols[locale] as NumberSymbols? ??
|
||||||
|
numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?;
|
||||||
|
|
||||||
|
// insert group separator
|
||||||
|
final regex = RegExp(r'\B(?=(\d{3})+(?!\d))');
|
||||||
|
returnValue = returnValue.replaceAllMapped(
|
||||||
|
regex,
|
||||||
|
(m) => "${m.group(0)}${numberSymbols?.GROUP_SEP ?? ","}",
|
||||||
|
);
|
||||||
|
|
||||||
// if true and withUnitName is true, we will show "~" prepended on amount
|
// if true and withUnitName is true, we will show "~" prepended on amount
|
||||||
bool didLosePrecision = false;
|
bool didLosePrecision = false;
|
||||||
|
|
||||||
|
@ -239,11 +295,7 @@ extension AmountUnitExt on AmountUnit {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get decimal separator based on locale
|
// get decimal separator based on locale
|
||||||
final String separator =
|
final String separator = numberSymbols?.DECIMAL_SEP ?? ".";
|
||||||
(numberFormatSymbols[locale] as NumberSymbols?)?.DECIMAL_SEP ??
|
|
||||||
(numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?)
|
|
||||||
?.DECIMAL_SEP ??
|
|
||||||
".";
|
|
||||||
|
|
||||||
// append separator and fractional amount
|
// append separator and fractional amount
|
||||||
returnValue += "$separator$remainder";
|
returnValue += "$separator$remainder";
|
||||||
|
|
|
@ -28,7 +28,7 @@ void main() {
|
||||||
coin: Coin.bitcoin,
|
coin: Coin.bitcoin,
|
||||||
maxDecimalPlaces: 8,
|
maxDecimalPlaces: 8,
|
||||||
),
|
),
|
||||||
"10123.45678 mBTC",
|
"10,123.45678 mBTC",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -38,7 +38,7 @@ void main() {
|
||||||
coin: Coin.bitcoin,
|
coin: Coin.bitcoin,
|
||||||
maxDecimalPlaces: 8,
|
maxDecimalPlaces: 8,
|
||||||
),
|
),
|
||||||
"10123456.78 µBTC",
|
"10,123,456.78 µBTC",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -48,7 +48,7 @@ void main() {
|
||||||
coin: Coin.bitcoin,
|
coin: Coin.bitcoin,
|
||||||
maxDecimalPlaces: 8,
|
maxDecimalPlaces: 8,
|
||||||
),
|
),
|
||||||
"1012345678 sats",
|
"1,012,345,678 sats",
|
||||||
);
|
);
|
||||||
final dec = Decimal.parse("10.123456789123456789");
|
final dec = Decimal.parse("10.123456789123456789");
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ void main() {
|
||||||
coin: Coin.ethereum,
|
coin: Coin.ethereum,
|
||||||
maxDecimalPlaces: 9,
|
maxDecimalPlaces: 9,
|
||||||
),
|
),
|
||||||
"~10123.456789123 mETH",
|
"~10,123.456789123 mETH",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -108,7 +108,7 @@ void main() {
|
||||||
coin: Coin.ethereum,
|
coin: Coin.ethereum,
|
||||||
maxDecimalPlaces: 8,
|
maxDecimalPlaces: 8,
|
||||||
),
|
),
|
||||||
"~10123456.78912345 µETH",
|
"~10,123,456.78912345 µETH",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -118,7 +118,7 @@ void main() {
|
||||||
coin: Coin.ethereum,
|
coin: Coin.ethereum,
|
||||||
maxDecimalPlaces: 1,
|
maxDecimalPlaces: 1,
|
||||||
),
|
),
|
||||||
"~10123456789.1 gwei",
|
"~10,123,456,789.1 gwei",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -128,7 +128,7 @@ void main() {
|
||||||
coin: Coin.ethereum,
|
coin: Coin.ethereum,
|
||||||
maxDecimalPlaces: 18,
|
maxDecimalPlaces: 18,
|
||||||
),
|
),
|
||||||
"10123456789123.456789 mwei",
|
"10,123,456,789,123.456789 mwei",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -138,7 +138,7 @@ void main() {
|
||||||
coin: Coin.ethereum,
|
coin: Coin.ethereum,
|
||||||
maxDecimalPlaces: 4,
|
maxDecimalPlaces: 4,
|
||||||
),
|
),
|
||||||
"10123456789123456.789 kwei",
|
"10,123,456,789,123,456.789 kwei",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -148,7 +148,78 @@ void main() {
|
||||||
coin: Coin.ethereum,
|
coin: Coin.ethereum,
|
||||||
maxDecimalPlaces: 1,
|
maxDecimalPlaces: 1,
|
||||||
),
|
),
|
||||||
"10123456789123456789 wei",
|
"10,123,456,789,123,456,789 wei",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parse eth string to amount", () {
|
||||||
|
final Amount amount = Amount.fromDecimal(
|
||||||
|
Decimal.parse("10.123456789123456789"),
|
||||||
|
fractionDigits: Coin.ethereum.decimals,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
AmountUnit.nano.tryParse(
|
||||||
|
"~10,123,456,789.1 gwei",
|
||||||
|
locale: "en_US",
|
||||||
|
coin: Coin.ethereum,
|
||||||
|
),
|
||||||
|
Amount.fromDecimal(
|
||||||
|
Decimal.parse("10.1234567891"),
|
||||||
|
fractionDigits: Coin.ethereum.decimals,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
AmountUnit.atto.tryParse(
|
||||||
|
"10,123,456,789,123,456,789 wei",
|
||||||
|
locale: "en_US",
|
||||||
|
coin: Coin.ethereum,
|
||||||
|
),
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parse btc string to amount", () {
|
||||||
|
final Amount amount = Amount(
|
||||||
|
rawValue: BigInt.from(1012345678),
|
||||||
|
fractionDigits: 8,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
AmountUnit.normal.tryParse(
|
||||||
|
"10.12345678 BTC",
|
||||||
|
locale: "en_US",
|
||||||
|
coin: Coin.bitcoin,
|
||||||
|
),
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
AmountUnit.milli.tryParse(
|
||||||
|
"10,123.45678 mBTC",
|
||||||
|
locale: "en_US",
|
||||||
|
coin: Coin.bitcoin,
|
||||||
|
),
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
AmountUnit.micro.tryParse(
|
||||||
|
"10,123,456.7822 µBTC",
|
||||||
|
locale: "en_US",
|
||||||
|
coin: Coin.bitcoin,
|
||||||
|
),
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
AmountUnit.nano.tryParse(
|
||||||
|
"1,012,345,678 sats",
|
||||||
|
locale: "en_US",
|
||||||
|
coin: Coin.bitcoin,
|
||||||
|
),
|
||||||
|
amount,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue