linera_ethereum/
common.rs1use std::num::ParseIntError;
8
9#[cfg(not(target_arch = "wasm32"))]
10use alloy::rpc::json_rpc;
11use alloy::rpc::types::eth::Log;
12use alloy_primitives::{Address, B256, U256};
13use num_bigint::{BigInt, BigUint};
14use num_traits::cast::ToPrimitive;
15use serde::{Deserialize, Serialize};
16use thiserror::Error;
17
18#[derive(Error, Debug)]
20pub enum EthereumQueryError {
21 #[error("the ID should be matching")]
23 IdIsNotMatching,
24
25 #[error("wrong JSON-RPC version")]
27 WrongJsonRpcVersion,
28}
29
30#[derive(Debug, Error)]
32pub enum EthereumServiceError {
33 #[error(transparent)]
35 EthereumQueryError(#[from] EthereumQueryError),
36
37 #[error(transparent)]
39 ParseIntError(#[from] ParseIntError),
40
41 #[error("Unsupported Ethereum type")]
43 UnsupportedEthereumTypeError,
44
45 #[error("Event parsing error")]
47 EventParsingError,
48
49 #[error(transparent)]
51 ParseBigIntError(#[from] num_bigint::ParseBigIntError),
52
53 #[error("Ethereum parsing error")]
55 EthereumParsingError,
56
57 #[error("Parse bool error")]
59 ParseBoolError,
60
61 #[error(transparent)]
63 FromHexError(#[from] alloy_primitives::hex::FromHexError),
64
65 #[error("Block not found on chain")]
67 BlockNotFound,
68
69 #[error(transparent)]
71 JsonError(#[from] serde_json::Error),
72
73 #[error(transparent)]
75 #[cfg(not(target_arch = "wasm32"))]
76 RpcError(#[from] json_rpc::RpcError<alloy::transports::TransportErrorKind>),
77
78 #[error(transparent)]
80 #[cfg(not(target_arch = "wasm32"))]
81 UrlParseError(#[from] url::ParseError),
82
83 #[error(transparent)]
85 #[cfg(not(target_arch = "wasm32"))]
86 AlloyReqwestError(#[from] alloy::transports::http::reqwest::Error),
87}
88
89#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
92pub enum EthereumDataType {
93 Address(String),
95 Uint256(U256),
97 Uint64(u64),
99 Int64(i64),
101 Uint32(u32),
103 Int32(i32),
105 Uint16(u16),
107 Int16(i16),
109 Uint8(u8),
111 Int8(i8),
113 Bool(bool),
115}
116
117pub fn event_name_from_expanded(event_name_expanded: &str) -> String {
121 event_name_expanded.replace(" indexed", "").to_string()
122}
123
124fn parse_entry(entry: B256, ethereum_type: &str) -> Result<EthereumDataType, EthereumServiceError> {
125 if ethereum_type == "address" {
126 let address = Address::from_word(entry);
127 let address = format!("{address:?}");
128 return Ok(EthereumDataType::Address(address));
129 }
130 if ethereum_type == "uint256" {
131 let entry = U256::from_be_bytes(entry.0);
132 return Ok(EthereumDataType::Uint256(entry));
133 }
134 if ethereum_type == "uint64" {
135 let entry = BigUint::from_bytes_be(&entry.0);
136 let entry = entry.to_u64().unwrap();
137 return Ok(EthereumDataType::Uint64(entry));
138 }
139 if ethereum_type == "int64" {
140 let entry = BigInt::from_signed_bytes_be(&entry.0);
141 let entry = entry.to_i64().unwrap();
142 return Ok(EthereumDataType::Int64(entry));
143 }
144 if ethereum_type == "uint32" {
145 let entry = BigUint::from_bytes_be(&entry.0);
146 let entry = entry.to_u32().unwrap();
147 return Ok(EthereumDataType::Uint32(entry));
148 }
149 if ethereum_type == "int32" {
150 let entry = BigInt::from_signed_bytes_be(&entry.0);
151 let entry = entry.to_i32().unwrap();
152 return Ok(EthereumDataType::Int32(entry));
153 }
154 if ethereum_type == "uint16" {
155 let entry = BigUint::from_bytes_be(&entry.0);
156 let entry = entry.to_u16().unwrap();
157 return Ok(EthereumDataType::Uint16(entry));
158 }
159 if ethereum_type == "int16" {
160 let entry = BigInt::from_signed_bytes_be(&entry.0);
161 let entry = entry.to_i16().unwrap();
162 return Ok(EthereumDataType::Int16(entry));
163 }
164 if ethereum_type == "uint8" {
165 let entry = BigUint::from_bytes_be(&entry.0);
166 let entry = entry.to_u8().unwrap();
167 return Ok(EthereumDataType::Uint8(entry));
168 }
169 if ethereum_type == "int8" {
170 let entry = BigInt::from_signed_bytes_be(&entry.0);
171 let entry = entry.to_i8().unwrap();
172 return Ok(EthereumDataType::Int8(entry));
173 }
174 if ethereum_type == "bool" {
175 let entry = BigUint::from_bytes_be(&entry.0);
176 let entry = entry.to_u8().unwrap();
177 let entry = match entry {
178 1 => true,
179 0 => false,
180 _ => {
181 return Err(EthereumServiceError::ParseBoolError);
182 }
183 };
184 return Ok(EthereumDataType::Bool(entry));
185 }
186 Err(EthereumServiceError::UnsupportedEthereumTypeError)
187}
188
189#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
191pub struct EthereumEvent {
192 pub values: Vec<EthereumDataType>,
194 pub block_number: u64,
196}
197
198fn get_inner_event_type(event_name_expanded: &str) -> Result<String, EthereumServiceError> {
199 if let Some(opening_paren_index) = event_name_expanded.find('(') {
200 if let Some(closing_paren_index) = event_name_expanded.find(')') {
201 let inner_types = &event_name_expanded[opening_paren_index + 1..closing_paren_index];
203 return Ok(inner_types.to_string());
204 }
205 }
206 Err(EthereumServiceError::EventParsingError)
207}
208
209pub fn parse_log(
212 event_name_expanded: &str,
213 log: &Log,
214) -> Result<EthereumEvent, EthereumServiceError> {
215 let inner_types = get_inner_event_type(event_name_expanded)?;
216 let ethereum_types = inner_types
217 .split(',')
218 .map(str::to_string)
219 .collect::<Vec<_>>();
220 let mut values = Vec::new();
221 let mut topic_index = 0;
222 let mut data_index = 0;
223 let mut vec = [0_u8; 32];
224 let log_data = log.data();
225 let topics = log_data.topics();
226 for ethereum_type in ethereum_types {
227 values.push(match ethereum_type.strip_suffix(" indexed") {
228 None => {
229 for (i, val) in vec.iter_mut().enumerate() {
230 *val = log_data.data[data_index * 32 + i];
231 }
232 data_index += 1;
233 let entry = vec.into();
234 parse_entry(entry, ðereum_type)?
235 }
236 Some(ethereum_type) => {
237 topic_index += 1;
238 parse_entry(topics[topic_index], ethereum_type)?
239 }
240 });
241 }
242 let block_number = log.block_number.unwrap();
243 Ok(EthereumEvent {
244 values,
245 block_number,
246 })
247}