use crate::nyxd::error::NyxdError;
use cosmrs::Coin;
use cosmrs::Gas;
use cosmwasm_std::{Decimal, Fraction, Uint128};
use nym_config::defaults;
use nym_network_defaults::NymNetworkDetails;
use std::ops::Mul;
use std::str::FromStr;
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct GasPrice {
pub amount: Decimal,
pub denom: String,
}
impl Mul<Gas> for &GasPrice {
type Output = Coin;
fn mul(self, gas_limit: Gas) -> Self::Output {
let limit_uint128 = Uint128::from(gas_limit);
let mut amount = limit_uint128.mul_floor(self.amount);
let gas_price_numerator = self.amount.numerator();
let gas_price_denominator = self.amount.denominator();
if limit_uint128 * gas_price_numerator > amount * gas_price_denominator {
amount += Uint128::new(1);
}
assert!(amount.u128() <= u64::MAX as u128);
Coin {
denom: self
.denom
.parse()
.expect("the gas price has been created with invalid denom"),
amount: (amount.u128() as u64).into(),
}
}
}
impl FromStr for GasPrice {
type Err = NyxdError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let possible_amount = s
.chars()
.take_while(|c| c.is_ascii_digit() || c == &'.')
.collect::<String>();
let amount_len = possible_amount.len();
let amount = possible_amount
.parse()
.map_err(|_| NyxdError::MalformedGasPrice)?;
let possible_denom = s.chars().skip(amount_len).collect::<String>();
let denom = possible_denom.trim().to_string();
Ok(GasPrice { amount, denom })
}
}
impl<'a> TryFrom<&'a NymNetworkDetails> for GasPrice {
type Error = NyxdError;
fn try_from(value: &'a NymNetworkDetails) -> Result<Self, Self::Error> {
format!(
"{}{}",
value.default_gas_price_amount(),
value.chain_details.mix_denom.base
)
.parse()
}
}
impl GasPrice {
pub fn new_with_default_price(denom: &str) -> Result<Self, NyxdError> {
format!("{}{}", defaults::GAS_PRICE_AMOUNT, denom).parse()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_gas_price_is_valid() {
let denom = "unym";
GasPrice::new_with_default_price(denom).unwrap();
}
#[test]
fn gas_price_parsing() {
assert_eq!(
GasPrice {
amount: "0.025".parse().unwrap(),
denom: "upunk".parse().unwrap()
},
"0.025upunk".parse().unwrap()
);
assert_eq!(
GasPrice {
amount: "123".parse().unwrap(),
denom: "upunk".parse().unwrap()
},
"123upunk".parse().unwrap()
);
assert!(".25upunk".parse::<GasPrice>().is_err());
assert_eq!(
"0.025upunk".parse::<GasPrice>().unwrap(),
"0.025 upunk".parse().unwrap()
);
let gas: GasPrice = "0.025 upunk ".parse().unwrap();
assert_eq!("upunk", gas.denom);
}
#[test]
fn gas_limit_multiplication() {
let gas_price: GasPrice = "0.025upunk".parse().unwrap();
let gas_limit: Gas = 157500u64;
let fee = &gas_price * gas_limit;
assert_eq!(fee.amount, 3938u128);
}
}