From 3a2e126ac776ccc534e7763cc3d6f48d35997923 Mon Sep 17 00:00:00 2001 From: Delweng Date: Fri, 12 Jul 2024 18:47:29 +0800 Subject: [PATCH] feat(otterscan): add ots slim block and serialze OperationType to int (#1043) * fix(otterscan): add OtsSlimBlock for BlockDetails Signed-off-by: jsvisa * feat(otterscan): use serde_repr to serialize OperationType to int Signed-off-by: jsvisa * feat(otterscan): add a new function for BlockDetails Signed-off-by: jsvisa * Revert "feat(otterscan): use serde_repr to serialize OperationType to int" This reverts commit 96df6f4d53d673f8d9cf59c569ff52b3f5cf4be4. Signed-off-by: jsvisa * feat(otterscan): custom implement Serialize for OperationType Signed-off-by: jsvisa * (de)serialize to u8 Signed-off-by: jsvisa * clippy Signed-off-by: jsvisa --------- Signed-off-by: jsvisa --- crates/rpc-types-trace/src/otterscan.rs | 114 ++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 5 deletions(-) diff --git a/crates/rpc-types-trace/src/otterscan.rs b/crates/rpc-types-trace/src/otterscan.rs index 85114451c9f..f5a71778cb2 100644 --- a/crates/rpc-types-trace/src/otterscan.rs +++ b/crates/rpc-types-trace/src/otterscan.rs @@ -3,12 +3,15 @@ //! //! -use alloy_primitives::{Address, Bloom, Bytes, TxHash, U256}; -use alloy_rpc_types_eth::{Block, Rich, Transaction, TransactionReceipt}; -use serde::{Deserialize, Serialize}; +use alloy_primitives::{Address, Bloom, Bytes, TxHash, B256, U256}; +use alloy_rpc_types_eth::{Block, Header, Rich, Transaction, TransactionReceipt, Withdrawal}; +use serde::{ + de::{self, Unexpected}, + Deserialize, Deserializer, Serialize, Serializer, +}; /// Operation type enum for `InternalOperation` struct -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum OperationType { /// Operation Transfer OpTransfer = 0, @@ -20,6 +23,37 @@ pub enum OperationType { OpCreate2 = 3, } +// Implement Serialize for OperationType +impl Serialize for OperationType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u8(*self as u8) + } +} + +// Implement Deserialize for OperationType +impl<'de> Deserialize<'de> for OperationType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + // Deserialize string, then parse it to u8 + let value = u8::deserialize(deserializer)?; + match value { + 0 => Ok(Self::OpTransfer), + 1 => Ok(Self::OpSelfDestruct), + 2 => Ok(Self::OpCreate), + 3 => Ok(Self::OpCreate2), + other => Err(de::Error::invalid_value( + Unexpected::Unsigned(other as u64), + &"a valid OperationType", + )), + } + } +} + /// Custom struct for otterscan `getInternalOperations` RPC response #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct InternalOperation { @@ -83,12 +117,45 @@ impl From for OtsBlock { } } +/// Custom `Block` struct that without transactions for Otterscan responses +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct OtsSlimBlock { + /// Header of the block. + #[serde(flatten)] + pub header: Header, + /// Uncles' hashes. + #[serde(default)] + pub uncles: Vec, + /// Integer the size of this block in bytes. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub size: Option, + /// Withdrawals in the block. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub withdrawals: Option>, + /// The number of transactions in the block. + #[doc(alias = "tx_count")] + pub transaction_count: usize, +} + +impl From for OtsSlimBlock { + fn from(block: Block) -> Self { + Self { + header: block.header, + uncles: block.uncles, + size: block.size, + withdrawals: block.withdrawals, + transaction_count: block.transactions.len(), + } + } +} + /// Custom struct for otterscan `getBlockDetails` RPC response #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BlockDetails { /// The block information with transaction count. - pub block: OtsBlock, + pub block: OtsSlimBlock, /// The issuance information for the block. pub issuance: InternalIssuance, /// The total fees for the block. @@ -105,6 +172,13 @@ impl From> for BlockDetails { } } +impl BlockDetails { + /// Create a new `BlockDetails` struct. + pub fn new(rich_block: Rich, issuance: InternalIssuance, total_fees: U256) -> Self { + Self { block: rich_block.inner.into(), issuance, total_fees } + } +} + /// Custom transaction receipt struct for otterscan `OtsBlockTransactions` struct #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -204,4 +278,34 @@ mod tests { let _receipt: OtsTransactionReceipt = serde_json::from_str(s).unwrap(); } + + #[test] + fn test_otterscan_interal_operation() { + let s = r#"{ + "type": 0, + "from": "0xea593b730d745fb5fe01b6d20e6603915252c6bf", + "to": "0xcc3d455481967dc97346ef1771a112d7a14c8f12", + "value": "0xee846f9305c00" + }"#; + let _op: InternalOperation = serde_json::from_str(s).unwrap(); + } + + #[test] + fn test_serialize_operation_type() { + assert_eq!(serde_json::to_string(&OperationType::OpTransfer).unwrap(), "0"); + assert_eq!(serde_json::to_string(&OperationType::OpSelfDestruct).unwrap(), "1"); + assert_eq!(serde_json::to_string(&OperationType::OpCreate).unwrap(), "2"); + assert_eq!(serde_json::to_string(&OperationType::OpCreate2).unwrap(), "3"); + } + + #[test] + fn test_deserialize_operation_type() { + assert_eq!(serde_json::from_str::("0").unwrap(), OperationType::OpTransfer); + assert_eq!( + serde_json::from_str::("1").unwrap(), + OperationType::OpSelfDestruct + ); + assert_eq!(serde_json::from_str::("2").unwrap(), OperationType::OpCreate); + assert_eq!(serde_json::from_str::("3").unwrap(), OperationType::OpCreate2); + } }