axiom_circuit/subquery/
receipt.rs1use anyhow::{bail, Result};
2use axiom_codec::{
3 special_values::{
4 RECEIPT_ADDRESS_IDX, RECEIPT_BLOCK_NUMBER_FIELD_IDX, RECEIPT_DATA_IDX_OFFSET,
5 RECEIPT_LOGS_BLOOM_IDX_OFFSET, RECEIPT_LOG_IDX_OFFSET, RECEIPT_TX_INDEX_FIELD_IDX,
6 RECEIPT_TX_TYPE_FIELD_IDX,
7 },
8 types::native::{AnySubquery, ReceiptSubquery},
9};
10use axiom_query::axiom_eth::{halo2_base::AssignedValue, Field};
11use ethers::{
12 providers::{JsonRpcClient, Middleware, Provider},
13 types::{BigEndianHash, BlockId, H256},
14};
15use num_derive::FromPrimitive;
16use num_traits::FromPrimitive;
17use tokio::runtime::Runtime;
18
19use super::{caller::FetchSubquery, types::AssignedReceiptSubquery, utils::pad_to_bytes32};
20use crate::impl_fr_from;
21
22#[derive(FromPrimitive)]
23pub enum ReceiptField {
24 Status, PostState, CumulativeGas,
27 LogsBloom,
28 Logs,
29 TxType = RECEIPT_TX_TYPE_FIELD_IDX as isize,
30 BlockNumber = RECEIPT_BLOCK_NUMBER_FIELD_IDX as isize,
31 TxIndex = RECEIPT_TX_INDEX_FIELD_IDX as isize,
32}
33impl_fr_from!(ReceiptField);
34
35pub async fn get_receipt_field_value<P: JsonRpcClient>(
36 provider: &Provider<P>,
37 query: ReceiptSubquery,
38) -> Result<H256> {
39 let block_id = BlockId::from(query.block_number as u64);
40 let tx = provider
41 .get_transaction_by_block_and_index(block_id, query.tx_idx.into())
42 .await?;
43 let tx_hash = tx.unwrap().hash;
44 let receipt = provider.get_transaction_receipt(tx_hash).await?.unwrap();
45 let field_or_log_idx = query.field_or_log_idx as usize;
47 if (RECEIPT_LOGS_BLOOM_IDX_OFFSET..RECEIPT_LOGS_BLOOM_IDX_OFFSET + 8)
48 .contains(&field_or_log_idx)
49 {
50 let bloom = receipt.logs_bloom.to_fixed_bytes();
51 let log_idx = (field_or_log_idx - RECEIPT_LOGS_BLOOM_IDX_OFFSET) * 32;
52 return Ok(H256::from_slice(&bloom[log_idx..log_idx + 32]));
53 }
54
55 if field_or_log_idx >= RECEIPT_LOG_IDX_OFFSET {
56 let log_idx = field_or_log_idx - RECEIPT_LOG_IDX_OFFSET;
57 if log_idx >= receipt.logs.len() {
58 bail!("Log does not exist")
59 }
60 let log = receipt.logs[log_idx].clone();
61 let topics = log.topics;
62 if query.event_schema != H256::zero() && query.event_schema != topics[0] {
63 bail!("Log does not match event schema")
64 }
65
66 let topic_or_data_or_address_idx = query.topic_or_data_or_address_idx as usize;
67
68 if topic_or_data_or_address_idx == RECEIPT_ADDRESS_IDX {
69 return Ok(log.address.into());
70 } else if topic_or_data_or_address_idx < RECEIPT_DATA_IDX_OFFSET {
71 if topic_or_data_or_address_idx > topics.len() {
72 bail!("Topic does not exist")
73 }
74
75 if topic_or_data_or_address_idx < topics.len() {
76 return Ok(topics[topic_or_data_or_address_idx]);
77 }
78
79 if topic_or_data_or_address_idx == topics.len() {
80 return Ok(log.address.into());
81 }
82 } else {
83 let data_idx = topic_or_data_or_address_idx - RECEIPT_DATA_IDX_OFFSET;
84 if data_idx >= log.data.len() / 32 {
85 bail!("Data does not exist")
86 }
87 let data_bytes = &log.data[data_idx * 32..(data_idx + 1) * 32];
88 return Ok(H256::from_slice(data_bytes));
89 }
90 }
91
92 let receipt_field_idx =
93 ReceiptField::from_usize(field_or_log_idx).expect("Invalid field index");
94 let val = match receipt_field_idx {
95 ReceiptField::Status => H256::from_low_u64_be(receipt.status.unwrap().as_u64()),
96 ReceiptField::PostState => receipt.root.unwrap(),
97 ReceiptField::CumulativeGas => H256::from_uint(&receipt.cumulative_gas_used),
98 ReceiptField::LogsBloom => {
99 let logs_bloom = receipt.logs_bloom;
100 H256::from(pad_to_bytes32(logs_bloom.as_fixed_bytes()))
101 }
102 ReceiptField::Logs => {
103 bail!("Use log idx instead of logs field")
104 }
105 ReceiptField::TxType => H256::from_low_u64_be(receipt.transaction_type.unwrap().as_u64()),
106 ReceiptField::BlockNumber => H256::from_low_u64_be(receipt.block_number.unwrap().as_u64()),
107 ReceiptField::TxIndex => H256::from_low_u64_be(receipt.transaction_index.as_u64()),
108 };
109
110 Ok(val)
111}
112
113impl<F: Field> FetchSubquery<F> for AssignedReceiptSubquery<F> {
114 fn fetch<P: JsonRpcClient>(&self, p: &Provider<P>) -> Result<H256> {
115 let rt = Runtime::new()?;
116 let val = rt.block_on(get_receipt_field_value(p, (*self).into()))?;
117 Ok(val)
118 }
119
120 fn any_subquery(&self) -> AnySubquery {
121 AnySubquery::Receipt((*self).into())
122 }
123
124 fn flatten(&self) -> Vec<AssignedValue<F>> {
125 vec![
126 self.block_number,
127 self.tx_idx,
128 self.field_or_log_idx,
129 self.topic_or_data_or_address_idx,
130 self.event_schema.hi(),
131 self.event_schema.lo(),
132 ]
133 }
134}