Skip to main content

unified_bridge/
token_info.rs

1use agglayer_primitives::{address, Address};
2use agglayer_tries::proof::ToBits;
3use serde::{Deserialize, Serialize};
4
5use crate::NetworkId;
6
7pub const L1_ETH: TokenInfo = TokenInfo {
8    origin_network: NetworkId::ETH_L1,
9    origin_token_address: address!("0000000000000000000000000000000000000000"),
10};
11
12/// Encapsulates the information to uniquely identify a token on the origin
13/// network.
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Copy)]
15#[cfg_attr(feature = "testutils", derive(arbitrary::Arbitrary))]
16pub struct TokenInfo {
17    /// Network which the token originates from
18    pub origin_network: NetworkId,
19
20    /// The address of the token on the origin network
21    pub origin_token_address: Address,
22}
23
24#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
25#[cfg_attr(feature = "testutils", derive(arbitrary::Arbitrary))]
26pub enum LeafType {
27    Transfer = 0,
28    Message = 1,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
32#[error("Invalid leaf type number")]
33pub struct LeafTypeFromU8Error;
34
35impl TryFrom<u8> for LeafType {
36    type Error = LeafTypeFromU8Error;
37
38    #[inline]
39    fn try_from(value: u8) -> Result<Self, Self::Error> {
40        match value {
41            0 => Ok(Self::Transfer),
42            1 => Ok(Self::Message),
43            _ => Err(LeafTypeFromU8Error),
44        }
45    }
46}
47
48impl ToBits<192> for TokenInfo {
49    #[inline]
50    fn to_bits(&self) -> [bool; 192] {
51        let address_bytes = self.origin_token_address.as_slice();
52        // Security: We assume here that `address_bytes` is a fixed-size array of
53        // 20 bytes. The following code could panic otherwise.
54        std::array::from_fn(|i| {
55            if i < 32 {
56                (self.origin_network.to_u32() >> i) & 1 == 1
57            } else {
58                ((address_bytes[(i - 32) / 8]) >> (i % 8)) & 1 == 1
59            }
60        })
61    }
62}
63
64impl TokenInfo {
65    /// Reconstructs a [`TokenInfo`] from its bit representation (the SMT path)
66    pub fn from_bits(bits: &[bool; 192]) -> Self {
67        // reconstruct the NetworkId from the bits 0..32
68        let mut network_id_u32 = 0u32;
69        for (i, &bit) in bits[0..32].iter().enumerate() {
70            if bit {
71                network_id_u32 |= 1 << i;
72            }
73        }
74
75        // reconstruct the Address from the bits 32..192
76        let mut address_bytes = [0u8; 20];
77        for (i, &bit) in bits[32..192].iter().enumerate() {
78            if bit {
79                let byte_index = i / 8;
80                let bit_offset = i % 8;
81                address_bytes[byte_index] |= 1 << bit_offset;
82            }
83        }
84
85        TokenInfo {
86            origin_network: NetworkId::from(network_id_u32),
87            origin_token_address: Address::from(address_bytes),
88        }
89    }
90}
91
92#[test]
93fn test_token_info_round_trip() {
94    let initial = TokenInfo {
95        origin_network: NetworkId::from(1234),
96        origin_token_address: Address::from([0xab; 20]),
97    };
98
99    assert_eq!(initial, TokenInfo::from_bits(&initial.to_bits()));
100}