1use std::collections::HashMap;
2use std::error::Error;
3
4use alloy::primitives::hex::FromHex;
5use alloy::primitives::{Bytes, B256};
6use base64::prelude::BASE64_STANDARD_NO_PAD;
7use base64::Engine;
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9use serde_either::SingleOrVec;
10
11use crate::global::CALLDATA_LIMIT;
12use crate::types::{AddressED, B256ED};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct EthCall {
17 pub from: Option<AddressED>,
19 pub to: Option<AddressED>,
21 #[serde(alias = "input", alias = "data")]
23 pub data: Option<RawBytes>,
24}
25
26impl EthCall {
27 pub fn new(from: Option<AddressED>, to: Option<AddressED>, data: RawBytes) -> Self {
29 Self {
30 from,
31 to,
32 data: Some(data),
33 }
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct PrecompileData {
40 #[serde(rename = "opReturnTxIds")]
42 pub op_return_tx_ids: Vec<B256ED>,
43 #[serde(rename = "bitcoinTxHexes")]
45 pub bitcoin_tx_hexes: HashMap<B256ED, RawBytes>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct GetLogsFilter {
51 #[serde(rename = "fromBlock")]
52 pub from_block: Option<String>,
54 #[serde(rename = "toBlock")]
55 pub to_block: Option<String>,
57 pub address: Option<AddressED>,
59 pub topics: Option<Vec<SingleOrVec<Option<B256ED>>>>,
61}
62
63impl GetLogsFilter {
64 pub(crate) fn topics_as_b256(&self) -> Option<Vec<SingleOrVec<Option<B256>>>> {
66 self.topics.as_ref().map(|topics| {
67 topics
68 .iter()
69 .map(|topic| match topic {
70 SingleOrVec::Single(t) => SingleOrVec::Single(t.clone().map(|t| t.bytes)),
71 SingleOrVec::Vec(ts) => SingleOrVec::Vec(
72 ts.iter()
73 .map(|t| t.clone().map(|t| t.bytes))
74 .collect::<Vec<Option<B256>>>(),
75 ),
76 })
77 .collect()
78 })
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct Base64Bytes(Option<String>);
88
89impl Base64Bytes {
90 pub fn new(inner: String) -> Self {
92 Self(Some(inner))
93 }
94
95 pub fn to_string(&self) -> String {
97 self.0.clone().unwrap_or_default()
98 }
99
100 pub fn from_bytes(bytes: Bytes) -> Result<Self, Box<dyn Error>> {
105 let mut data = vec![];
106 let nada_encoded = nada::encode(bytes.clone());
107 let mut zstd_compressed = vec![0; CALLDATA_LIMIT];
108 let zstd_length =
109 zstd_safe::compress(zstd_compressed.as_mut_slice(), bytes.iter().as_slice(), 22)
110 .map_err(|e| format!("Failed to compress with zstd: {}", e))?;
111 if bytes.len() < nada_encoded.len() && bytes.len() < zstd_length {
116 data.insert(0, 0x00);
117 data.extend_from_slice(&bytes);
118 } else if nada_encoded.len() < zstd_length {
119 data.insert(0, 0x01);
120 data.extend_from_slice(&nada_encoded);
121 } else {
122 data.insert(0, 0x02);
123 data.extend_from_slice(&zstd_compressed[..zstd_length]);
124 }
125 let base64_encoded = BASE64_STANDARD_NO_PAD.encode(data);
126 Ok(Self(Some(base64_encoded)))
127 }
128
129 pub fn empty() -> Self {
131 Self(None)
132 }
133
134 #[cfg(feature = "server")]
136 pub(crate) fn value(&self) -> Option<Bytes> {
137 self.0
138 .as_ref()
139 .and_then(|s| decode_bytes_from_inscription_data(s))
140 }
141}
142
143impl Serialize for Base64Bytes {
144 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
145 where
146 S: Serializer,
147 {
148 if let Some(inner) = &self.0 {
149 serializer.serialize_str(inner)
150 } else {
151 serializer.serialize_none()
152 }
153 }
154}
155
156impl<'de> Deserialize<'de> for Base64Bytes {
157 fn deserialize<D>(deserializer: D) -> Result<Base64Bytes, D::Error>
158 where
159 D: Deserializer<'de>,
160 {
161 let Ok(s) = String::deserialize(deserializer) else {
162 return Ok(Base64Bytes::empty());
163 };
164 Ok(Base64Bytes::new(s))
165 }
166}
167
168#[cfg(feature = "server")]
169pub fn select_bytes(
170 raw_bytes: &Option<RawBytes>,
171 base64_bytes: &Option<Base64Bytes>,
172) -> Result<Option<Bytes>, Box<dyn Error>> {
173 match (raw_bytes, base64_bytes) {
174 (Some(raw), None) => Ok(raw.value()),
175 (None, Some(encoded)) => Ok(encoded.value()),
176 (None, None) => Err("Both raw_bytes and base64_bytes are None".into()),
177 (Some(_), Some(_)) => {
178 Err("Both raw_bytes and base64_bytes are provided, only one should be used".into())
179 }
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, Eq)]
184pub struct RawBytes(Option<String>);
191
192impl RawBytes {
193 pub fn new(inner: String) -> Self {
195 Self(Some(inner))
196 }
197
198 pub fn to_string(&self) -> String {
200 self.0.clone().unwrap_or_default()
201 }
202
203 pub fn from_bytes(bytes: Bytes) -> Self {
207 Self(Some(format!("0x{}", hex::encode(bytes))))
208 }
209
210 pub fn empty() -> Self {
212 Self(None)
213 }
214
215 pub(crate) fn value(&self) -> Option<Bytes> {
216 self.0.as_ref().and_then(|s| Bytes::from_hex(s).ok())
217 }
218}
219
220impl Serialize for RawBytes {
221 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
222 where
223 S: Serializer,
224 {
225 if let Some(inner) = &self.0 {
226 serializer.serialize_str(inner)
227 } else {
228 serializer.serialize_none()
229 }
230 }
231}
232
233impl<'de> Deserialize<'de> for RawBytes {
234 fn deserialize<D>(deserializer: D) -> Result<RawBytes, D::Error>
235 where
236 D: Deserializer<'de>,
237 {
238 let Ok(s) = String::deserialize(deserializer) else {
239 return Ok(RawBytes::empty());
240 };
241 Ok(RawBytes::new(s))
242 }
243}
244
245#[cfg(feature = "server")]
246pub fn decode_bytes_from_inscription_data(mut inscription_data: &str) -> Option<Bytes> {
247 if let Some((base64, _)) = inscription_data.split_once('=') {
248 inscription_data = base64;
250 }
251 let base64_decoded = BASE64_STANDARD_NO_PAD.decode(inscription_data).ok()?;
252 match base64_decoded[0] {
257 0x00 => {
258 if base64_decoded.len() > CALLDATA_LIMIT {
260 None
261 } else {
262 Some(Bytes::from(base64_decoded[1..].to_vec()))
263 }
264 }
265 0x01 => {
266 nada::decode_with_limit(base64_decoded[1..].iter().cloned(), CALLDATA_LIMIT)
268 .ok()
269 .map(Bytes::from)
270 }
271 0x02 => {
272 match zstd_safe::get_frame_content_size(&base64_decoded[1..]) {
274 Ok(Some(size)) => {
275 if size > CALLDATA_LIMIT as u64 {
277 return None;
278 }
279 }
280 Ok(None) => {
281 }
284 Err(_) => {
285 return None;
286 }
287 }
288 decode_zstd_into_bytes(&base64_decoded[1..])
290 }
291 _ => {
292 None
294 }
295 }
296}
297
298#[cfg(feature = "server")]
299fn decode_zstd_into_bytes(data: &[u8]) -> Option<Bytes> {
300 let mut decompressed = vec![0u8; CALLDATA_LIMIT]; if let Ok(length) = zstd_safe::decompress(decompressed.as_mut_slice(), &data) {
302 if length > CALLDATA_LIMIT {
303 None
304 } else {
305 let mut decompressed = decompressed.to_vec();
306 decompressed.truncate(length);
307 Some(Bytes::from(decompressed))
308 }
309 } else {
310 None
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use alloy::primitives::U256;
317 use alloy::sol_types::{sol, SolCall};
318 use base64::prelude::BASE64_STANDARD_NO_PAD;
319
320 use super::*;
321
322 sol! {
323 function transfer(address receiver, bytes ticker, uint256 amount);
324 }
325
326 #[test]
327 fn test_calldata_base64_roundtrip() {
328 let address: &str = "0xdead09C7d1621C9D49EdD5c070933b500ac5beef";
329 let ticker = vec![0x6f, 0x72, 0x64, 0x69];
330 let amount = 42;
331 let data = Bytes::from(
332 transferCall::new((
333 address.parse().unwrap(),
334 Bytes::from(ticker),
335 U256::from(amount),
336 ))
337 .abi_encode(),
338 );
339
340 let encoded = Base64Bytes::from_bytes(data.clone()).unwrap();
341
342 let decoded = encoded.value().unwrap();
343
344 assert_eq!(decoded, data);
345 }
346
347 #[test]
348 fn test_decode_bytes_from_base64_data_uncompressed() {
349 let data = vec![0x00, 0xde, 0xad, 0xbe, 0xef, 0xff];
351 let base64_encoded = BASE64_STANDARD_NO_PAD.encode(data);
352 let result = decode_bytes_from_inscription_data(&base64_encoded);
353 assert_eq!(
354 result,
355 Some(Bytes::from(vec![0xde, 0xad, 0xbe, 0xef, 0xff]))
356 );
357 }
358
359 #[test]
360 fn test_decode_bytes_from_base64_data_extra_equals() {
361 let data = vec![0x00, 0xde, 0xad, 0xbe, 0xef, 0xff];
363 let base64_encoded = BASE64_STANDARD_NO_PAD.encode(data);
364
365 let base64_encoded = format!("{}====", base64_encoded);
367 let result = decode_bytes_from_inscription_data(&base64_encoded);
368 assert_eq!(
369 result,
370 Some(Bytes::from(vec![0xde, 0xad, 0xbe, 0xef, 0xff]))
371 );
372 }
373
374 #[test]
375 fn test_decode_bytes_from_base64_data_nada() {
376 let data = vec![
377 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
378 ];
379 let mut nada_encoded = nada::encode(data.clone());
380 nada_encoded.insert(0, 0x01);
382 assert_eq!(
383 nada_encoded,
384 vec![0x01, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0xff, 0x01, 0xff, 0x04, 0xff, 0x02]
385 );
386
387 let base64_encoded = BASE64_STANDARD_NO_PAD.encode(nada_encoded);
388 let result = decode_bytes_from_inscription_data(&base64_encoded);
389 assert_eq!(result, Some(Bytes::from(data)));
390 }
391
392 #[test]
393 fn test_decode_bytes_from_base64_data_repetition_zstd() {
394 let data = vec![0xde, 0xad, 0xbe, 0xef].repeat(4096);
396 let mut compressed = [0u8; 1024 * 1024];
397 let length = zstd_safe::compress(&mut compressed, data.as_slice(), 22).unwrap();
398 let mut compressed_vec = compressed.to_vec();
400 compressed_vec.truncate(length);
401 compressed_vec.insert(0, 0x02);
402 let base64_encoded = BASE64_STANDARD_NO_PAD.encode(compressed_vec);
403 let result = decode_bytes_from_inscription_data(&base64_encoded);
404 assert_eq!(result, Some(Bytes::from(data)));
405 }
406
407 #[test]
408 fn test_decode_bytes_from_base64_data_random_zstd() {
409 let mut data = vec![0; 32 * 1024];
412 for i in 0..data.len() {
413 data[i] = rand::random();
414 }
415 let mut compressed = [0u8; 1024 * 1024];
416 let length = zstd_safe::compress(&mut compressed, data.as_slice(), 22).unwrap();
417 let mut compressed_vec = compressed.to_vec();
419 compressed_vec.truncate(length);
420 compressed_vec.insert(0, 0x02);
421 let base64_encoded = BASE64_STANDARD_NO_PAD.encode(compressed_vec);
422 let result = decode_bytes_from_inscription_data(&base64_encoded);
423 assert_eq!(result, Some(Bytes::from(data)));
424 }
425
426 #[test]
427 fn test_decode_bytes_from_base64_data_huge_zstd() {
428 let data = vec![0xde, 0xad, 0xbe, 0xef].repeat(512 * 1024);
430 let mut compressed = [0u8; 1024 * 1024];
431 let length = zstd_safe::compress(&mut compressed, data.as_slice(), 22).unwrap();
432 let mut compressed_vec = compressed.to_vec();
434 compressed_vec.truncate(length);
435 compressed_vec.insert(0, 0x02);
436 let base64_encoded = BASE64_STANDARD_NO_PAD.encode(compressed_vec);
438 let result = decode_bytes_from_inscription_data(&base64_encoded);
439 assert_eq!(result, None);
440 }
441
442 #[test]
443 fn test_invalid_zstd() {
444 let data = vec![0x02, 0xde, 0xad, 0xbe, 0xef];
445 let base64_encoded = BASE64_STANDARD_NO_PAD.encode(data);
446 let result = decode_bytes_from_inscription_data(&base64_encoded);
447 assert_eq!(result, None);
448 }
449
450 #[test]
451 fn test_invalid_base64() {
452 let invalid_base64 = "invalid_base64";
453 let result = decode_bytes_from_inscription_data(&invalid_base64.to_string());
454 assert_eq!(result, None);
455 }
456}