add nano and pico BTC to Donomination enum

This commit is contained in:
KaFai Choi 2022-01-10 21:30:28 +07:00
parent b91058c50c
commit e80de8b1ee
No known key found for this signature in database
GPG Key ID: C93944C459E21542
1 changed files with 38 additions and 5 deletions

View File

@ -28,6 +28,10 @@ pub enum Denomination {
MilliBitcoin, MilliBitcoin,
/// uBTC /// uBTC
MicroBitcoin, MicroBitcoin,
/// nBTC
NanoBitcoin,
/// pBTC
PicoBitcoin,
/// bits /// bits
Bit, Bit,
/// satoshi /// satoshi
@ -43,6 +47,8 @@ impl Denomination {
Denomination::Bitcoin => -8, Denomination::Bitcoin => -8,
Denomination::MilliBitcoin => -5, Denomination::MilliBitcoin => -5,
Denomination::MicroBitcoin => -2, Denomination::MicroBitcoin => -2,
Denomination::NanoBitcoin => 1,
Denomination::PicoBitcoin => 4,
Denomination::Bit => -2, Denomination::Bit => -2,
Denomination::Satoshi => 0, Denomination::Satoshi => 0,
Denomination::MilliSatoshi => 3, Denomination::MilliSatoshi => 3,
@ -56,6 +62,8 @@ impl fmt::Display for Denomination {
Denomination::Bitcoin => "BTC", Denomination::Bitcoin => "BTC",
Denomination::MilliBitcoin => "mBTC", Denomination::MilliBitcoin => "mBTC",
Denomination::MicroBitcoin => "uBTC", Denomination::MicroBitcoin => "uBTC",
Denomination::NanoBitcoin => "nBTC",
Denomination::PicoBitcoin => "pBTC",
Denomination::Bit => "bits", Denomination::Bit => "bits",
Denomination::Satoshi => "satoshi", Denomination::Satoshi => "satoshi",
Denomination::MilliSatoshi => "msat", Denomination::MilliSatoshi => "msat",
@ -68,15 +76,15 @@ impl FromStr for Denomination {
/// Convert from a str to Denomination. /// Convert from a str to Denomination.
/// ///
/// Any combination of upper and/or lower case, excluding uppercase 'M' is considered valid. /// Any combination of upper and/or lower case, excluding uppercase 'M', 'P' is considered valid.
/// - Singular: BTC, mBTC, uBTC /// - Singular: BTC, mBTC, uBTC, nBTC, pBTC
/// - Plural or singular: sat, satoshi, bit, msat /// - Plural or singular: sat, satoshi, bit, msat
/// ///
/// Due to ambiguity between mega and milli we prohibit usage of leading capital 'M'. /// Due to ambiguity between mega and milli we prohibit usage of leading capital 'M'.
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
use self::ParseAmountError::*; use self::ParseAmountError::*;
if s.starts_with('M') { if s.starts_with(|ch| ch == 'M' || ch == 'P') {
return Err(denomination_from_str(s).map_or_else( return Err(denomination_from_str(s).map_or_else(
|| UnknownDenomination(s.to_owned()), || UnknownDenomination(s.to_owned()),
|_| PossiblyConfusingDenomination(s.to_owned()) |_| PossiblyConfusingDenomination(s.to_owned())
@ -100,6 +108,14 @@ fn denomination_from_str(mut s: &str) -> Option<Denomination> {
return Some(Denomination::MicroBitcoin); return Some(Denomination::MicroBitcoin);
} }
if s.eq_ignore_ascii_case("nBTC") {
return Some(Denomination::NanoBitcoin);
}
if s.eq_ignore_ascii_case("pBTC") {
return Some(Denomination::PicoBitcoin);
}
if s.ends_with('s') || s.ends_with('S') { if s.ends_with('s') || s.ends_with('S') {
s = &s[..(s.len() - 1)]; s = &s[..(s.len() - 1)];
} }
@ -1439,6 +1455,12 @@ mod tests {
assert_eq!("-5", SignedAmount::from_sat(-5).to_string_in(D::Satoshi)); assert_eq!("-5", SignedAmount::from_sat(-5).to_string_in(D::Satoshi));
assert_eq!("0.10000000", Amount::from_sat(100_000_00).to_string_in(D::Bitcoin)); assert_eq!("0.10000000", Amount::from_sat(100_000_00).to_string_in(D::Bitcoin));
assert_eq!("-100.00", SignedAmount::from_sat(-10_000).to_string_in(D::Bit)); assert_eq!("-100.00", SignedAmount::from_sat(-10_000).to_string_in(D::Bit));
assert_eq!("2535830", Amount::from_sat(253583).to_string_in(D::NanoBitcoin));
assert_eq!("-100000", SignedAmount::from_sat(-10_000).to_string_in(D::NanoBitcoin));
assert_eq!("2535830000", Amount::from_sat(253583).to_string_in(D::PicoBitcoin));
assert_eq!("-100000000", SignedAmount::from_sat(-10_000).to_string_in(D::PicoBitcoin));
assert_eq!(ua_str(&ua_sat(0).to_string_in(D::Satoshi), D::Satoshi), Ok(ua_sat(0))); assert_eq!(ua_str(&ua_sat(0).to_string_in(D::Satoshi), D::Satoshi), Ok(ua_sat(0)));
assert_eq!(ua_str(&ua_sat(500).to_string_in(D::Bitcoin), D::Bitcoin), Ok(ua_sat(500))); assert_eq!(ua_str(&ua_sat(500).to_string_in(D::Bitcoin), D::Bitcoin), Ok(ua_sat(500)));
@ -1453,6 +1475,15 @@ mod tests {
// Test an overflow bug in `abs()` // Test an overflow bug in `abs()`
assert_eq!(sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::MicroBitcoin), Err(ParseAmountError::TooBig)); assert_eq!(sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::MicroBitcoin), Err(ParseAmountError::TooBig));
assert_eq!(sa_str(&sa_sat(-1).to_string_in(D::NanoBitcoin), D::NanoBitcoin), Ok(sa_sat(-1)));
assert_eq!(sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::NanoBitcoin), Err(ParseAmountError::TooPrecise));
assert_eq!(sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::NanoBitcoin), Err(ParseAmountError::TooPrecise));
assert_eq!(sa_str(&sa_sat(-1).to_string_in(D::PicoBitcoin), D::PicoBitcoin), Ok(sa_sat(-1)));
assert_eq!(sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::PicoBitcoin), Err(ParseAmountError::TooPrecise));
assert_eq!(sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::PicoBitcoin), Err(ParseAmountError::TooPrecise));
} }
#[test] #[test]
@ -1465,7 +1496,9 @@ mod tests {
assert_eq!(Amount::from_str(&denom(amt, D::MicroBitcoin)), Ok(amt)); assert_eq!(Amount::from_str(&denom(amt, D::MicroBitcoin)), Ok(amt));
assert_eq!(Amount::from_str(&denom(amt, D::Bit)), Ok(amt)); assert_eq!(Amount::from_str(&denom(amt, D::Bit)), Ok(amt));
assert_eq!(Amount::from_str(&denom(amt, D::Satoshi)), Ok(amt)); assert_eq!(Amount::from_str(&denom(amt, D::Satoshi)), Ok(amt));
assert_eq!(Amount::from_str(&denom(amt, D::NanoBitcoin)), Ok(amt));
assert_eq!(Amount::from_str(&denom(amt, D::MilliSatoshi)), Ok(amt)); assert_eq!(Amount::from_str(&denom(amt, D::MilliSatoshi)), Ok(amt));
assert_eq!(Amount::from_str(&denom(amt, D::PicoBitcoin)), Ok(amt));
assert_eq!(Amount::from_str("42 satoshi BTC"), Err(ParseAmountError::InvalidFormat)); assert_eq!(Amount::from_str("42 satoshi BTC"), Err(ParseAmountError::InvalidFormat));
assert_eq!(SignedAmount::from_str("-42 satoshi BTC"), Err(ParseAmountError::InvalidFormat)); assert_eq!(SignedAmount::from_str("-42 satoshi BTC"), Err(ParseAmountError::InvalidFormat));
@ -1693,7 +1726,7 @@ mod tests {
#[test] #[test]
fn denomination_string_acceptable_forms() { fn denomination_string_acceptable_forms() {
// Non-exhaustive list of valid forms. // Non-exhaustive list of valid forms.
let valid = vec!["BTC", "btc", "mBTC", "mbtc", "uBTC", "ubtc", "SATOSHI","Satoshi", "Satoshis", "satoshis", "SAT", "Sat", "sats", "bit", "bits"]; let valid = vec!["BTC", "btc", "mBTC", "mbtc", "uBTC", "ubtc", "SATOSHI","Satoshi", "Satoshis", "satoshis", "SAT", "Sat", "sats", "bit", "bits", "nBTC", "pBTC"];
for denom in valid.iter() { for denom in valid.iter() {
assert!(Denomination::from_str(denom).is_ok()); assert!(Denomination::from_str(denom).is_ok());
} }
@ -1702,7 +1735,7 @@ mod tests {
#[test] #[test]
fn disallow_confusing_forms() { fn disallow_confusing_forms() {
// Non-exhaustive list of confusing forms. // Non-exhaustive list of confusing forms.
let confusing = vec!["Msat", "Msats", "MSAT", "MSATS", "MSat", "MSats", "MBTC", "Mbtc"]; let confusing = vec!["Msat", "Msats", "MSAT", "MSATS", "MSat", "MSats", "MBTC", "Mbtc", "PBTC"];
for denom in confusing.iter() { for denom in confusing.iter() {
match Denomination::from_str(denom) { match Denomination::from_str(denom) {
Ok(_) => panic!("from_str should error for {}", denom), Ok(_) => panic!("from_str should error for {}", denom),