1use std::{fmt::Display, str::FromStr};
2
3use alloy::{consensus::Eip658Value, primitives::U256};
4use anyhow::{bail, Result};
5use eth_trie_proofs::{tx::ConsensusTx, tx_receipt::ConsensusTxReceipt};
6use serde::{Deserialize, Serialize};
7
8use crate::task::datalake::DatalakeField;
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub enum TransactionField {
12 Nonce,
14 GasPrice,
15 GasLimit,
16 To,
17 Value,
18 Input,
19 V,
20 R,
21 S,
22 ChainId,
23 AccessList,
25
26 MaxFeePerGas,
28 MaxPriorityFeePerGas,
30
31 BlobVersionedHashes,
33 MaxFeePerBlobGas,
35}
36
37impl TransactionField {
38 pub fn variants() -> Vec<String> {
39 vec![
40 "NONCE".to_string(),
41 "GAS_PRICE".to_string(),
42 "GAS_LIMIT".to_string(),
43 "TO".to_string(),
44 "VALUE".to_string(),
45 "INPUT".to_string(),
46 "V".to_string(),
47 "R".to_string(),
48 "S".to_string(),
49 "CHAIN_ID".to_string(),
50 "ACCESS_LIST".to_string(),
51 "MAX_FEE_PER_GAS".to_string(),
52 "MAX_PRIORITY_FEE_PER_GAS".to_string(),
53 "BLOB_VERSIONED_HASHES".to_string(),
54 "MAX_FEE_PER_BLOB_GAS".to_string(),
55 ]
56 }
57
58 pub fn integer_variants_index(index: u8) -> Self {
60 match index {
61 0 => TransactionField::Nonce,
62 1 => TransactionField::GasPrice,
63 2 => TransactionField::GasLimit,
64 3 => TransactionField::ChainId,
65 4 => TransactionField::MaxFeePerGas,
66 5 => TransactionField::MaxPriorityFeePerGas,
67 6 => TransactionField::MaxFeePerBlobGas,
68 _ => unreachable!(),
69 }
70 }
71}
72
73impl DatalakeField for TransactionField {
76 fn from_index(index: u8) -> Result<Self> {
77 match index {
78 0 => Ok(TransactionField::Nonce),
79 1 => Ok(TransactionField::GasPrice),
80 2 => Ok(TransactionField::GasLimit),
81 3 => Ok(TransactionField::To),
82 4 => Ok(TransactionField::Value),
83 5 => Ok(TransactionField::Input),
84 6 => Ok(TransactionField::V),
85 7 => Ok(TransactionField::R),
86 8 => Ok(TransactionField::S),
87 9 => Ok(TransactionField::ChainId),
88 10 => Ok(TransactionField::AccessList),
89 11 => Ok(TransactionField::MaxFeePerGas),
90 12 => Ok(TransactionField::MaxPriorityFeePerGas),
91 13 => Ok(TransactionField::BlobVersionedHashes),
92 14 => Ok(TransactionField::MaxFeePerBlobGas),
93 _ => bail!("Invalid transaction field index"),
94 }
95 }
96
97 fn to_index(&self) -> u8 {
98 match self {
99 TransactionField::Nonce => 0,
100 TransactionField::GasPrice => 1,
101 TransactionField::GasLimit => 2,
102 TransactionField::To => 3,
103 TransactionField::Value => 4,
104 TransactionField::Input => 5,
105 TransactionField::V => 6,
106 TransactionField::R => 7,
107 TransactionField::S => 8,
108 TransactionField::ChainId => 9,
109 TransactionField::AccessList => 10,
110 TransactionField::MaxFeePerGas => 11,
111 TransactionField::MaxPriorityFeePerGas => 12,
112 TransactionField::BlobVersionedHashes => 13,
113 TransactionField::MaxFeePerBlobGas => 14,
114 }
115 }
116
117 fn decode_field_from_rlp(&self, rlp: &[u8]) -> U256 {
118 let raw_tx = ConsensusTx::rlp_decode(rlp).unwrap();
119 match self {
120 TransactionField::Nonce => U256::from(raw_tx.nonce()),
121 TransactionField::GasPrice => {
122 U256::from(raw_tx.gas_price().expect("gas price does not exist"))
123 }
124 TransactionField::GasLimit => U256::from(raw_tx.gas_limit()),
125 TransactionField::To => U256::from_str_radix(
126 &raw_tx.to().to().expect("to does not exist").to_string(),
127 16,
128 )
129 .unwrap(),
130 TransactionField::Value => U256::from(raw_tx.value()),
131 TransactionField::Input => U256::from_be_slice(raw_tx.input()),
132 TransactionField::V => U256::from(raw_tx.v()),
133 TransactionField::R => U256::from(raw_tx.r()),
134 TransactionField::S => U256::from(raw_tx.s()),
135 TransactionField::ChainId => {
136 U256::from(raw_tx.chain_id().expect("chain id does not exist"))
137 }
138 TransactionField::AccessList => todo!("access list cannot parse into u256"),
140 TransactionField::MaxFeePerGas => U256::from(
141 raw_tx
142 .max_fee_per_gas()
143 .expect("max fee per gas does not exist"),
144 ),
145 TransactionField::MaxPriorityFeePerGas => U256::from(
146 raw_tx
147 .max_priority_fee_per_gas()
148 .expect("max priority fee per gas does not exist"),
149 ),
150 TransactionField::BlobVersionedHashes => raw_tx
151 .blob_versioned_hashes()
152 .expect("blob versioned hashes does not exist")[0]
153 .into(),
154 TransactionField::MaxFeePerBlobGas => U256::from(
155 raw_tx
156 .max_fee_per_blob_gas()
157 .expect("max fee per blob gas does not exist"),
158 ),
159 }
160 }
161}
162
163impl FromStr for TransactionField {
164 type Err = anyhow::Error;
165
166 fn from_str(s: &str) -> Result<Self> {
167 match s {
168 "NONCE" => Ok(TransactionField::Nonce),
169 "GAS_PRICE" => Ok(TransactionField::GasPrice),
170 "GAS_LIMIT" => Ok(TransactionField::GasLimit),
171 "TO" => Ok(TransactionField::To),
172 "VALUE" => Ok(TransactionField::Value),
173 "INPUT" => Ok(TransactionField::Input),
174 "V" => Ok(TransactionField::V),
175 "R" => Ok(TransactionField::R),
176 "S" => Ok(TransactionField::S),
177 "CHAIN_ID" => Ok(TransactionField::ChainId),
178 "ACCESS_LIST" => Ok(TransactionField::AccessList),
179 "MAX_FEE_PER_GAS" => Ok(TransactionField::MaxFeePerGas),
180 "MAX_PRIORITY_FEE_PER_GAS" => Ok(TransactionField::MaxPriorityFeePerGas),
181 "BLOB_VERSIONED_HASHES" => Ok(TransactionField::BlobVersionedHashes),
182 "MAX_FEE_PER_BLOB_GAS" => Ok(TransactionField::MaxFeePerBlobGas),
183 _ => bail!("Unknown transaction datalake field"),
184 }
185 }
186}
187
188impl Display for TransactionField {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 match self {
191 TransactionField::Nonce => write!(f, "NONCE"),
192 TransactionField::GasPrice => write!(f, "GAS_PRICE"),
193 TransactionField::GasLimit => write!(f, "GAS_LIMIT"),
194 TransactionField::To => write!(f, "TO"),
195 TransactionField::Value => write!(f, "VALUE"),
196 TransactionField::Input => write!(f, "INPUT"),
197 TransactionField::V => write!(f, "V"),
198 TransactionField::R => write!(f, "R"),
199 TransactionField::S => write!(f, "S"),
200 TransactionField::ChainId => write!(f, "CHAIN_ID"),
201 TransactionField::AccessList => write!(f, "ACCESS_LIST"),
202 TransactionField::MaxFeePerGas => write!(f, "MAX_FEE_PER_GAS"),
203 TransactionField::MaxPriorityFeePerGas => write!(f, "MAX_PRIORITY_FEE_PER_GAS"),
204 TransactionField::BlobVersionedHashes => write!(f, "BLOB_VERSIONED_HASHES"),
205 TransactionField::MaxFeePerBlobGas => write!(f, "MAX_FEE_PER_BLOB_GAS"),
206 }
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
211pub enum TransactionReceiptField {
212 Success,
213 CumulativeGasUsed,
214 Logs,
215 Bloom,
216}
217
218impl TransactionReceiptField {
219 pub fn variants() -> Vec<String> {
220 vec![
221 "SUCCESS".to_string(),
222 "CUMULATIVE_GAS_USED".to_string(),
223 "LOGS".to_string(),
224 "BLOOM".to_string(),
225 ]
226 }
227}
228
229impl FromStr for TransactionReceiptField {
230 type Err = anyhow::Error;
231
232 fn from_str(s: &str) -> Result<Self> {
233 match s {
234 "SUCCESS" => Ok(TransactionReceiptField::Success),
235 "CUMULATIVE_GAS_USED" => Ok(TransactionReceiptField::CumulativeGasUsed),
236 "LOGS" => Ok(TransactionReceiptField::Logs),
237 "BLOOM" => Ok(TransactionReceiptField::Bloom),
238 _ => bail!("Unknown transaction receipt field"),
239 }
240 }
241}
242
243impl Display for TransactionReceiptField {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 match self {
246 TransactionReceiptField::Success => write!(f, "SUCCESS"),
247 TransactionReceiptField::CumulativeGasUsed => write!(f, "CUMULATIVE_GAS_USED"),
248 TransactionReceiptField::Logs => write!(f, "LOGS"),
249 TransactionReceiptField::Bloom => write!(f, "BLOOM"),
250 }
251 }
252}
253
254impl DatalakeField for TransactionReceiptField {
255 fn to_index(&self) -> u8 {
256 match self {
257 TransactionReceiptField::Success => 0,
258 TransactionReceiptField::CumulativeGasUsed => 1,
259 TransactionReceiptField::Logs => 2,
260 TransactionReceiptField::Bloom => 3,
261 }
262 }
263
264 fn from_index(index: u8) -> Result<Self> {
265 match index {
266 0 => Ok(TransactionReceiptField::Success),
267 1 => Ok(TransactionReceiptField::CumulativeGasUsed),
268 2 => Ok(TransactionReceiptField::Logs),
269 3 => Ok(TransactionReceiptField::Bloom),
270 _ => bail!("Invalid transaction receipt field index"),
271 }
272 }
273
274 fn decode_field_from_rlp(&self, rlp: &[u8]) -> U256 {
275 let raw_tx_receipt = ConsensusTxReceipt::rlp_decode(rlp).unwrap();
276
277 match self {
278 TransactionReceiptField::Success => match raw_tx_receipt.status() {
279 Eip658Value::Eip658(bool) => U256::from(*bool as u8),
280 Eip658Value::PostState(state) => (*state).into(),
281 },
282 TransactionReceiptField::CumulativeGasUsed => {
283 U256::from(raw_tx_receipt.cumulative_gas_used())
284 }
285 TransactionReceiptField::Logs => U256::from(raw_tx_receipt.logs().len()),
287 TransactionReceiptField::Bloom => U256::from(raw_tx_receipt.bloom().len()),
288 }
289 }
290}