Skip to main content

hopper_native/
address.rs

1//! Solana address type -- 32-byte public key.
2
3/// Number of bytes in an address.
4pub const ADDRESS_BYTES: usize = 32;
5
6/// Maximum length of a single PDA seed.
7pub const MAX_SEED_LEN: usize = 32;
8
9/// Maximum number of seeds for PDA derivation.
10pub const MAX_SEEDS: usize = 16;
11
12/// Marker appended to PDA hash inputs: `"ProgramDerivedAddress"`.
13pub const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
14
15/// A Solana address (public key): 32 bytes, transparent layout.
16#[repr(transparent)]
17#[cfg_attr(feature = "copy", derive(Copy))]
18#[derive(Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
19pub struct Address(pub(crate) [u8; 32]);
20
21impl Address {
22    /// Construct from a raw byte array.
23    #[inline(always)]
24    pub const fn new_from_array(bytes: [u8; 32]) -> Self {
25        Self(bytes)
26    }
27
28    /// Return the underlying bytes by value.
29    #[inline(always)]
30    pub const fn to_bytes(&self) -> [u8; 32] {
31        self.0
32    }
33
34    /// Borrow the underlying byte array.
35    #[inline(always)]
36    pub const fn as_array(&self) -> &[u8; 32] {
37        &self.0
38    }
39}
40
41impl From<[u8; 32]> for Address {
42    #[inline(always)]
43    fn from(bytes: [u8; 32]) -> Self {
44        Self(bytes)
45    }
46}
47
48impl From<Address> for [u8; 32] {
49    #[inline(always)]
50    fn from(addr: Address) -> [u8; 32] {
51        addr.0
52    }
53}
54
55impl TryFrom<&[u8]> for Address {
56    type Error = core::array::TryFromSliceError;
57
58    #[inline]
59    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
60        let arr: [u8; 32] = slice.try_into()?;
61        Ok(Self(arr))
62    }
63}
64
65impl AsRef<[u8]> for Address {
66    #[inline(always)]
67    fn as_ref(&self) -> &[u8] {
68        &self.0
69    }
70}
71
72impl AsMut<[u8]> for Address {
73    #[inline(always)]
74    fn as_mut(&mut self) -> &mut [u8] {
75        &mut self.0
76    }
77}
78
79impl AsRef<[u8; 32]> for Address {
80    #[inline(always)]
81    fn as_ref(&self) -> &[u8; 32] {
82        &self.0
83    }
84}
85
86impl core::hash::Hash for Address {
87    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
88        self.0.hash(state);
89    }
90}
91
92impl core::fmt::Debug for Address {
93    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
94        write!(f, "Address({:?})", &self.0[..4])
95    }
96}
97
98/// Fast address equality using 4 x u64 comparison.
99#[inline(always)]
100pub fn address_eq(a: &Address, b: &Address) -> bool {
101    let a_ptr = a.0.as_ptr() as *const u64;
102    let b_ptr = b.0.as_ptr() as *const u64;
103    // SAFETY: Address is 32 bytes = 4 x u64. The #[repr(transparent)]
104    // layout guarantees the bytes are contiguous. We compare as u64
105    // for fewer instructions.
106    unsafe {
107        *a_ptr == *b_ptr
108            && *a_ptr.add(1) == *b_ptr.add(1)
109            && *a_ptr.add(2) == *b_ptr.add(2)
110            && *a_ptr.add(3) == *b_ptr.add(3)
111    }
112}
113
114/// Compile-time base58 address literal.
115///
116/// Usage: `const MY_ADDR: Address = address!("11111111111111111111111111111111");`
117#[macro_export]
118macro_rules! address {
119    ( $literal:expr ) => {
120        $crate::address::Address::new_from_array(five8_const::decode_32_const($literal))
121    };
122}