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