mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-30 01:28:21 -05:00
feat(rpc): Add ots_ namespace and trait bindings for Otterscan Endpoints (#3778)
Co-authored-by: Miguel Palhas <mpalhas@gmail.com> Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
@@ -26,6 +26,7 @@ mod eth;
|
||||
mod eth_filter;
|
||||
mod eth_pubsub;
|
||||
mod net;
|
||||
mod otterscan;
|
||||
mod reth;
|
||||
mod rpc;
|
||||
mod trace;
|
||||
@@ -45,6 +46,7 @@ pub mod servers {
|
||||
eth_filter::EthFilterApiServer,
|
||||
eth_pubsub::EthPubSubApiServer,
|
||||
net::NetApiServer,
|
||||
otterscan::OtterscanServer,
|
||||
reth::RethApiServer,
|
||||
rpc::RpcApiServer,
|
||||
trace::TraceApiServer,
|
||||
@@ -66,6 +68,7 @@ pub mod clients {
|
||||
engine::{EngineApiClient, EngineEthApiClient},
|
||||
eth::EthApiClient,
|
||||
net::NetApiClient,
|
||||
otterscan::OtterscanClient,
|
||||
rpc::RpcApiServer,
|
||||
trace::TraceApiClient,
|
||||
txpool::TxPoolApiClient,
|
||||
|
||||
85
crates/rpc/rpc-api/src/otterscan.rs
Normal file
85
crates/rpc/rpc-api/src/otterscan.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
|
||||
use reth_primitives::{Address, BlockId, BlockNumberOrTag, TxHash, H256};
|
||||
use reth_rpc_types::{
|
||||
BlockDetails, ContractCreator, InternalOperation, OtsBlockTransactions, TraceEntry,
|
||||
Transaction, TransactionsWithReceipts,
|
||||
};
|
||||
|
||||
/// Otterscan rpc interface.
|
||||
#[cfg_attr(not(feature = "client"), rpc(server, namespace = "ots"))]
|
||||
#[cfg_attr(feature = "client", rpc(server, client, namespace = "ots"))]
|
||||
pub trait Otterscan {
|
||||
/// Check if a certain address contains a deployed code.
|
||||
#[method(name = "hasCode")]
|
||||
async fn has_code(&self, address: Address, block_number: Option<BlockId>) -> RpcResult<bool>;
|
||||
|
||||
/// Very simple API versioning scheme. Every time we add a new capability, the number is
|
||||
/// incremented. This allows for Otterscan to check if the node contains all API it
|
||||
/// needs.
|
||||
#[method(name = "getApiLevel")]
|
||||
async fn get_api_level(&self) -> RpcResult<u64>;
|
||||
|
||||
/// Return the internal ETH transfers inside a transaction.
|
||||
#[method(name = "getInternalOperations")]
|
||||
async fn get_internal_operations(&self, tx_hash: TxHash) -> RpcResult<Vec<InternalOperation>>;
|
||||
|
||||
/// Given a transaction hash, returns its raw revert reason.
|
||||
#[method(name = "getTransactionError")]
|
||||
async fn get_transaction_error(&self, tx_hash: TxHash) -> RpcResult<String>;
|
||||
|
||||
/// Extract all variations of calls, contract creation and self-destructs and returns a call
|
||||
/// tree.
|
||||
#[method(name = "traceTransaction")]
|
||||
async fn trace_transaction(&self, tx_hash: TxHash) -> RpcResult<TraceEntry>;
|
||||
|
||||
/// Tailor-made and expanded version of eth_getBlockByNumber for block details page in
|
||||
/// Otterscan.
|
||||
#[method(name = "getBlockDetails")]
|
||||
async fn get_block_details(
|
||||
&self,
|
||||
block_number: BlockNumberOrTag,
|
||||
) -> RpcResult<Option<BlockDetails>>;
|
||||
|
||||
/// Tailor-made and expanded version of eth_getBlockByHash for block details page in Otterscan.
|
||||
#[method(name = "getBlockDetailsByHash")]
|
||||
async fn get_block_details_by_hash(&self, block_hash: H256) -> RpcResult<Option<BlockDetails>>;
|
||||
|
||||
/// Get paginated transactions for a certain block. Also remove some verbose fields like logs.
|
||||
#[method(name = "getBlockTransactions")]
|
||||
async fn get_block_transactions(
|
||||
&self,
|
||||
block_number: BlockNumberOrTag,
|
||||
page_number: usize,
|
||||
page_size: usize,
|
||||
) -> RpcResult<OtsBlockTransactions>;
|
||||
|
||||
/// Gets paginated inbound/outbound transaction calls for a certain address.
|
||||
#[method(name = "searchTransactionsBefore")]
|
||||
async fn search_transactions_before(
|
||||
&self,
|
||||
address: Address,
|
||||
block_number: BlockNumberOrTag,
|
||||
page_size: usize,
|
||||
) -> RpcResult<TransactionsWithReceipts>;
|
||||
|
||||
/// Gets paginated inbound/outbound transaction calls for a certain address.
|
||||
#[method(name = "searchTransactionsAfter")]
|
||||
async fn search_transactions_after(
|
||||
&self,
|
||||
address: Address,
|
||||
block_number: BlockNumberOrTag,
|
||||
page_size: usize,
|
||||
) -> RpcResult<TransactionsWithReceipts>;
|
||||
|
||||
/// Gets the transaction hash for a certain sender address, given its nonce.
|
||||
#[method(name = "getTransactionBySenderAndNonce")]
|
||||
async fn get_transaction_by_sender_and_nonce(
|
||||
&self,
|
||||
sender: Address,
|
||||
nonce: u64,
|
||||
) -> RpcResult<Option<Transaction>>;
|
||||
|
||||
/// Gets the transaction hash and the address who created a contract.
|
||||
#[method(name = "getContractCreator")]
|
||||
async fn get_contract_creator(&self, address: Address) -> RpcResult<Option<ContractCreator>>;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ use reth_rpc::{
|
||||
gas_oracle::GasPriceOracle,
|
||||
},
|
||||
AdminApi, DebugApi, EngineEthApi, EthApi, EthFilter, EthPubSub, EthSubscriptionIdProvider,
|
||||
NetApi, RPCApi, RethApi, TraceApi, TracingCallGuard, TxPoolApi, Web3Api,
|
||||
NetApi, OtterscanApi, RPCApi, RethApi, TraceApi, TracingCallGuard, TxPoolApi, Web3Api,
|
||||
};
|
||||
use reth_rpc_api::{servers::*, EngineApiServer};
|
||||
use reth_tasks::{TaskSpawner, TokioTaskExecutor};
|
||||
@@ -651,6 +651,8 @@ pub enum RethRpcModule {
|
||||
Rpc,
|
||||
/// `reth_` module
|
||||
Reth,
|
||||
/// `ots_` module
|
||||
Ots,
|
||||
}
|
||||
|
||||
// === impl RethRpcModule ===
|
||||
@@ -779,6 +781,13 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Register Otterscan Namespace
|
||||
pub fn register_ots(&mut self) -> &mut Self {
|
||||
let eth_api = self.eth_api();
|
||||
self.modules.insert(RethRpcModule::Ots, OtterscanApi::new(eth_api).into_rpc().into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Register Debug Namespace
|
||||
pub fn register_debug(&mut self) -> &mut Self {
|
||||
let eth_api = self.eth_api();
|
||||
@@ -936,6 +945,7 @@ where
|
||||
)
|
||||
.into_rpc()
|
||||
.into(),
|
||||
RethRpcModule::Ots => OtterscanApi::new(eth_api.clone()).into_rpc().into(),
|
||||
RethRpcModule::Reth => {
|
||||
RethApi::new(self.provider.clone(), Box::new(self.executor.clone()))
|
||||
.into_rpc()
|
||||
@@ -1774,6 +1784,7 @@ mod tests {
|
||||
"trace" => RethRpcModule::Trace,
|
||||
"web3" => RethRpcModule::Web3,
|
||||
"rpc" => RethRpcModule::Rpc,
|
||||
"ots" => RethRpcModule::Ots,
|
||||
"reth" => RethRpcModule::Reth,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
|
||||
mod admin;
|
||||
mod eth;
|
||||
mod otterscan;
|
||||
mod rpc;
|
||||
|
||||
pub use admin::*;
|
||||
pub use eth::*;
|
||||
pub use otterscan::*;
|
||||
pub use rpc::*;
|
||||
|
||||
97
crates/rpc/rpc-types/src/otterscan.rs
Normal file
97
crates/rpc/rpc-types/src/otterscan.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use crate::{Block, Transaction, TransactionReceipt};
|
||||
use reth_primitives::{Address, Bytes, U256};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Operation type enum for `InternalOperation` struct
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum OperationType {
|
||||
/// Operation Transfer
|
||||
OpTransfer = 0,
|
||||
/// Operation Contract self destruct
|
||||
OpSelfDestruct = 1,
|
||||
/// Operation Create
|
||||
OpCreate = 2,
|
||||
/// Operation Create2
|
||||
OpCreate2 = 3,
|
||||
}
|
||||
|
||||
/// Custom struct for otterscan `getInternalOperations` RPC response
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct InternalOperation {
|
||||
r#type: OperationType,
|
||||
from: Address,
|
||||
to: Address,
|
||||
value: u128,
|
||||
}
|
||||
|
||||
/// Custom struct for otterscan `traceTransaction` RPC response
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct TraceEntry {
|
||||
r#type: String,
|
||||
depth: u32,
|
||||
from: Address,
|
||||
to: Address,
|
||||
value: u128,
|
||||
input: Bytes,
|
||||
}
|
||||
|
||||
/// Internal issuance struct for `BlockDetails` struct
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InternalIssuance {
|
||||
block_reward: U256,
|
||||
uncle_reward: U256,
|
||||
issuance: U256,
|
||||
}
|
||||
|
||||
/// Custom `Block` struct that includes transaction count for Otterscan responses
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OtsBlock {
|
||||
#[serde(flatten)]
|
||||
block: Block,
|
||||
transaction_count: usize,
|
||||
}
|
||||
|
||||
/// Custom struct for otterscan `getBlockDetails` RPC response
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BlockDetails {
|
||||
block: OtsBlock,
|
||||
issuance: InternalIssuance,
|
||||
total_fees: U256,
|
||||
}
|
||||
|
||||
/// Custom transaction receipt struct for otterscan `OtsBlockTransactions` struct
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OtsTransactionReceipt {
|
||||
#[serde(flatten)]
|
||||
receipt: TransactionReceipt,
|
||||
timestamp: u64,
|
||||
}
|
||||
|
||||
/// Custom struct for otterscan `getBlockTransactions` RPC response
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct OtsBlockTransactions {
|
||||
fullblock: OtsBlock,
|
||||
receipts: Vec<OtsTransactionReceipt>,
|
||||
}
|
||||
|
||||
/// Custom struct for otterscan `searchTransactionsAfter`and `searchTransactionsBefore` RPC
|
||||
/// responses
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionsWithReceipts {
|
||||
txs: Vec<Transaction>,
|
||||
receipts: Vec<OtsTransactionReceipt>,
|
||||
first_page: bool,
|
||||
last_page: bool,
|
||||
}
|
||||
|
||||
/// Custom struct for otterscan `getContractCreator` RPC responses
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ContractCreator {
|
||||
tx: Transaction,
|
||||
creator: Address,
|
||||
}
|
||||
@@ -37,6 +37,7 @@ mod engine;
|
||||
pub mod eth;
|
||||
mod layers;
|
||||
mod net;
|
||||
mod otterscan;
|
||||
mod reth;
|
||||
mod rpc;
|
||||
mod trace;
|
||||
@@ -50,6 +51,7 @@ pub use engine::{EngineApi, EngineEthApi};
|
||||
pub use eth::{EthApi, EthApiSpec, EthFilter, EthPubSub, EthSubscriptionIdProvider};
|
||||
pub use layers::{AuthLayer, AuthValidator, Claims, JwtAuthValidator, JwtError, JwtSecret};
|
||||
pub use net::NetApi;
|
||||
pub use otterscan::OtterscanApi;
|
||||
pub use reth::RethApi;
|
||||
pub use rpc::RPCApi;
|
||||
pub use trace::TraceApi;
|
||||
|
||||
111
crates/rpc/rpc/src/otterscan.rs
Normal file
111
crates/rpc/rpc/src/otterscan.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
use crate::result::internal_rpc_err;
|
||||
use async_trait::async_trait;
|
||||
use jsonrpsee::core::RpcResult;
|
||||
use reth_primitives::{Address, BlockId, BlockNumberOrTag, TxHash, H256};
|
||||
use reth_rpc_api::{EthApiServer, OtterscanServer};
|
||||
use reth_rpc_types::{
|
||||
BlockDetails, ContractCreator, InternalOperation, OtsBlockTransactions, TraceEntry,
|
||||
Transaction, TransactionsWithReceipts,
|
||||
};
|
||||
|
||||
/// Otterscan Api
|
||||
#[derive(Debug)]
|
||||
pub struct OtterscanApi<Eth> {
|
||||
eth: Eth,
|
||||
}
|
||||
|
||||
impl<Eth> OtterscanApi<Eth> {
|
||||
/// Creates a new instance of `Otterscan`.
|
||||
pub fn new(eth: Eth) -> Self {
|
||||
Self { eth }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<Eth> OtterscanServer for OtterscanApi<Eth>
|
||||
where
|
||||
Eth: EthApiServer,
|
||||
{
|
||||
/// Handler for `ots_hasCode`
|
||||
async fn has_code(&self, address: Address, block_number: Option<BlockId>) -> RpcResult<bool> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `ots_getApiLevel`
|
||||
async fn get_api_level(&self) -> RpcResult<u64> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `ots_getInternalOperations`
|
||||
async fn get_internal_operations(&self, tx_hash: TxHash) -> RpcResult<Vec<InternalOperation>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `ots_getTransactionError`
|
||||
async fn get_transaction_error(&self, tx_hash: TxHash) -> RpcResult<String> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `ots_traceTransaction`
|
||||
async fn trace_transaction(&self, tx_hash: TxHash) -> RpcResult<TraceEntry> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `ots_getBlockDetails`
|
||||
async fn get_block_details(
|
||||
&self,
|
||||
block_number: BlockNumberOrTag,
|
||||
) -> RpcResult<Option<BlockDetails>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `getBlockDetailsByHash`
|
||||
async fn get_block_details_by_hash(&self, block_hash: H256) -> RpcResult<Option<BlockDetails>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `getBlockTransactions`
|
||||
async fn get_block_transactions(
|
||||
&self,
|
||||
block_number: BlockNumberOrTag,
|
||||
page_number: usize,
|
||||
page_size: usize,
|
||||
) -> RpcResult<OtsBlockTransactions> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `searchTransactionsBefore`
|
||||
async fn search_transactions_before(
|
||||
&self,
|
||||
address: Address,
|
||||
block_number: BlockNumberOrTag,
|
||||
page_size: usize,
|
||||
) -> RpcResult<TransactionsWithReceipts> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `searchTransactionsAfter`
|
||||
async fn search_transactions_after(
|
||||
&self,
|
||||
address: Address,
|
||||
block_number: BlockNumberOrTag,
|
||||
page_size: usize,
|
||||
) -> RpcResult<TransactionsWithReceipts> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `getTransactionBySenderAndNonce`
|
||||
async fn get_transaction_by_sender_and_nonce(
|
||||
&self,
|
||||
sender: Address,
|
||||
nonce: u64,
|
||||
) -> RpcResult<Option<Transaction>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `getContractCreator`
|
||||
async fn get_contract_creator(&self, address: Address) -> RpcResult<Option<ContractCreator>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user