1#![cfg_attr(docsrs, feature(doc_cfg))]
19
20use client::ClientError;
21use jsonrpsee::{
22 core::{async_trait, RpcResult},
23 types::{ErrorCode, ErrorObjectOwned},
24};
25use pallet_revive::evm::*;
26use sp_core::{keccak_256, H160, H256, U256};
27use thiserror::Error;
28use tokio::time::Duration;
29
30pub mod cli;
31pub mod client;
32pub mod example;
33pub mod subxt_client;
34
35#[cfg(test)]
36mod tests;
37
38mod block_info_provider;
39pub use block_info_provider::*;
40
41mod receipt_provider;
42pub use receipt_provider::*;
43
44mod fee_history_provider;
45pub use fee_history_provider::*;
46
47mod receipt_extractor;
48pub use receipt_extractor::*;
49
50mod apis;
51pub use apis::*;
52
53pub const LOG_TARGET: &str = "eth-rpc";
54
55pub struct EthRpcServerImpl {
57 client: client::Client,
59
60 accounts: Vec<Account>,
62}
63
64impl EthRpcServerImpl {
65 pub fn new(client: client::Client) -> Self {
67 Self { client, accounts: vec![] }
68 }
69
70 pub fn with_accounts(mut self, accounts: Vec<Account>) -> Self {
72 self.accounts = accounts;
73 self
74 }
75}
76
77#[derive(Error, Debug)]
79pub enum EthRpcError {
80 #[error("Client error: {0}")]
82 ClientError(#[from] ClientError),
83 #[error("Decoding error: {0}")]
85 RlpError(#[from] rlp::DecoderError),
86 #[error("Conversion error")]
88 ConversionError,
89 #[error("Invalid signature")]
91 InvalidSignature,
92 #[error("Account not found for address {0:?}")]
94 AccountNotFound(H160),
95 #[error("Invalid transaction")]
97 InvalidTransaction,
98 #[error("Invalid transaction {0:?}")]
100 TransactionTypeNotSupported(Byte),
101}
102
103impl From<EthRpcError> for ErrorObjectOwned {
105 fn from(value: EthRpcError) -> Self {
106 match value {
107 EthRpcError::ClientError(err) => Self::from(err),
108 _ => Self::owned::<String>(ErrorCode::InvalidRequest.code(), value.to_string(), None),
109 }
110 }
111}
112
113#[async_trait]
114impl EthRpcServer for EthRpcServerImpl {
115 async fn net_version(&self) -> RpcResult<String> {
116 Ok(self.client.chain_id().to_string())
117 }
118
119 async fn net_listening(&self) -> RpcResult<bool> {
120 let syncing = self.client.syncing().await?;
121 let listening = matches!(syncing, SyncingStatus::Bool(false));
122 Ok(listening)
123 }
124
125 async fn syncing(&self) -> RpcResult<SyncingStatus> {
126 Ok(self.client.syncing().await?)
127 }
128
129 async fn block_number(&self) -> RpcResult<U256> {
130 let number = self.client.block_number().await?;
131 Ok(number.into())
132 }
133
134 async fn get_transaction_receipt(
135 &self,
136 transaction_hash: H256,
137 ) -> RpcResult<Option<ReceiptInfo>> {
138 let receipt = self.client.receipt(&transaction_hash).await;
139 Ok(receipt)
140 }
141
142 async fn estimate_gas(
143 &self,
144 transaction: GenericTransaction,
145 block: Option<BlockNumberOrTag>,
146 ) -> RpcResult<U256> {
147 log::trace!(target: LOG_TARGET, "estimate_gas transaction={transaction:?} block={block:?}");
148 let block = block.unwrap_or_default();
149 let hash = self.client.block_hash_for_tag(block.clone().into()).await?;
150 let runtime_api = self.client.runtime_api(hash);
151 let dry_run = runtime_api.dry_run(transaction, block.into()).await?;
152 log::trace!(target: LOG_TARGET, "estimate_gas result={dry_run:?}");
153 Ok(dry_run.eth_gas)
154 }
155
156 async fn call(
157 &self,
158 transaction: GenericTransaction,
159 block: Option<BlockNumberOrTagOrHash>,
160 ) -> RpcResult<Bytes> {
161 let block = block.unwrap_or_default();
162 let hash = self.client.block_hash_for_tag(block.clone()).await?;
163 let runtime_api = self.client.runtime_api(hash);
164 let dry_run = runtime_api.dry_run(transaction, block).await?;
165 Ok(dry_run.data.into())
166 }
167
168 async fn send_raw_transaction(&self, transaction: Bytes) -> RpcResult<H256> {
169 let hash = H256(keccak_256(&transaction.0));
170 log::trace!(target: LOG_TARGET, "send_raw_transaction transaction: {transaction:?} ethereum_hash: {hash:?}");
171 let call = subxt_client::tx().revive().eth_transact(transaction.0);
172
173 let receiver = self.client.block_notifier().map(|sender| sender.subscribe());
175
176 self.client.submit(call).await.map_err(|err| {
178 log::trace!(target: LOG_TARGET, "send_raw_transaction ethereum_hash: {hash:?} failed: {err:?}");
179 err
180 })?;
181
182 log::trace!(target: LOG_TARGET, "send_raw_transaction with hash: {hash:?}");
183
184 if let Some(mut receiver) = receiver {
186 if let Err(err) = tokio::time::timeout(Duration::from_millis(500), async {
187 loop {
188 if let Ok(block_hash) = receiver.recv().await {
189 let Ok(Some(block)) = self.client.block_by_hash(&block_hash).await else {
190 log::debug!(target: LOG_TARGET, "Could not find the block with the received hash: {hash:?}.");
191 continue
192 };
193 let Some(evm_block) = self.client.evm_block(block, false).await else {
194 log::debug!(target: LOG_TARGET, "Failed to get the EVM block for substrate block with hash: {hash:?}");
195 continue
196 };
197 if evm_block.transactions.contains_tx(hash) {
198 log::debug!(target: LOG_TARGET, "{hash:} was included in a block");
199 break;
200 }
201 }
202 }
203 })
204 .await
205 {
206 log::debug!(target: LOG_TARGET, "timeout waiting for new block: {err:?}");
207 }
208 }
209
210 log::debug!(target: LOG_TARGET, "send_raw_transaction hash: {hash:?}");
211 Ok(hash)
212 }
213
214 async fn send_transaction(&self, mut transaction: GenericTransaction) -> RpcResult<H256> {
215 log::debug!(target: LOG_TARGET, "{transaction:#?}");
216
217 let Some(from) = transaction.from else {
218 log::debug!(target: LOG_TARGET, "Transaction must have a sender");
219 return Err(EthRpcError::InvalidTransaction.into());
220 };
221
222 let account = self
223 .accounts
224 .iter()
225 .find(|account| account.address() == from)
226 .ok_or(EthRpcError::AccountNotFound(from))?;
227
228 if transaction.gas.is_none() {
229 transaction.gas = Some(self.estimate_gas(transaction.clone(), None).await?);
230 }
231
232 if transaction.gas_price.is_none() {
233 transaction.gas_price = Some(self.gas_price().await?);
234 }
235
236 if transaction.nonce.is_none() {
237 transaction.nonce =
238 Some(self.get_transaction_count(from, BlockTag::Latest.into()).await?);
239 }
240
241 if transaction.chain_id.is_none() {
242 transaction.chain_id = Some(self.chain_id().await?);
243 }
244
245 let tx = transaction.try_into_unsigned().map_err(|_| EthRpcError::InvalidTransaction)?;
246 let payload = account.sign_transaction(tx).signed_payload();
247 self.send_raw_transaction(Bytes(payload)).await
248 }
249
250 async fn get_block_by_hash(
251 &self,
252 block_hash: H256,
253 hydrated_transactions: bool,
254 ) -> RpcResult<Option<Block>> {
255 let Some(block) = self.client.block_by_ethereum_hash(&block_hash).await? else {
256 return Ok(None);
257 };
258 let block = self.client.evm_block(block, hydrated_transactions).await;
259 Ok(block)
260 }
261
262 async fn get_balance(&self, address: H160, block: BlockNumberOrTagOrHash) -> RpcResult<U256> {
263 let hash = self.client.block_hash_for_tag(block).await?;
264 let runtime_api = self.client.runtime_api(hash);
265 let balance = runtime_api.balance(address).await?;
266 Ok(balance)
267 }
268
269 async fn chain_id(&self) -> RpcResult<U256> {
270 Ok(self.client.chain_id().into())
271 }
272
273 async fn gas_price(&self) -> RpcResult<U256> {
274 let hash = self.client.block_hash_for_tag(BlockTag::Latest.into()).await?;
275 let runtime_api = self.client.runtime_api(hash);
276 Ok(runtime_api.gas_price().await?)
277 }
278
279 async fn max_priority_fee_per_gas(&self) -> RpcResult<U256> {
280 Ok(Default::default())
283 }
284
285 async fn get_code(&self, address: H160, block: BlockNumberOrTagOrHash) -> RpcResult<Bytes> {
286 let hash = self.client.block_hash_for_tag(block).await?;
287 let code = self.client.runtime_api(hash).code(address).await?;
288 Ok(code.into())
289 }
290
291 async fn accounts(&self) -> RpcResult<Vec<H160>> {
292 Ok(self.accounts.iter().map(|account| account.address()).collect())
293 }
294
295 async fn get_block_by_number(
296 &self,
297 block_number: BlockNumberOrTag,
298 hydrated_transactions: bool,
299 ) -> RpcResult<Option<Block>> {
300 let Some(block) = self.client.block_by_number_or_tag(&block_number).await? else {
301 return Ok(None);
302 };
303 let block = self.client.evm_block(block, hydrated_transactions).await;
304 Ok(block)
305 }
306
307 async fn get_block_transaction_count_by_hash(
308 &self,
309 block_hash: Option<H256>,
310 ) -> RpcResult<Option<U256>> {
311 let block_hash = if let Some(block_hash) = block_hash {
312 block_hash
313 } else {
314 self.client.latest_block().await.hash()
315 };
316
317 let Some(substrate_hash) = self.client.resolve_substrate_hash(&block_hash).await else {
318 return Ok(None);
319 };
320
321 Ok(self.client.receipts_count_per_block(&substrate_hash).await.map(U256::from))
322 }
323
324 async fn get_block_transaction_count_by_number(
325 &self,
326 block: Option<BlockNumberOrTag>,
327 ) -> RpcResult<Option<U256>> {
328 let substrate_hash = if let Some(block) = self
329 .client
330 .block_by_number_or_tag(&block.unwrap_or_else(|| BlockTag::Latest.into()))
331 .await?
332 {
333 block.hash()
334 } else {
335 return Ok(None);
336 };
337
338 Ok(self.client.receipts_count_per_block(&substrate_hash).await.map(U256::from))
339 }
340
341 async fn get_logs(&self, filter: Option<Filter>) -> RpcResult<FilterResults> {
342 let logs = self.client.logs(filter).await?;
343 Ok(FilterResults::Logs(logs))
344 }
345
346 async fn get_storage_at(
347 &self,
348 address: H160,
349 storage_slot: U256,
350 block: BlockNumberOrTagOrHash,
351 ) -> RpcResult<Bytes> {
352 let hash = self.client.block_hash_for_tag(block).await?;
353 let runtime_api = self.client.runtime_api(hash);
354 let bytes = runtime_api.get_storage(address, storage_slot.to_big_endian()).await?;
355 Ok(bytes.unwrap_or([0u8; 32].into()).into())
356 }
357
358 async fn get_transaction_by_block_hash_and_index(
359 &self,
360 block_hash: H256,
361 transaction_index: U256,
362 ) -> RpcResult<Option<TransactionInfo>> {
363 let Some(substrate_block_hash) = self.client.resolve_substrate_hash(&block_hash).await
364 else {
365 return Ok(None);
366 };
367 self.get_transaction_by_substrate_block_hash_and_index(
368 substrate_block_hash,
369 transaction_index,
370 )
371 .await
372 }
373
374 async fn get_transaction_by_block_number_and_index(
375 &self,
376 block: BlockNumberOrTag,
377 transaction_index: U256,
378 ) -> RpcResult<Option<TransactionInfo>> {
379 let Some(block) = self.client.block_by_number_or_tag(&block).await? else {
380 return Ok(None);
381 };
382 self.get_transaction_by_substrate_block_hash_and_index(block.hash(), transaction_index)
383 .await
384 }
385
386 async fn get_transaction_by_hash(
387 &self,
388 transaction_hash: H256,
389 ) -> RpcResult<Option<TransactionInfo>> {
390 let receipt = self.client.receipt(&transaction_hash).await;
391 let signed_tx = self.client.signed_tx_by_hash(&transaction_hash).await;
392 if let (Some(receipt), Some(signed_tx)) = (receipt, signed_tx) {
393 return Ok(Some(TransactionInfo::new(&receipt, signed_tx)));
394 }
395
396 Ok(None)
397 }
398
399 async fn get_transaction_count(
400 &self,
401 address: H160,
402 block: BlockNumberOrTagOrHash,
403 ) -> RpcResult<U256> {
404 let hash = self.client.block_hash_for_tag(block).await?;
405 let runtime_api = self.client.runtime_api(hash);
406 let nonce = runtime_api.nonce(address).await?;
407 Ok(nonce)
408 }
409
410 async fn web3_client_version(&self) -> RpcResult<String> {
411 let git_revision = env!("GIT_REVISION");
412 let rustc_version = env!("RUSTC_VERSION");
413 let target = env!("TARGET");
414 Ok(format!("eth-rpc/{git_revision}/{target}/{rustc_version}"))
415 }
416
417 async fn fee_history(
418 &self,
419 block_count: U256,
420 newest_block: BlockNumberOrTag,
421 reward_percentiles: Option<Vec<f64>>,
422 ) -> RpcResult<FeeHistoryResult> {
423 let block_count: u32 = block_count.try_into().map_err(|_| EthRpcError::ConversionError)?;
424 let result = self.client.fee_history(block_count, newest_block, reward_percentiles).await?;
425 Ok(result)
426 }
427}
428
429impl EthRpcServerImpl {
430 async fn get_transaction_by_substrate_block_hash_and_index(
431 &self,
432 substrate_block_hash: H256,
433 transaction_index: U256,
434 ) -> RpcResult<Option<TransactionInfo>> {
435 let Some(receipt) = self
436 .client
437 .receipt_by_hash_and_index(
438 &substrate_block_hash,
439 transaction_index.try_into().map_err(|_| EthRpcError::ConversionError)?,
440 )
441 .await
442 else {
443 return Ok(None);
444 };
445 let Some(signed_tx) = self.client.signed_tx_by_hash(&receipt.transaction_hash).await else {
446 return Ok(None);
447 };
448
449 Ok(Some(TransactionInfo::new(&receipt, signed_tx)))
450 }
451}