我们知道,在同以太坊区块链进行交互的编程语言中,javascript是最便捷的语言,没有之一。但是世界上的语言不只一种,我们有时也需要使用其它语言和以太坊区块链交互,例如Rust。为此,Rust中有一个crate叫web3,它其中有个类型U256来对应Solidity中的uint256。但是它却缺少了两个很常用的功能,就是去除精度后转化为人类易读的浮点数和它的反向操作。例如我们需要将值为500000000000000000精度为18的数显示为0.5 等。 在ethers.js中,提供了parseUnits和formatUnits这两个函数进行类似的操作,因此,作为学习Rust语言的一个练习 ,我们也模拟了这两个函数,代码如下: convert.rs
use std::error::Error;
use std::fmt;
use web3::types::U256;
#[derive(Debug)]
pub enum ParseU256Error {
FormatError,
DecimalsError,
}
impl fmt::Display for ParseU256Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParseU256Error::FormatError => write!(f, "Format Error!"),
ParseU256Error::DecimalsError => write!(f, "Decimals Error!"),
}
}
}
impl Error for ParseU256Error {}
pub trait ParseFormat {
type Error;
fn parse_units(source: &'static str, d: u32) -> Result<U256, Self::Error>;
fn format_units(&self, d: u32) -> Result<String, Self::Error>;
}
const ZEROS: &str = "000000000000000000";
impl ParseFormat for U256 {
type Error = ParseU256Error;
fn parse_units(source: &'static str, d: u32) -> Result<U256, ParseU256Error> {
if d > 18 {
return Err(ParseU256Error::DecimalsError);
}
let decimals = U256::exp10(d as usize);
let strs: Vec<&str> = source.split(".").collect();
if strs.len() > 2 {
return Err(ParseU256Error::FormatError);
}
let inter = U256::from_dec_str(strs[0])
.unwrap()
.checked_mul(decimals)
.unwrap();
if strs.len() == 1 {
return Ok(inter);
}
let mut z: String = strs[1].to_string();
let frac = if strs[1].len() >= d as usize {
&strs[1][..d as usize]
} else {
z.push_str(&ZEROS[..d as usize - strs[1].len()]);
&z[..]
};
let frac = U256::from_dec_str(frac).unwrap();
Ok(inter.checked_add(frac).unwrap())
}
fn format_units(&self, d: u32) -> Result<String, ParseU256Error> {
if d > 18 {
return Err(ParseU256Error::DecimalsError);
}
let decimals = U256::exp10(d as usize);
let m = self.checked_rem(decimals).unwrap().to_string();
let mut b = String::from(&ZEROS[..d as usize - m.len()]);
b.push_str(&m[..]);
let b = b.trim_end_matches('0');
let mut inter = self.checked_div(decimals).unwrap().to_string();
if m != "0" {
inter.push('.');
inter.push_str(b);
}
Ok(inter.clone())
}
}
#[test]
fn test_wrap() {
let source = "1.035000000000000000001";
let balance = U256::from(1_035_000_000_000_000_000u64);
let b = U256::parse_units(source, 18).unwrap();
assert_eq!(b, balance);
let s = balance.format_units(18).unwrap();
assert_eq!(s, "1.035");
let source2 = "13897";
let bal2 = U256::from(13_897_000_000_000 as u64);
let b2 = U256::parse_units(source2, 9).unwrap();
assert_eq!(b2, bal2);
let s2 = bal2.format_units(9).unwrap();
assert_eq!(s2, source2);
}
注意,我们这个转换精度不能超过18。
下面我们来看一下具体的操作。 main.rs
use std::str::FromStr;
use web3::types::{Address,U256};
use web3::contract::{Contract, Options};
mod convert;
use convert::ParseFormat;
#[tokio::main]
async fn main() -> web3::contract::Result<()> {
const HTTP_URL: &str = "https://bsc-dataseed4.ninicoin.io";
const SAFE_MOON: &str = "0x8076C74C5e3F5852037F31Ff0093Eeb8c8ADd8D3";
let transport = web3::transports::Http::new(HTTP_URL)?;
let provider = web3::Web3::new(transport);
let safe_addr = Address::from_str(SAFE_MOON).unwrap();
let contract = Contract::from_json(provider.eth(), safe_addr, include_bytes!("../abi2.json")).unwrap();
let result = contract.query("_taxFee",(),None,Options::default(),None);
let tax_fee: U256 = result.await?;
println!("_taxFee:{}",tax_fee);
assert_eq!(tax_fee, 5.into());
let balance = provider.eth().balance(safe_addr, None).await?;
let balance = balance.format_units(18).unwrap();
println!("Balance:{} BNB",balance);
Ok(())
}
在上面的示例代码中,我们演示的了两个功能,第一个查询SafeMoon合约中的_taxFee ,另一个是查询了SafeMoon合约的BNB余额,运行结果如下(结果可能不同):
_taxFee:5
Balance:4957.85048317300265945 BNB
可以看到,大致实现了我们的需求。 上面的convert.rs 并不完善,特别是其中的错误处理很简单,这是下一步优化的方向。
|