alloy_zksync/network/
receipt_response.rs

1use serde::{Deserialize, Serialize};
2
3use alloy::consensus::TxReceipt;
4use alloy::primitives::{Address, B256, BlockHash, Bloom, TxHash, U64};
5use alloy::rpc::types::{Log, TransactionReceipt};
6
7use super::receipt_envelope::ReceiptEnvelope;
8use crate::types::*;
9
10/// Transaction receipt type that includes L2 specific fields.
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12#[serde(rename_all = "camelCase")]
13pub struct ReceiptResponse<T = ReceiptEnvelope<Log>> {
14    /// Standard transaction receipt data.
15    #[serde(flatten)]
16    inner: TransactionReceipt<T>,
17    /// Number of the l1 batch this transaction was included within.
18    l1_batch_number: Option<U64>,
19    /// Index of transaction in l1 batch.
20    l1_batch_tx_index: Option<U64>,
21    /// L2 to L1 logs generated within this transaction.
22    l2_to_l1_logs: Vec<L2ToL1Log>,
23}
24
25impl ReceiptResponse {
26    /// Logs generated within this transaction.
27    pub fn logs(&self) -> &[Log] {
28        self.inner.inner.logs()
29    }
30    /// Transaction receipt's logs bloom.
31    pub fn logs_bloom(&self) -> Bloom {
32        self.inner.inner.bloom()
33    }
34    /// Number of the l1 batch this transaction was included within.
35    pub fn l1_batch_number(&self) -> Option<U64> {
36        self.l1_batch_number
37    }
38    /// Index of transaction in l1 batch.
39    pub fn l1_batch_tx_index(&self) -> Option<U64> {
40        self.l1_batch_tx_index
41    }
42    /// L2 to L1 logs generated within this transaction.
43    pub fn l2_to_l1_logs(&self) -> &[L2ToL1Log] {
44        &self.l2_to_l1_logs
45    }
46
47    /// Returns the authorization list for the transaction.
48    pub fn authorization_list(&self) -> Option<&Vec<Address>> {
49        None
50    }
51}
52
53impl<T: TxReceipt<Log = Log>> alloy::network::ReceiptResponse for ReceiptResponse<T> {
54    /// Address of the created contract, or `None` if the transaction was not a deployment.
55    fn contract_address(&self) -> Option<Address> {
56        self.inner.contract_address()
57    }
58
59    /// Status of the transaction.
60    fn status(&self) -> bool {
61        self.inner.status()
62    }
63
64    /// Hash of the block this transaction was included within.
65    fn block_hash(&self) -> Option<BlockHash> {
66        self.inner.block_hash()
67    }
68
69    /// Number of the block this transaction was included within.
70    fn block_number(&self) -> Option<u64> {
71        self.inner.block_number()
72    }
73
74    /// Transaction Hash.
75    fn transaction_hash(&self) -> TxHash {
76        self.inner.transaction_hash()
77    }
78
79    /// Index within the block.
80    fn transaction_index(&self) -> Option<u64> {
81        self.inner.transaction_index()
82    }
83
84    /// Gas used by this transaction alone.
85    fn gas_used(&self) -> u64 {
86        self.inner.gas_used()
87    }
88
89    /// Effective gas price.
90    fn effective_gas_price(&self) -> u128 {
91        self.inner.effective_gas_price()
92    }
93
94    /// Blob gas used by the eip-4844 transaction.
95    fn blob_gas_used(&self) -> Option<u64> {
96        self.inner.blob_gas_used()
97    }
98
99    /// Blob gas price paid by the eip-4844 transaction.
100    fn blob_gas_price(&self) -> Option<u128> {
101        self.inner.blob_gas_price()
102    }
103
104    /// Address of the sender.
105    fn from(&self) -> Address {
106        self.inner.from()
107    }
108
109    /// Address of the receiver.
110    fn to(&self) -> Option<Address> {
111        self.inner.to()
112    }
113
114    /// Returns the cumulative gas used at this receipt.
115    fn cumulative_gas_used(&self) -> u64 {
116        self.inner.cumulative_gas_used()
117    }
118
119    /// The post-transaction state root (pre Byzantium)
120    ///
121    /// EIP98 makes this field optional.
122    fn state_root(&self) -> Option<B256> {
123        self.inner.state_root()
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use std::str::FromStr;
130
131    use super::*;
132    use alloy::network::ReceiptResponse as AlloyReceiptResponse;
133    use alloy::primitives::U256;
134    use alloy::primitives::address;
135
136    #[tokio::test(flavor = "multi_thread")]
137    async fn receipt_test() {
138        let receipt_json = r#"
139        {
140            "blockHash": "0x5046bdc714b2a9b40e9fbfdfc5140371c1b03b40335d908de92a7686dcc067e9",
141            "blockNumber": "0x1d1551e",
142            "contractAddress": "0x0000000000000000000000000000000000008006",
143            "cumulativeGasUsed": "0x0",
144            "effectiveGasPrice": "0x17d7840",
145            "from": "0x1bc3366b3664c01b8687b1efcfc6478d9351a8a9",
146            "gasUsed": "0x2b9bcb",
147            "l1BatchNumber": "0x72ae1",
148            "l1BatchTxIndex": "0x469",
149            "l2ToL1Logs": [
150                {
151                    "blockHash": "0x5046bdc714b2a9b40e9fbfdfc5140371c1b03b40335d908de92a7686dcc067e9",
152                    "blockNumber": "30496030",
153                    "isService": true,
154                    "key": "0x000000000000000000000000000000000000000000000000000000000000800a",
155                    "l1BatchNumber": "0x72ae1",
156                    "logIndex": "0x0",
157                    "sender": "0x0000000000000000000000000000000000008008",
158                    "shardId": "0x0",
159                    "transactionHash": "0xb2adc4d2b3203e186001dc37fdf02cc8e772518425d263adc6a17dbddff3bfda",
160                    "transactionIndex": "0x0",
161                    "transactionLogIndex": "0x0",
162                    "txIndexInL1Batch": "0x12d",
163                    "value": "0x30c635c6a0084404145f3723046c1c1b21eb5ccbb97893c90747c7a8bd83a641"
164                }
165            ],
166            "logs": [
167                {
168                    "address": "0x000000000000000000000000000000000000800a",
169                    "blockHash": "0x5046bdc714b2a9b40e9fbfdfc5140371c1b03b40335d908de92a7686dcc067e9",
170                    "blockNumber": "0x1d1551e",
171                    "blockTimestamp": "0x660c1740",
172                    "data": "0x0000000000000000000000000000000000000000000000000001011c8f80b6c0",
173                    "l1BatchNumber": "0x72ae1",
174                    "logIndex": "0x0",
175                    "logType": null,
176                    "removed": false,
177                    "topics": [
178                        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
179                        "0x0000000000000000000000001bc3366b3664c01b8687b1efcfc6478d9351a8a9",
180                        "0x0000000000000000000000000000000000000000000000000000000000008001"
181                    ],
182                    "transactionHash": "0xb2adc4d2b3203e186001dc37fdf02cc8e772518425d263adc6a17dbddff3bfda",
183                    "transactionIndex": "0x0",
184                    "transactionLogIndex": "0x0"
185                },
186                {
187                    "address": "0x000000000000000000000000000000000000800a",
188                    "blockHash": "0x5046bdc714b2a9b40e9fbfdfc5140371c1b03b40335d908de92a7686dcc067e9",
189                    "blockNumber": "0x1d1551e",
190                    "blockTimestamp": "0x660c1740",
191                    "data": "0x000000000000000000000000000000000000000000000000000042a896fb71c0",
192                    "l1BatchNumber": "0x72ae1",
193                    "logIndex": "0x1",
194                    "logType": null,
195                    "removed": false,
196                    "topics": [
197                        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
198                        "0x0000000000000000000000000000000000000000000000000000000000008001",
199                        "0x0000000000000000000000001bc3366b3664c01b8687b1efcfc6478d9351a8a9"
200                    ],
201                    "transactionHash": "0xb2adc4d2b3203e186001dc37fdf02cc8e772518425d263adc6a17dbddff3bfda",
202                    "transactionIndex": "0x0",
203                    "transactionLogIndex": "0x1"
204                }
205            ],
206            "logsBloom": "0x00280000000400000000000088000000000000000000008000004000800000100000000000000000002000000010000002000000000040000008000000000080000000002000840000000008000000240000000000000000000080010000080000000000020500000004000000000800000000000000000001000010000000000000000000000000000004000000000000001000000000000000004000000000000000000001100004000000000010000000000000000000000000000000000000000002008002800000080400500110402000000000000000000000000020000000000000000000000000000000000000002041000000020000000000000060",
207            "status": "0x1",
208            "to": "0x9b5def958d0f3b6955cbea4d5b7809b2fb26b059",
209            "transactionHash": "0xb2adc4d2b3203e186001dc37fdf02cc8e772518425d263adc6a17dbddff3bfda",
210            "transactionIndex": "0x0",
211            "type": "0x2",
212            "blobGasUsed": 111111,
213            "blobGasPrice": 222222
214        }
215        "#;
216
217        let receipt = serde_json::from_str::<ReceiptResponse>(receipt_json).unwrap();
218        assert_eq!(receipt.l1_batch_number(), Some(U64::from(0x72ae1)));
219        assert_eq!(receipt.l1_batch_tx_index(), Some(U64::from(0x469)));
220        assert_eq!(
221            receipt.l2_to_l1_logs(),
222            vec![L2ToL1Log {
223                block_hash: Some(
224                    B256::from_str(
225                        "0x5046bdc714b2a9b40e9fbfdfc5140371c1b03b40335d908de92a7686dcc067e9"
226                    )
227                    .unwrap()
228                ),
229                block_number: U64::from(30496030),
230                l1_batch_number: Some(U64::from(0x72ae1)),
231                log_index: U256::from(0),
232                transaction_index: U64::from(0),
233                transaction_hash: B256::from_str(
234                    "0xb2adc4d2b3203e186001dc37fdf02cc8e772518425d263adc6a17dbddff3bfda"
235                )
236                .unwrap(),
237                transaction_log_index: U256::from(0),
238                tx_index_in_l1_batch: Some(U64::from(301)),
239                shard_id: U64::from(0),
240                is_service: true,
241                sender: address!("0000000000000000000000000000000000008008"),
242                key: B256::from_str(
243                    "0x000000000000000000000000000000000000000000000000000000000000800a"
244                )
245                .unwrap(),
246                value: B256::from_str(
247                    "0x30c635c6a0084404145f3723046c1c1b21eb5ccbb97893c90747c7a8bd83a641"
248                )
249                .unwrap()
250            }]
251        );
252        assert_eq!(receipt.logs(), receipt.inner.inner.logs());
253        assert_eq!(receipt.logs_bloom(), receipt.inner.inner.bloom());
254        assert_eq!(
255            receipt.contract_address(),
256            Some(address!("0000000000000000000000000000000000008006"))
257        );
258        assert!(receipt.status());
259        assert_eq!(
260            receipt.block_hash(),
261            Some(
262                B256::from_str(
263                    "0x5046bdc714b2a9b40e9fbfdfc5140371c1b03b40335d908de92a7686dcc067e9"
264                )
265                .unwrap()
266            )
267        );
268        assert_eq!(receipt.block_number(), Some(30496030));
269        assert_eq!(
270            receipt.transaction_hash(),
271            B256::from_str("0xb2adc4d2b3203e186001dc37fdf02cc8e772518425d263adc6a17dbddff3bfda")
272                .unwrap()
273        );
274        assert_eq!(receipt.transaction_index(), Some(0));
275        assert_eq!(receipt.gas_used(), 2857931);
276        assert_eq!(receipt.effective_gas_price(), 25000000);
277        assert_eq!(receipt.blob_gas_used(), Some(111111));
278        assert_eq!(receipt.blob_gas_price(), Some(222222));
279        assert_eq!(
280            receipt.from(),
281            address!("1bc3366b3664c01b8687b1efcfc6478d9351a8a9")
282        );
283        assert_eq!(
284            receipt.to(),
285            Some(address!("9b5def958d0f3b6955cbea4d5b7809b2fb26b059"))
286        );
287        assert_eq!(receipt.authorization_list(), None);
288        assert_eq!(receipt.cumulative_gas_used(), 0);
289        assert_eq!(receipt.state_root(), None);
290    }
291}