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("Failed to deploy the smart contract")]
37 DeployError,
38
39 #[error("Unsupported Ethereum type")]
40 UnsupportedEthereumTypeError,
41
42 #[error("Event parsing error")]
43 EventParsingError,
44
45 #[error(transparent)]
47 ParseBigIntError(#[from] num_bigint::ParseBigIntError),
48
49 #[error("Ethereum parsing error")]
51 EthereumParsingError,
52
53 #[error("Parse bool error")]
55 ParseBoolError,
56
57 #[error(transparent)]
59 FromHexError(#[from] alloy_primitives::hex::FromHexError),
60
61 #[error(transparent)]
63 JsonError(#[from] serde_json::Error),
64
65 #[error(transparent)]
67 #[cfg(not(target_arch = "wasm32"))]
68 RpcError(#[from] json_rpc::RpcError<alloy::transports::TransportErrorKind>),
69
70 #[error(transparent)]
72 #[cfg(not(target_arch = "wasm32"))]
73 UrlParseError(#[from] url::ParseError),
74
75 #[error(transparent)]
77 #[cfg(not(target_arch = "wasm32"))]
78 AlloyReqwestError(#[from] alloy::transports::http::reqwest::Error),
79}
80
81#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
84pub enum EthereumDataType {
85 Address(String),
86 Uint256(U256),
87 Uint64(u64),
88 Int64(i64),
89 Uint32(u32),
90 Int32(i32),
91 Uint16(u16),
92 Int16(i16),
93 Uint8(u8),
94 Int8(i8),
95 Bool(bool),
96}
97
98pub fn event_name_from_expanded(event_name_expanded: &str) -> String {
102 event_name_expanded.replace(" indexed", "").to_string()
103}
104
105fn parse_entry(entry: B256, ethereum_type: &str) -> Result<EthereumDataType, EthereumServiceError> {
106 if ethereum_type == "address" {
107 let address = Address::from_word(entry);
108 let address = format!("{:?}", address);
109 return Ok(EthereumDataType::Address(address));
110 }
111 if ethereum_type == "uint256" {
112 let entry = U256::from_be_bytes(entry.0);
113 return Ok(EthereumDataType::Uint256(entry));
114 }
115 if ethereum_type == "uint64" {
116 let entry = BigUint::from_bytes_be(&entry.0);
117 let entry = entry.to_u64().unwrap();
118 return Ok(EthereumDataType::Uint64(entry));
119 }
120 if ethereum_type == "int64" {
121 let entry = BigInt::from_signed_bytes_be(&entry.0);
122 let entry = entry.to_i64().unwrap();
123 return Ok(EthereumDataType::Int64(entry));
124 }
125 if ethereum_type == "uint32" {
126 let entry = BigUint::from_bytes_be(&entry.0);
127 let entry = entry.to_u32().unwrap();
128 return Ok(EthereumDataType::Uint32(entry));
129 }
130 if ethereum_type == "int32" {
131 let entry = BigInt::from_signed_bytes_be(&entry.0);
132 let entry = entry.to_i32().unwrap();
133 return Ok(EthereumDataType::Int32(entry));
134 }
135 if ethereum_type == "uint16" {
136 let entry = BigUint::from_bytes_be(&entry.0);
137 let entry = entry.to_u16().unwrap();
138 return Ok(EthereumDataType::Uint16(entry));
139 }
140 if ethereum_type == "int16" {
141 let entry = BigInt::from_signed_bytes_be(&entry.0);
142 let entry = entry.to_i16().unwrap();
143 return Ok(EthereumDataType::Int16(entry));
144 }
145 if ethereum_type == "uint8" {
146 let entry = BigUint::from_bytes_be(&entry.0);
147 let entry = entry.to_u8().unwrap();
148 return Ok(EthereumDataType::Uint8(entry));
149 }
150 if ethereum_type == "int8" {
151 let entry = BigInt::from_signed_bytes_be(&entry.0);
152 let entry = entry.to_i8().unwrap();
153 return Ok(EthereumDataType::Int8(entry));
154 }
155 if ethereum_type == "bool" {
156 let entry = BigUint::from_bytes_be(&entry.0);
157 let entry = entry.to_u8().unwrap();
158 let entry = match entry {
159 1 => true,
160 0 => false,
161 _ => {
162 return Err(EthereumServiceError::ParseBoolError);
163 }
164 };
165 return Ok(EthereumDataType::Bool(entry));
166 }
167 Err(EthereumServiceError::UnsupportedEthereumTypeError)
168}
169
170#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
172pub struct EthereumEvent {
173 pub values: Vec<EthereumDataType>,
174 pub block_number: u64,
175}
176
177fn get_inner_event_type(event_name_expanded: &str) -> Result<String, EthereumServiceError> {
178 if let Some(opening_paren_index) = event_name_expanded.find('(') {
179 if let Some(closing_paren_index) = event_name_expanded.find(')') {
180 let inner_types = &event_name_expanded[opening_paren_index + 1..closing_paren_index];
182 return Ok(inner_types.to_string());
183 }
184 }
185 Err(EthereumServiceError::EventParsingError)
186}
187
188pub fn parse_log(
189 event_name_expanded: &str,
190 log: Log,
191) -> Result<EthereumEvent, EthereumServiceError> {
192 let inner_types = get_inner_event_type(event_name_expanded)?;
193 let ethereum_types = inner_types
194 .split(',')
195 .map(|s| s.to_string())
196 .collect::<Vec<_>>();
197 let mut values = Vec::new();
198 let mut topic_index = 0;
199 let mut data_index = 0;
200 let mut vec = [0_u8; 32];
201 let log_data = log.data();
202 let topics = log_data.topics();
203 for ethereum_type in ethereum_types {
204 values.push(match ethereum_type.strip_suffix(" indexed") {
205 None => {
206 for (i, val) in vec.iter_mut().enumerate() {
207 *val = log_data.data[data_index * 32 + i];
208 }
209 data_index += 1;
210 let entry = vec.into();
211 parse_entry(entry, ðereum_type)?
212 }
213 Some(ethereum_type) => {
214 topic_index += 1;
215 parse_entry(topics[topic_index], ethereum_type)?
216 }
217 });
218 }
219 let block_number = log.block_number.unwrap();
220 Ok(EthereumEvent {
221 values,
222 block_number,
223 })
224}