1use std::fmt::Debug;
7
8use alloy::rpc::types::eth::{
9 request::{TransactionInput, TransactionRequest},
10 BlockId, BlockNumberOrTag, Filter, Log,
11};
12use alloy_primitives::{Address, Bytes, B256, U256, U64};
13use async_trait::async_trait;
14use linera_base::ensure;
15use serde::{de::DeserializeOwned, Deserialize, Serialize};
16use serde_json::value::RawValue;
17
18use crate::common::{
19 event_name_from_expanded, parse_log, EthereumEvent, EthereumQueryError, EthereumServiceError,
20};
21
22#[async_trait]
24pub trait JsonRpcClient {
25 type Error: From<serde_json::Error> + From<EthereumQueryError>;
27
28 async fn request_inner(&self, payload: Vec<u8>) -> Result<Vec<u8>, Self::Error>;
30
31 async fn get_id(&self) -> u64;
33
34 async fn request<T, R>(&self, method: &str, params: T) -> Result<R, Self::Error>
36 where
37 T: Debug + Serialize + Send + Sync,
38 R: DeserializeOwned + Send,
39 {
40 let id = self.get_id().await;
41 let payload = JsonRpcRequest::new(id, method, params);
42 let payload = serde_json::to_vec(&payload)?;
43 let body = self.request_inner(payload).await?;
44 let result = serde_json::from_slice::<JsonRpcResponse>(&body)?;
45 let raw = result.result;
46 let res = serde_json::from_str(raw.get())?;
47 ensure!(id == result.id, EthereumQueryError::IdIsNotMatching);
48 ensure!(
49 "2.0" == result.jsonrpc,
50 EthereumQueryError::WrongJsonRpcVersion
51 );
52 Ok(res)
53 }
54}
55
56#[derive(Serialize, Deserialize, Debug)]
57struct JsonRpcRequest<'a, T> {
58 id: u64,
59 jsonrpc: &'a str,
60 method: &'a str,
61 params: T,
62}
63
64impl<'a, T> JsonRpcRequest<'a, T> {
65 pub fn new(id: u64, method: &'a str, params: T) -> Self {
67 Self {
68 id,
69 jsonrpc: "2.0",
70 method,
71 params,
72 }
73 }
74}
75
76#[derive(Debug, Deserialize)]
78pub struct JsonRpcResponse {
79 id: u64,
80 jsonrpc: String,
81 result: Box<RawValue>,
82}
83
84#[async_trait]
87pub trait EthereumQueries {
88 type Error;
90
91 async fn get_accounts(&self) -> Result<Vec<String>, Self::Error>;
93
94 async fn get_block_number(&self) -> Result<u64, Self::Error>;
96
97 async fn get_balance(&self, address: &str, block_number: u64) -> Result<U256, Self::Error>;
101
102 async fn read_events(
111 &self,
112 contract_address: &str,
113 event_name_expanded: &str,
114 from_block: u64,
115 to_block: u64,
116 ) -> Result<Vec<EthereumEvent>, Self::Error>;
117
118 async fn non_executive_call(
123 &self,
124 contract_address: &str,
125 data: Bytes,
126 from: &str,
127 block: u64,
128 ) -> Result<Bytes, Self::Error>;
129
130 async fn get_chain_id(&self) -> Result<u64, Self::Error>;
132
133 async fn is_block_hash_finalized(&self, block_hash: B256) -> Result<bool, Self::Error>;
142}
143
144pub(crate) fn get_block_id(block_number: u64) -> BlockId {
145 let number = BlockNumberOrTag::Number(block_number);
146 BlockId::Number(number)
147}
148
149#[async_trait]
150impl<C> EthereumQueries for C
151where
152 C: JsonRpcClient + Sync,
153 EthereumServiceError: From<<C as JsonRpcClient>::Error>,
154{
155 type Error = EthereumServiceError;
156
157 async fn get_accounts(&self) -> Result<Vec<String>, Self::Error> {
158 let results: Vec<String> = self.request("eth_accounts", ()).await?;
159 Ok(results
160 .into_iter()
161 .map(|x| x.to_lowercase())
162 .collect::<Vec<_>>())
163 }
164
165 async fn get_block_number(&self) -> Result<u64, Self::Error> {
166 let result = self.request::<_, U64>("eth_blockNumber", ()).await?;
167 Ok(result.to::<u64>())
168 }
169
170 async fn get_chain_id(&self) -> Result<u64, Self::Error> {
171 let result = self.request::<_, U64>("eth_chainId", ()).await?;
172 Ok(result.to::<u64>())
173 }
174
175 async fn get_balance(&self, address: &str, block_number: u64) -> Result<U256, Self::Error> {
176 let address = address.parse::<Address>()?;
177 let tag = get_block_id(block_number);
178 Ok(self.request("eth_getBalance", (address, tag)).await?)
179 }
180
181 async fn read_events(
182 &self,
183 contract_address: &str,
184 event_name_expanded: &str,
185 from_block: u64,
186 to_block: u64,
187 ) -> Result<Vec<EthereumEvent>, Self::Error> {
188 let contract_address = contract_address.parse::<Address>()?;
189 let event_name = event_name_from_expanded(event_name_expanded);
190 let filter = Filter::new()
191 .address(contract_address)
192 .event(&event_name)
193 .from_block(from_block)
194 .to_block(to_block - 1);
195 let events = self
196 .request::<_, Vec<Log>>("eth_getLogs", (filter,))
197 .await?;
198 events
199 .into_iter()
200 .map(|x| parse_log(event_name_expanded, &x))
201 .collect::<Result<_, _>>()
202 }
203
204 async fn non_executive_call(
205 &self,
206 contract_address: &str,
207 data: Bytes,
208 from: &str,
209 block: u64,
210 ) -> Result<Bytes, Self::Error> {
211 let contract_address = contract_address.parse::<Address>()?;
212 let from = from.parse::<Address>()?;
213 let input = TransactionInput::new(data);
214 let tx = TransactionRequest::default()
215 .from(from)
216 .to(contract_address)
217 .input(input);
218 let tag = get_block_id(block);
219 Ok(self.request::<_, Bytes>("eth_call", (tx, tag)).await?)
220 }
221
222 async fn is_block_hash_finalized(&self, block_hash: B256) -> Result<bool, Self::Error> {
223 let block: Option<EthBlock> = self
225 .request("eth_getBlockByHash", (block_hash, false))
226 .await?;
227 let block = block.ok_or(EthereumServiceError::BlockNotFound)?;
228 let block_number = block.number.to::<u64>();
229
230 let finalized: EthBlock = self
232 .request("eth_getBlockByNumber", ("finalized", false))
233 .await?;
234 if block_number > finalized.number.to::<u64>() {
235 return Ok(false);
236 }
237
238 let canonical: Option<EthBlock> = self
244 .request(
245 "eth_getBlockByNumber",
246 (BlockNumberOrTag::Number(block_number), false),
247 )
248 .await?;
249 Ok(canonical.map(|canonical| canonical.hash) == Some(block_hash))
250 }
251}
252
253#[derive(Deserialize)]
255struct EthBlock {
256 number: U64,
257 hash: B256,
258}