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_arithmetic::Permill;
27use sp_core::{keccak_256, H160, H256, U256};
28use thiserror::Error;
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 let hash = self.client.block_hash_for_tag(block.unwrap_or_default().into()).await?;
148 let runtime_api = self.client.runtime_api(hash);
149 let dry_run = runtime_api.dry_run(transaction).await?;
150 Ok(dry_run.eth_gas)
151 }
152
153 async fn call(
154 &self,
155 transaction: GenericTransaction,
156 block: Option<BlockNumberOrTagOrHash>,
157 ) -> RpcResult<Bytes> {
158 let hash = self.client.block_hash_for_tag(block.unwrap_or_default()).await?;
159 let runtime_api = self.client.runtime_api(hash);
160 let dry_run = runtime_api.dry_run(transaction).await?;
161 Ok(dry_run.data.into())
162 }
163
164 async fn send_raw_transaction(&self, transaction: Bytes) -> RpcResult<H256> {
165 let hash = H256(keccak_256(&transaction.0));
166 let call = subxt_client::tx().revive().eth_transact(transaction.0);
167 self.client.submit(call).await.map_err(|err| {
168 log::debug!(target: LOG_TARGET, "submit call failed: {err:?}");
169 err
170 })?;
171
172 log::debug!(target: LOG_TARGET, "send_raw_transaction hash: {hash:?}");
173 Ok(hash)
174 }
175
176 async fn send_transaction(&self, mut transaction: GenericTransaction) -> RpcResult<H256> {
177 log::debug!(target: LOG_TARGET, "{transaction:#?}");
178
179 let Some(from) = transaction.from else {
180 log::debug!(target: LOG_TARGET, "Transaction must have a sender");
181 return Err(EthRpcError::InvalidTransaction.into());
182 };
183
184 let account = self
185 .accounts
186 .iter()
187 .find(|account| account.address() == from)
188 .ok_or(EthRpcError::AccountNotFound(from))?;
189
190 if transaction.gas.is_none() {
191 transaction.gas = Some(self.estimate_gas(transaction.clone(), None).await?);
192 }
193
194 if transaction.gas_price.is_none() {
195 transaction.gas_price = Some(self.gas_price().await?);
196 }
197
198 if transaction.nonce.is_none() {
199 transaction.nonce =
200 Some(self.get_transaction_count(from, BlockTag::Latest.into()).await?);
201 }
202
203 if transaction.chain_id.is_none() {
204 transaction.chain_id = Some(self.chain_id().await?);
205 }
206
207 let tx = transaction.try_into_unsigned().map_err(|_| EthRpcError::InvalidTransaction)?;
208 let payload = account.sign_transaction(tx).signed_payload();
209 self.send_raw_transaction(Bytes(payload)).await
210 }
211
212 async fn get_block_by_hash(
213 &self,
214 block_hash: H256,
215 hydrated_transactions: bool,
216 ) -> RpcResult<Option<Block>> {
217 let Some(block) = self.client.block_by_hash(&block_hash).await? else {
218 return Ok(None);
219 };
220 let block = self.client.evm_block(block, hydrated_transactions).await;
221 Ok(Some(block))
222 }
223
224 async fn get_balance(&self, address: H160, block: BlockNumberOrTagOrHash) -> RpcResult<U256> {
225 let hash = self.client.block_hash_for_tag(block).await?;
226 let runtime_api = self.client.runtime_api(hash);
227 let balance = runtime_api.balance(address).await?;
228 Ok(balance)
229 }
230
231 async fn chain_id(&self) -> RpcResult<U256> {
232 Ok(self.client.chain_id().into())
233 }
234
235 async fn gas_price(&self) -> RpcResult<U256> {
236 let hash = self.client.block_hash_for_tag(BlockTag::Latest.into()).await?;
237 let runtime_api = self.client.runtime_api(hash);
238 Ok(runtime_api.gas_price().await?)
239 }
240
241 async fn max_priority_fee_per_gas(&self) -> RpcResult<U256> {
242 let gas_price = self.gas_price().await?;
244 Ok(Permill::from_percent(20).mul_ceil(gas_price))
245 }
246
247 async fn get_code(&self, address: H160, block: BlockNumberOrTagOrHash) -> RpcResult<Bytes> {
248 let hash = self.client.block_hash_for_tag(block).await?;
249 let code = self.client.runtime_api(hash).code(address).await?;
250 Ok(code.into())
251 }
252
253 async fn accounts(&self) -> RpcResult<Vec<H160>> {
254 Ok(self.accounts.iter().map(|account| account.address()).collect())
255 }
256
257 async fn get_block_by_number(
258 &self,
259 block_number: BlockNumberOrTag,
260 hydrated_transactions: bool,
261 ) -> RpcResult<Option<Block>> {
262 let Some(block) = self.client.block_by_number_or_tag(&block_number).await? else {
263 return Ok(None);
264 };
265 let block = self.client.evm_block(block, hydrated_transactions).await;
266 Ok(Some(block))
267 }
268
269 async fn get_block_transaction_count_by_hash(
270 &self,
271 block_hash: Option<H256>,
272 ) -> RpcResult<Option<U256>> {
273 let block_hash = if let Some(block_hash) = block_hash {
274 block_hash
275 } else {
276 self.client.latest_block().await.hash()
277 };
278 Ok(self.client.receipts_count_per_block(&block_hash).await.map(U256::from))
279 }
280
281 async fn get_block_transaction_count_by_number(
282 &self,
283 block: Option<BlockNumberOrTag>,
284 ) -> RpcResult<Option<U256>> {
285 let Some(block) = self
286 .get_block_by_number(block.unwrap_or_else(|| BlockTag::Latest.into()), false)
287 .await?
288 else {
289 return Ok(None);
290 };
291
292 Ok(self.client.receipts_count_per_block(&block.hash).await.map(U256::from))
293 }
294
295 async fn get_logs(&self, filter: Option<Filter>) -> RpcResult<FilterResults> {
296 let logs = self.client.logs(filter).await?;
297 Ok(FilterResults::Logs(logs))
298 }
299
300 async fn get_storage_at(
301 &self,
302 address: H160,
303 storage_slot: U256,
304 block: BlockNumberOrTagOrHash,
305 ) -> RpcResult<Bytes> {
306 let hash = self.client.block_hash_for_tag(block).await?;
307 let runtime_api = self.client.runtime_api(hash);
308 let bytes = runtime_api.get_storage(address, storage_slot.to_big_endian()).await?;
309 Ok(bytes.unwrap_or_default().into())
310 }
311
312 async fn get_transaction_by_block_hash_and_index(
313 &self,
314 block_hash: H256,
315 transaction_index: U256,
316 ) -> RpcResult<Option<TransactionInfo>> {
317 let Some(receipt) = self
318 .client
319 .receipt_by_hash_and_index(
320 &block_hash,
321 transaction_index.try_into().map_err(|_| EthRpcError::ConversionError)?,
322 )
323 .await
324 else {
325 return Ok(None);
326 };
327
328 let Some(signed_tx) = self.client.signed_tx_by_hash(&receipt.transaction_hash).await else {
329 return Ok(None);
330 };
331
332 Ok(Some(TransactionInfo::new(&receipt, signed_tx)))
333 }
334
335 async fn get_transaction_by_block_number_and_index(
336 &self,
337 block: BlockNumberOrTag,
338 transaction_index: U256,
339 ) -> RpcResult<Option<TransactionInfo>> {
340 let Some(block) = self.client.block_by_number_or_tag(&block).await? else {
341 return Ok(None);
342 };
343 self.get_transaction_by_block_hash_and_index(block.hash(), transaction_index)
344 .await
345 }
346
347 async fn get_transaction_by_hash(
348 &self,
349 transaction_hash: H256,
350 ) -> RpcResult<Option<TransactionInfo>> {
351 let receipt = self.client.receipt(&transaction_hash).await;
352 let signed_tx = self.client.signed_tx_by_hash(&transaction_hash).await;
353 if let (Some(receipt), Some(signed_tx)) = (receipt, signed_tx) {
354 return Ok(Some(TransactionInfo::new(&receipt, signed_tx)));
355 }
356
357 Ok(None)
358 }
359
360 async fn get_transaction_count(
361 &self,
362 address: H160,
363 block: BlockNumberOrTagOrHash,
364 ) -> RpcResult<U256> {
365 let hash = self.client.block_hash_for_tag(block).await?;
366 let runtime_api = self.client.runtime_api(hash);
367 let nonce = runtime_api.nonce(address).await?;
368 Ok(nonce)
369 }
370
371 async fn web3_client_version(&self) -> RpcResult<String> {
372 let git_revision = env!("GIT_REVISION");
373 let rustc_version = env!("RUSTC_VERSION");
374 let target = env!("TARGET");
375 Ok(format!("eth-rpc/{git_revision}/{target}/{rustc_version}"))
376 }
377
378 async fn fee_history(
379 &self,
380 block_count: U256,
381 newest_block: BlockNumberOrTag,
382 reward_percentiles: Option<Vec<f64>>,
383 ) -> RpcResult<FeeHistoryResult> {
384 let block_count: u32 = block_count.try_into().map_err(|_| EthRpcError::ConversionError)?;
385 let result = self.client.fee_history(block_count, newest_block, reward_percentiles).await?;
386 Ok(result)
387 }
388}