Coin and denom tests

This commit is contained in:
Drazen Urch
2021-09-14 10:29:31 +02:00
parent 0278bd2c26
commit 40fbdff05a
+189 -15
View File
@@ -9,23 +9,25 @@ use cosmwasm_std::Uint128;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::fmt;
use std::ops::Add;
use std::ops::{Add, Sub};
use std::str::FromStr;
use ts_rs::TS;
use validator_client::nymd::GasPrice;
use crate::format_err;
#[derive(TS, Serialize, Deserialize, Clone)]
#[derive(TS, Serialize, Deserialize, Clone, PartialEq, Debug)]
pub enum Denom {
Major,
Minor,
}
const MINOR_IN_MAJOR: f64 = 1_000_000.;
impl fmt::Display for Denom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Denom::Major => f.write_str(&DENOM[1..].to_uppercase()),
Denom::Major => f.write_str(&DENOM[1..]),
Denom::Minor => f.write_str(DENOM),
}
}
@@ -49,7 +51,7 @@ impl FromStr for Denom {
}
}
#[derive(TS, Serialize, Deserialize, Clone)]
#[derive(TS, Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct Coin {
amount: String,
denom: Denom,
@@ -93,12 +95,55 @@ impl Add for Coin {
}
}
// Allows adding minor and major denominations, output will have the LHS denom.
impl Sub for Coin {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
let denom = self.denom.clone();
let lhs = self.to_minor();
let rhs = rhs.to_minor();
let lhs_amount = lhs.amount.parse::<i64>().unwrap();
let rhs_amount = rhs.amount.parse::<i64>().unwrap();
let amount = lhs_amount - rhs_amount;
let coin = Coin {
amount: amount.to_string(),
denom: Denom::Minor,
};
match denom {
Denom::Major => coin.to_major(),
Denom::Minor => coin,
}
}
}
impl Coin {
pub fn major<T: ToString>(amount: T) -> Coin {
Coin {
amount: amount.to_string(),
denom: Denom::Major,
}
}
pub fn minor<T: ToString>(amount: T) -> Coin {
Coin {
amount: amount.to_string(),
denom: Denom::Minor,
}
}
pub fn new<T: ToString>(amount: T, denom: &Denom) -> Coin {
Coin {
amount: amount.to_string(),
denom: denom.clone(),
}
}
pub fn to_major(&self) -> Coin {
match self.denom {
Denom::Major => self.clone(),
Denom::Minor => Coin {
amount: (self.amount.parse::<f64>().unwrap() / 1_000_000.).to_string(),
amount: (self.amount.parse::<f64>().unwrap() / MINOR_IN_MAJOR).to_string(),
denom: Denom::Major,
},
}
@@ -108,7 +153,7 @@ impl Coin {
match self.denom {
Denom::Minor => self.clone(),
Denom::Major => Coin {
amount: (self.amount.parse::<f64>().unwrap() * 1_000_000.).to_string(),
amount: (self.amount.parse::<f64>().unwrap() * MINOR_IN_MAJOR).to_string(),
denom: Denom::Minor,
},
}
@@ -121,20 +166,12 @@ impl Coin {
pub fn denom(&self) -> Denom {
self.denom.clone()
}
pub fn new(amount: &str, denom: &Denom) -> Coin {
Coin {
amount: amount.to_string(),
denom: denom.clone(),
}
}
}
impl TryFrom<Coin> for CosmWasmCoin {
type Error = String;
fn try_from(coin: Coin) -> Result<CosmWasmCoin, String> {
let coin = coin.to_minor();
Ok(CosmWasmCoin::new(
Uint128::try_from(coin.amount.as_str()).unwrap().u128(),
coin.denom.to_string(),
@@ -146,7 +183,6 @@ impl TryFrom<Coin> for CosmosCoin {
type Error = String;
fn try_from(coin: Coin) -> Result<CosmosCoin, String> {
let coin = coin.to_minor();
match Decimal::from_str(&coin.amount) {
Ok(d) => Ok(CosmosCoin {
amount: d,
@@ -174,3 +210,141 @@ impl From<CosmWasmCoin> for Coin {
}
}
}
#[cfg(test)]
mod test {
use crate::{Coin, Denom};
use cosmos_sdk::Coin as CosmosCoin;
use cosmos_sdk::Decimal;
use cosmos_sdk::Denom as CosmosDenom;
use cosmwasm_std::Coin as CosmWasmCoin;
use serde_json::json;
use std::convert::{TryFrom, TryInto};
use std::str::FromStr;
#[test]
fn json_to_coin() {
let minor = json!({
"amount": "1",
"denom": "Minor"
});
let major = json!({
"amount": "1",
"denom": "Major"
});
let test_minor_coin = Coin::minor("1");
let test_major_coin = Coin::major("1");
let minor_coin = serde_json::from_value::<Coin>(minor).unwrap();
let major_coin = serde_json::from_value::<Coin>(major).unwrap();
assert_eq!(minor_coin, test_minor_coin);
assert_eq!(major_coin, test_major_coin);
}
#[test]
fn denom_conversions() {
let minor = Coin::minor("1");
let major = minor.to_major();
assert_eq!(major, Coin::major("0.000001"));
let minor = major.to_minor();
assert_eq!(minor, Coin::minor("1"));
}
fn amounts() -> Vec<&'static str> {
vec![
"1",
"10",
"100",
"1000",
"10000",
"100000",
"10000000",
"100000000",
"1000000000",
"10000000000",
"100000000000",
"1000000000000",
"10000000000000",
"100000000000000",
"1000000000000000",
"10000000000000000",
"100000000000000000",
"1000000000000000000"
]
}
#[test]
fn coin_to_cosmoswasm() {
for amount in amounts() {
let coin: Coin = Coin::minor(amount).into();
let cosmoswasm_coin: CosmWasmCoin = coin.try_into().unwrap();
assert_eq!(
cosmoswasm_coin,
CosmWasmCoin::new(amount.parse::<u128>().unwrap(), Denom::Minor.to_string())
);
assert_eq!(
Coin::try_from(cosmoswasm_coin).unwrap(),
Coin::minor(amount)
);
let coin: Coin = Coin::major(amount).into();
let cosmoswasm_coin: CosmWasmCoin = coin.try_into().unwrap();
assert_eq!(
cosmoswasm_coin,
CosmWasmCoin::new(amount.parse::<u128>().unwrap(), Denom::Major.to_string())
);
assert_eq!(
Coin::try_from(cosmoswasm_coin).unwrap(),
Coin::major(amount)
);
}
}
#[test]
fn coin_to_cosmos() {
for amount in amounts() {
let coin: Coin = Coin::minor(amount).into();
let cosmos_coin: CosmosCoin = coin.try_into().unwrap();
assert_eq!(
cosmos_coin,
CosmosCoin {
amount: Decimal::from_str(amount).unwrap(),
denom: CosmosDenom::from_str(&Denom::Minor.to_string()).unwrap()
}
);
assert_eq!(Coin::try_from(cosmos_coin).unwrap(), Coin::minor(amount));
let coin: Coin = Coin::major(amount).into();
let cosmos_coin: CosmosCoin = coin.try_into().unwrap();
assert_eq!(
cosmos_coin,
CosmosCoin {
amount: Decimal::from_str(amount).unwrap(),
denom: CosmosDenom::from_str(&Denom::Major.to_string()).unwrap()
}
);
assert_eq!(Coin::try_from(cosmos_coin).unwrap(), Coin::major(amount));
}
}
#[test]
fn test_add() {
assert_eq!(Coin::minor("1") + Coin::minor("1"), Coin::minor("2"));
assert_eq!(Coin::major("1") + Coin::major("1"), Coin::major("2"));
assert_eq!(Coin::minor("1") + Coin::major("1"), Coin::minor("1000001"));
assert_eq!(Coin::major("1") + Coin::minor("1"), Coin::major("1.000001"));
}
#[test]
fn test_sub() {
assert_eq!(Coin::minor("1") - Coin::minor("1"), Coin::minor("0"));
assert_eq!(Coin::major("1") - Coin::major("1"), Coin::major("0"));
assert_eq!(Coin::minor("1") - Coin::major("1"), Coin::minor("-999999"));
assert_eq!(Coin::major("1") - Coin::minor("1"), Coin::major("0.999999"));
}
}