verbs_rs/
utils.rs

1//! Numerical and Ethereum utilities
2
3use alloy_primitives::{Address, U256};
4use revm::primitives::bitvec::macros::internal::funty::Fundamental;
5
6/// Create a revm address from a hex string.
7///
8/// # Arguments
9///
10/// * `hx` - Hex-string, should be prefixed with `0x`.
11///
12pub fn address_from_hex(hx: &str) -> Address {
13    let address = hx
14        .strip_prefix("0x")
15        .expect("Addresses require '0x' prefix");
16    let address = hex::decode(address).expect("Decoding hex string failed");
17    Address::from_slice(address.as_slice())
18}
19
20/// Create a Bytes object from a hex string.
21///
22/// # Arguments
23///
24/// * `hx` - Hex string.
25///
26pub fn data_bytes_from_hex(hx: &str) -> Vec<u8> {
27    hex::decode(hx).expect("Decoding hex failed")
28}
29
30/// Convert values in in eth to weth
31pub trait Eth {
32    fn to_weth(x: u128) -> Self;
33}
34
35/// Convert revm u256 from eth to weth value
36impl Eth for U256 {
37    fn to_weth(x: u128) -> Self {
38        let x: u128 = x * 10u128.pow(18);
39        Self::from(x)
40    }
41}
42
43/// Scale a U256 value representing a fixed number
44/// of decimals into a value that fits a f64 (discarding some precision)
45///
46/// # Arguments
47///
48/// * `x` - U256 value
49/// * `decimals` - Number of decimals represented by `x`
50/// * `precision` - Desired precision of the output
51///
52pub fn scale_data_value(x: U256, decimals: usize, precision: usize) -> f64 {
53    let s1 = U256::from(decimals - precision);
54    let x = x / U256::from(10).pow(s1);
55    // This will check for overflow
56    //let x = x.as_u64();
57    let x: u64 = x
58        .clamp(U256::ZERO, U256::from(u64::MAX))
59        .try_into()
60        .unwrap();
61    x.as_f64() / 10f64.powi(precision.as_i32())
62}
63
64/// Clamp a u256 to the u128 range and cast
65///
66/// # Arguments
67///
68/// * `x` - u256 value
69///
70pub fn clamp_u256_to_u128(x: U256) -> u128 {
71    x.clamp(U256::ZERO, U256::from(u128::MAX))
72        .try_into()
73        .unwrap()
74}
75
76/// Divide two u256 values returning a f64
77///
78/// # Arguments
79///
80/// * `x` - u256 value
81/// * `y` - u256 value
82/// * `precision` - decimal precision of the result
83///
84pub fn div_u256(x: U256, y: U256, precision: i32) -> f64 {
85    let z = x * U256::from(10).pow(U256::from(precision)) / y;
86    let z: u64 = z
87        .clamp(U256::ZERO, U256::from(u64::MAX))
88        .try_into()
89        .unwrap();
90    z.as_f64() / 10f64.powi(precision)
91}
92
93/// Append contract bytecode and encoded arguments for use in deployment
94///
95/// # Arguments
96///
97/// * `bytecode_hex` - &str Contract bytecode hex
98/// * `args` - `Option<Vec<u8>>` ABI encoded constructor arguments
99///
100pub fn constructor_data(bytecode_hex: &str, mut args: Option<Vec<u8>>) -> Vec<u8> {
101    let mut bytecode: Vec<u8> = data_bytes_from_hex(bytecode_hex);
102    match &mut args {
103        Some(a) => bytecode.append(a),
104        None => {}
105    }
106    bytecode
107}
108
109#[cfg(test)]
110mod tests {
111
112    use super::*;
113    use assert_approx_eq::assert_approx_eq;
114    use rstest::rstest;
115
116    #[rstest]
117    #[case(5, 17, 0.5)]
118    #[case(1, 15, 0.001)]
119    #[case(15, 17, 1.5)]
120    #[case(1000000005, 12, 1000.000005)]
121    fn scaling_values(#[case] a: u128, #[case] exp: u128, #[case] expected: f64) {
122        let x = U256::from(a) * U256::from(10).pow(U256::from(exp));
123        let y = scale_data_value(x, 18, 6);
124        assert_approx_eq!(y, expected);
125    }
126
127    #[test]
128    fn scaling_out_of_bounds() {
129        // Test if value is too large we continue
130        let x = U256::MAX;
131        let y = scale_data_value(x, 4, 0);
132        assert_approx_eq!(y, u64::MAX.as_f64());
133    }
134
135    #[rstest]
136    #[case(10, 5, 15, 15, 2.0, 0.5)]
137    #[case(1, 1, 20, 16, 10000.0, 0.0001)]
138    fn dividing_u256(
139        #[case] a: u128,
140        #[case] b: u128,
141        #[case] exp_a: u128,
142        #[case] exp_b: u128,
143        #[case] expected_a: f64,
144        #[case] expected_b: f64,
145    ) {
146        let x = U256::from(a) * U256::from(10).pow(U256::from(exp_a));
147        let y = U256::from(b) * U256::from(10).pow(U256::from(exp_b));
148
149        let z = div_u256(x, y, 6);
150        assert_approx_eq!(z, expected_a);
151
152        let z = div_u256(y, x, 6);
153        assert_approx_eq!(z, expected_b);
154    }
155
156    #[test]
157    fn div_out_of_bounds() {
158        let x = U256::from(10).pow(U256::from(30));
159        let y = U256::from(1);
160        let z = div_u256(x, y, 0);
161        assert_approx_eq!(z, u64::MAX.as_f64())
162    }
163}