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