1use alloc::string::String;
8use alloc::vec::Vec;
9
10use bytecheck::CheckBytes;
11use rkyv::ser::serializers::{
12 BufferScratch, BufferSerializer, CompositeSerializer,
13};
14use rkyv::{Archive, Deserialize, Serialize};
15
16use crate::SCRATCH_BUF_BYTES;
17
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct Event {
22 pub source: ContractId,
23 pub topic: String,
24 #[cfg_attr(
25 feature = "serde",
26 serde(with = "serde_with::As::<serde_with::hex::Hex>")
27 )]
28 pub data: Vec<u8>,
29 #[cfg_attr(feature = "serde", serde(default))]
30 pub reverted: bool,
31}
32
33pub type StandardBufSerializer<'a> = CompositeSerializer<
35 BufferSerializer<&'a mut [u8]>,
36 BufferScratch<&'a mut [u8; SCRATCH_BUF_BYTES]>,
37>;
38
39pub const CONTRACT_ID_BYTES: usize = 32;
41
42#[derive(
44 PartialEq,
45 Eq,
46 Archive,
47 Serialize,
48 CheckBytes,
49 Deserialize,
50 PartialOrd,
51 Ord,
52 Hash,
53 Clone,
54 Copy,
55)]
56#[archive(as = "Self")]
57#[repr(C)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub struct ContractId(
60 #[cfg_attr(
61 feature = "serde",
62 serde(with = "serde_with::As::<serde_with::hex::Hex>")
63 )]
64 [u8; CONTRACT_ID_BYTES],
65);
66
67impl ContractId {
68 pub const fn from_bytes(bytes: [u8; CONTRACT_ID_BYTES]) -> Self {
70 Self(bytes)
71 }
72
73 pub const fn to_bytes(self) -> [u8; CONTRACT_ID_BYTES] {
75 self.0
76 }
77
78 pub fn as_bytes(&self) -> &[u8] {
81 &self.0
82 }
83
84 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
87 &mut self.0
88 }
89}
90
91impl From<[u8; CONTRACT_ID_BYTES]> for ContractId {
92 fn from(bytes: [u8; CONTRACT_ID_BYTES]) -> Self {
93 Self::from_bytes(bytes)
94 }
95}
96
97impl AsRef<[u8]> for ContractId {
98 fn as_ref(&self) -> &[u8] {
99 self.as_bytes()
100 }
101}
102
103impl AsMut<[u8]> for ContractId {
104 fn as_mut(&mut self) -> &mut [u8] {
105 self.as_bytes_mut()
106 }
107}
108
109impl PartialEq<[u8; CONTRACT_ID_BYTES]> for ContractId {
110 fn eq(&self, other: &[u8; CONTRACT_ID_BYTES]) -> bool {
111 self.0.eq(other)
112 }
113}
114
115impl core::fmt::Debug for ContractId {
119 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
120 core::fmt::Display::fmt(self, f)
121 }
122}
123
124impl core::fmt::Display for ContractId {
130 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
131 if f.alternate() {
132 write!(f, "0x")?
133 }
134 for byte in self.0 {
135 write!(f, "{:02x}", &byte)?
136 }
137 Ok(())
138 }
139}
140
141impl TryFrom<String> for ContractId {
142 type Error = core::fmt::Error;
143
144 fn try_from(value: String) -> core::result::Result<Self, Self::Error> {
148 let value = value.trim_start_matches("0x");
149 let decoded = hex::decode(value).map_err(|_| core::fmt::Error)?;
150 let bytes: [u8; CONTRACT_ID_BYTES] =
151 decoded.try_into().map_err(|_| core::fmt::Error)?;
152
153 Ok(ContractId::from_bytes(bytes))
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use alloc::format;
160 use alloc::string::ToString;
161
162 use rand::rngs::StdRng;
163 use rand::{Rng, SeedableRng};
164
165 use super::*;
166
167 const CONTRACT_ID_STR: &str =
168 "0000000000000000000000000000000000000000000000000000000000000000";
169 const CONTRACT_ID_STR_PRETTY: &str =
170 "0x0000000000000000000000000000000000000000000000000000000000000000";
171
172 #[test]
173 fn contract_id_display() {
174 let contract_id = ContractId::from_bytes([0u8; CONTRACT_ID_BYTES]);
175 assert_eq!(format!("{}", contract_id), CONTRACT_ID_STR);
176
177 let contract_id = ContractId::from_bytes([0u8; CONTRACT_ID_BYTES]);
178 assert_eq!(format!("{:#?}", contract_id), CONTRACT_ID_STR_PRETTY);
179 }
180
181 #[test]
182 fn contract_id_debug() {
183 let contract_id = ContractId::from_bytes([0u8; CONTRACT_ID_BYTES]);
184 assert_eq!(format!("{}", contract_id), CONTRACT_ID_STR);
185 }
186
187 #[test]
188 fn contract_id_to_from_string() {
189 let mut rng = StdRng::seed_from_u64(1618);
190 let contract_id = ContractId::from_bytes(rng.r#gen());
191
192 let string = contract_id.to_string();
193
194 assert_eq!(string.starts_with("0x"), false);
195 assert_eq!(string.len(), CONTRACT_ID_BYTES * 2);
196
197 let contract_id_from_string = ContractId::try_from(string).unwrap();
198
199 assert_eq!(contract_id, contract_id_from_string);
200 }
201
202 #[test]
203 fn contract_id_try_from_invalid_string() {
204 let too_short = ContractId::try_from("0x".to_string()).is_err();
205
206 let too_long =
207 ContractId::try_from(format!("{}0", CONTRACT_ID_STR)).is_err();
208
209 assert!(too_short);
210 assert!(too_long);
211 }
212}