1use alloy_consensus::{TxEnvelope, TxType, TypedTransaction};
2use alloy_eips::eip2930::AccessList;
3use alloy_network::{
4 BuildResult, Network, NetworkWallet, ReceiptResponse, TransactionBuilder,
5 TransactionBuilderError,
6};
7use alloy_primitives::{
8 Address, BlockHash, Bytes, ChainId, Log, Signature, TxHash, TxKind, B256, U256,
9};
10use alloy_provider::fillers::{
11 ChainIdFiller, GasFiller, JoinFill, NonceFiller, RecommendedFillers,
12};
13
14use pod_types::{
15 consensus::committee::CommitteeError, ledger::Transaction, metadata::DetailedReceiptMetadata,
16};
17
18use alloy_rpc_types::{TransactionReceipt, TransactionRequest};
19use pod_types::{Committee, Hashable, Merkleizable, Receipt, Timestamp};
20use serde::{Deserialize, Serialize};
21use std::ops::{Deref, DerefMut};
22
23#[derive(Debug, Clone, Copy)]
24pub struct PodNetwork;
25
26#[derive(Debug, Serialize, Deserialize, Clone)]
27pub struct PodTransactionRequest {
28 #[serde(flatten)]
29 pub inner: TransactionRequest,
30}
31
32impl Default for PodTransactionRequest {
33 fn default() -> Self {
34 let mut inner = TransactionRequest::default();
35 inner.set_max_fee_per_gas(1_000_000_000);
36 inner.set_max_priority_fee_per_gas(1_000_000_000);
37 Self { inner }
38 }
39}
40
41impl Deref for PodTransactionRequest {
42 type Target = TransactionRequest;
43
44 fn deref(&self) -> &Self::Target {
45 &self.inner
46 }
47}
48
49impl DerefMut for PodTransactionRequest {
50 fn deref_mut(&mut self) -> &mut Self::Target {
51 &mut self.inner
52 }
53}
54
55impl From<TypedTransaction> for PodTransactionRequest {
56 fn from(value: TypedTransaction) -> Self {
57 Self {
58 inner: value.into(),
59 }
60 }
61}
62
63impl From<TxEnvelope> for PodTransactionRequest {
64 fn from(value: TxEnvelope) -> Self {
65 Self {
66 inner: value.into(),
67 }
68 }
69}
70
71impl TransactionBuilder<PodNetwork> for PodTransactionRequest {
72 fn chain_id(&self) -> Option<ChainId> {
73 self.chain_id
74 }
75
76 fn set_chain_id(&mut self, chain_id: ChainId) {
77 self.chain_id = Some(chain_id);
78 }
79
80 fn nonce(&self) -> Option<u64> {
81 self.nonce
82 }
83
84 fn set_nonce(&mut self, nonce: u64) {
85 self.nonce = Some(nonce);
86 }
87
88 fn take_nonce(&mut self) -> Option<u64> {
89 self.nonce.take()
90 }
91
92 fn input(&self) -> Option<&Bytes> {
93 self.input.input()
94 }
95
96 fn set_input<T: Into<Bytes>>(&mut self, input: T) {
97 self.input.input = Some(input.into());
98 }
99
100 fn from(&self) -> Option<Address> {
101 self.from
102 }
103
104 fn set_from(&mut self, from: Address) {
105 self.from = Some(from);
106 }
107
108 fn kind(&self) -> Option<TxKind> {
109 self.to
110 }
111
112 fn clear_kind(&mut self) {
113 self.to = None;
114 }
115
116 fn set_kind(&mut self, kind: TxKind) {
117 self.to = Some(kind);
118 }
119
120 fn value(&self) -> Option<U256> {
121 self.value
122 }
123
124 fn set_value(&mut self, value: U256) {
125 self.value = Some(value)
126 }
127
128 fn gas_price(&self) -> Option<u128> {
129 self.gas_price
130 }
131
132 fn set_gas_price(&mut self, gas_price: u128) {
133 self.gas_price = Some(gas_price);
134 }
135
136 fn max_fee_per_gas(&self) -> Option<u128> {
137 self.max_fee_per_gas
138 }
139
140 fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) {
141 self.max_fee_per_gas = Some(max_fee_per_gas);
142 }
143
144 fn max_priority_fee_per_gas(&self) -> Option<u128> {
145 self.max_priority_fee_per_gas
146 }
147
148 fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) {
149 self.max_priority_fee_per_gas = Some(max_priority_fee_per_gas);
150 }
151
152 fn gas_limit(&self) -> Option<u64> {
153 self.gas
154 }
155
156 fn set_gas_limit(&mut self, gas_limit: u64) {
157 self.gas = Some(gas_limit);
158 }
159
160 fn access_list(&self) -> Option<&AccessList> {
161 self.access_list.as_ref()
162 }
163
164 fn set_access_list(&mut self, access_list: AccessList) {
165 self.access_list = Some(access_list);
166 }
167
168 fn complete_type(&self, ty: TxType) -> Result<(), Vec<&'static str>> {
169 match ty {
170 TxType::Eip1559 => self.complete_1559(),
171 _ => unimplemented!(), }
173 }
174
175 fn can_submit(&self) -> bool {
176 self.from.is_some()
180 }
181
182 fn can_build(&self) -> bool {
183 self.max_fee_per_gas.is_some() && self.max_priority_fee_per_gas.is_some()
185 }
186
187 #[doc(alias = "output_transaction_type")]
188 fn output_tx_type(&self) -> TxType {
189 TxType::Eip1559
190 }
191
192 #[doc(alias = "output_transaction_type_checked")]
193 fn output_tx_type_checked(&self) -> Option<TxType> {
194 self.buildable_type()
195 }
196
197 fn prep_for_submission(&mut self) {
198 self.transaction_type = Some(self.preferred_type() as u8);
199 self.trim_conflicting_keys();
200 self.populate_blob_hashes();
201 }
202
203 fn build_unsigned(self) -> BuildResult<TypedTransaction, PodNetwork> {
204 if let Err((tx_type, missing)) = self.missing_keys() {
205 return Err(
206 TransactionBuilderError::InvalidTransactionRequest(tx_type, missing)
207 .into_unbuilt(self),
208 );
209 }
210 Ok(self
211 .inner
212 .build_typed_tx()
213 .expect("checked by missing_keys"))
214 }
215
216 async fn build<W: NetworkWallet<PodNetwork>>(
217 self,
218 wallet: &W,
219 ) -> Result<<PodNetwork as Network>::TxEnvelope, TransactionBuilderError<PodNetwork>> {
220 Ok(wallet.sign_request(self).await?)
221 }
222}
223
224impl ReceiptResponse for PodReceiptResponse {
225 fn contract_address(&self) -> Option<Address> {
226 None
228 }
229
230 fn status(&self) -> bool {
231 self.receipt.status()
232 }
233
234 fn block_hash(&self) -> Option<BlockHash> {
235 Some(BlockHash::default())
237 }
238
239 fn block_number(&self) -> Option<u64> {
240 None
242 }
243
244 fn transaction_hash(&self) -> TxHash {
245 self.receipt.transaction_hash()
246 }
247
248 fn transaction_index(&self) -> Option<u64> {
249 None
251 }
252
253 fn gas_used(&self) -> u64 {
254 self.receipt.gas_used()
255 }
256
257 fn effective_gas_price(&self) -> u128 {
258 self.receipt.effective_gas_price()
259 }
260
261 fn blob_gas_used(&self) -> Option<u64> {
262 None
264 }
265
266 fn blob_gas_price(&self) -> Option<u128> {
267 None
269 }
270
271 fn from(&self) -> Address {
272 self.receipt.from()
273 }
274
275 fn to(&self) -> Option<Address> {
276 self.receipt.to()
277 }
278
279 fn cumulative_gas_used(&self) -> u64 {
280 self.receipt.cumulative_gas_used()
282 }
283
284 fn state_root(&self) -> Option<B256> {
285 None
287 }
288}
289
290#[derive(Debug, Serialize, Deserialize, Clone)]
291pub struct AttestationData {
292 pub public_key: Address,
293 pub signature: Signature,
294 pub timestamp: Timestamp,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct PodReceiptResponse {
299 #[serde(flatten)]
300 pub receipt: TransactionReceipt,
301 pub pod_metadata: DetailedReceiptMetadata,
302}
303
304impl PodReceiptResponse {
305 pub fn verify_receipt(&self, committee: &Committee) -> Result<(), CommitteeError> {
306 let logs = self
307 .receipt
308 .inner
309 .logs()
310 .iter()
311 .map(|l| l.inner.clone())
312 .collect::<Vec<Log>>();
313
314 let logs_root = logs.to_merkle_tree().hash_custom();
315 let tx_hash = self.pod_metadata.transaction.hash_custom();
316 let to = match self.pod_metadata.transaction.to {
317 TxKind::Create => None,
318 TxKind::Call(address) => Some(address),
319 };
320
321 let receipt = Receipt {
322 status: self.status(),
323 actual_gas_used: self.receipt.gas_used,
324 logs,
325 logs_root,
326 tx_hash,
327 max_fee_per_gas: self.pod_metadata.transaction.max_fee_per_gas,
328 signer: self.pod_metadata.transaction.signer,
329 to,
330 contract_address: self.receipt.contract_address,
331 };
332
333 committee.verify_aggregate_attestation(
334 receipt.tx_hash,
335 &self
336 .pod_metadata
337 .attestations
338 .iter()
339 .map(|a| a.signature)
340 .collect(),
341 )?;
342
343 committee.verify_aggregate_attestation(
344 receipt.hash_custom(),
345 &self
346 .pod_metadata
347 .receipt_attestations
348 .iter()
349 .map(|a| a.signature)
350 .collect(),
351 )?;
352
353 Ok(())
354 }
355
356 pub fn transaction(&self) -> &pod_types::Signed<Transaction> {
357 &self.pod_metadata.transaction
358 }
359}
360
361impl Deref for PodReceiptResponse {
362 type Target = TransactionReceipt;
363 fn deref(&self) -> &TransactionReceipt {
364 &self.receipt
365 }
366}
367
368impl Network for PodNetwork {
369 type TxType = TxType;
370 type TxEnvelope = alloy_consensus::TxEnvelope;
371 type UnsignedTx = TypedTransaction;
372 type ReceiptEnvelope = alloy_consensus::ReceiptEnvelope;
373 type Header = alloy_consensus::Header;
374 type TransactionRequest = PodTransactionRequest;
375 type TransactionResponse = alloy_rpc_types::Transaction;
376 type ReceiptResponse = PodReceiptResponse;
377 type HeaderResponse = alloy_rpc_types::Header;
378 type BlockResponse = alloy_rpc_types::Block;
379}
380
381impl RecommendedFillers for PodNetwork {
382 type RecommendedFillers = JoinFill<GasFiller, JoinFill<NonceFiller, ChainIdFiller>>;
383
384 fn recommended_fillers() -> Self::RecommendedFillers {
385 JoinFill::new(
386 GasFiller,
387 JoinFill::new(NonceFiller::default(), ChainIdFiller::default()),
388 )
389 }
390}