Skip to main content

builder_relayer_client_rust/
utils.rs

1use alloy::primitives::{Address, U256};
2
3pub fn sleep_ms(ms: u64) -> tokio::time::Sleep {
4    tokio::time::sleep(std::time::Duration::from_millis(ms))
5}
6
7pub fn keccak256(bytes: &[u8]) -> [u8; 32] {
8    alloy::primitives::keccak256(bytes).into()
9}
10
11pub fn hex_to_address(s: &str) -> Address {
12    s.parse().expect("invalid address")
13}
14
15pub fn u256_from_dec_str(s: &str) -> U256 {
16    U256::from_str_radix(s, 10).expect("invalid u256")
17}
18
19/// Split and repack ECDSA signature like TS splitAndPackSig
20/// input: 0x{r}{s}{v}
21/// output: 0x{r(32)}{s(32)}{v(1)}
22pub fn split_and_pack_sig(sig_hex: &str) -> String {
23    // Mirror TS logic (splitSignature + encodePacked) so relayer validates:
24    // TS maps: 0/1 -> 31/32 and 27/28 -> 31/32 then encodes r,s,v as uint256,uint256,uint8 (decimal bigints)
25    let s = sig_hex.trim_start_matches("0x");
26    assert!(s.len() >= 130, "sig too short");
27    let r_hex = &s[0..64];
28    let s_hex = &s[64..128];
29    let v_hex = &s[128..130];
30    let mut v = u8::from_str_radix(v_hex, 16).expect("v");
31    match v {
32        0 | 1 => v += 31,  // 0/1 -> 31/32
33        27 | 28 => v += 4, // 27/28 -> 31/32
34        _ => {}
35    }
36    // r,s interpreted as uint256 decimal strings in TS. We'll keep hex then convert to U256 -> decimal.
37    let r_u256 = U256::from_str_radix(r_hex, 16).expect("r parse");
38    let s_u256 = U256::from_str_radix(s_hex, 16).expect("s parse");
39    // encodePacked(uint256,uint256,uint8) == left padded 32 bytes for r,s then 1 byte v
40    // Simpler: manually pad r,s as 32-byte big endian.
41    let r_bytes = r_u256.to_be_bytes::<32>();
42    let s_bytes = s_u256.to_be_bytes::<32>();
43    let packed_bytes: Vec<u8> = [r_bytes.as_slice(), s_bytes.as_slice(), &[v]].concat();
44    format!("0x{}", hex::encode(packed_bytes))
45}