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, PrimitiveSignature, 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 input(&self) -> Option<&Bytes> {
89 self.input.input()
90 }
91
92 fn set_input<T: Into<Bytes>>(&mut self, input: T) {
93 self.input.input = Some(input.into());
94 }
95
96 fn from(&self) -> Option<Address> {
97 self.from
98 }
99
100 fn set_from(&mut self, from: Address) {
101 self.from = Some(from);
102 }
103
104 fn kind(&self) -> Option<TxKind> {
105 self.to
106 }
107
108 fn clear_kind(&mut self) {
109 self.to = None;
110 }
111
112 fn set_kind(&mut self, kind: TxKind) {
113 self.to = Some(kind);
114 }
115
116 fn value(&self) -> Option<U256> {
117 self.value
118 }
119
120 fn set_value(&mut self, value: U256) {
121 self.value = Some(value)
122 }
123
124 fn gas_price(&self) -> Option<u128> {
125 self.gas_price
126 }
127
128 fn set_gas_price(&mut self, gas_price: u128) {
129 self.gas_price = Some(gas_price);
130 }
131
132 fn max_fee_per_gas(&self) -> Option<u128> {
133 self.max_fee_per_gas
134 }
135
136 fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) {
137 self.max_fee_per_gas = Some(max_fee_per_gas);
138 }
139
140 fn max_priority_fee_per_gas(&self) -> Option<u128> {
141 self.max_priority_fee_per_gas
142 }
143
144 fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) {
145 self.max_priority_fee_per_gas = Some(max_priority_fee_per_gas);
146 }
147
148 fn gas_limit(&self) -> Option<u64> {
149 self.gas
150 }
151
152 fn set_gas_limit(&mut self, gas_limit: u64) {
153 self.gas = Some(gas_limit);
154 }
155
156 fn access_list(&self) -> Option<&AccessList> {
157 self.access_list.as_ref()
158 }
159
160 fn set_access_list(&mut self, access_list: AccessList) {
161 self.access_list = Some(access_list);
162 }
163
164 fn complete_type(&self, ty: TxType) -> Result<(), Vec<&'static str>> {
165 match ty {
166 TxType::Eip1559 => self.complete_1559(),
167 _ => unimplemented!(), }
169 }
170
171 fn can_submit(&self) -> bool {
172 self.from.is_some()
176 }
177
178 fn can_build(&self) -> bool {
179 self.max_fee_per_gas.is_some() && self.max_priority_fee_per_gas.is_some()
181 }
182
183 #[doc(alias = "output_transaction_type")]
184 fn output_tx_type(&self) -> TxType {
185 TxType::Eip1559
186 }
187
188 #[doc(alias = "output_transaction_type_checked")]
189 fn output_tx_type_checked(&self) -> Option<TxType> {
190 self.buildable_type()
191 }
192
193 fn prep_for_submission(&mut self) {
194 self.transaction_type = Some(self.preferred_type() as u8);
195 self.trim_conflicting_keys();
196 self.populate_blob_hashes();
197 }
198
199 fn build_unsigned(self) -> BuildResult<TypedTransaction, PodNetwork> {
200 if let Err((tx_type, missing)) = self.missing_keys() {
201 return Err(
202 TransactionBuilderError::InvalidTransactionRequest(tx_type, missing)
203 .into_unbuilt(self),
204 );
205 }
206 Ok(self
207 .inner
208 .build_typed_tx()
209 .expect("checked by missing_keys"))
210 }
211
212 async fn build<W: NetworkWallet<PodNetwork>>(
213 self,
214 wallet: &W,
215 ) -> Result<<PodNetwork as Network>::TxEnvelope, TransactionBuilderError<PodNetwork>> {
216 Ok(wallet.sign_request(self).await?)
217 }
218}
219
220impl ReceiptResponse for PodReceiptResponse {
221 fn contract_address(&self) -> Option<Address> {
222 None
224 }
225
226 fn status(&self) -> bool {
227 self.receipt.status()
228 }
229
230 fn block_hash(&self) -> Option<BlockHash> {
231 Some(BlockHash::default())
233 }
234
235 fn block_number(&self) -> Option<u64> {
236 None
238 }
239
240 fn transaction_hash(&self) -> TxHash {
241 self.receipt.transaction_hash()
242 }
243
244 fn transaction_index(&self) -> Option<u64> {
245 None
247 }
248
249 fn gas_used(&self) -> u64 {
250 self.receipt.gas_used()
251 }
252
253 fn effective_gas_price(&self) -> u128 {
254 self.receipt.effective_gas_price()
255 }
256
257 fn blob_gas_used(&self) -> Option<u64> {
258 None
260 }
261
262 fn blob_gas_price(&self) -> Option<u128> {
263 None
265 }
266
267 fn from(&self) -> Address {
268 self.receipt.from()
269 }
270
271 fn to(&self) -> Option<Address> {
272 self.receipt.to()
273 }
274
275 fn cumulative_gas_used(&self) -> u64 {
276 self.receipt.cumulative_gas_used()
278 }
279
280 fn state_root(&self) -> Option<B256> {
281 None
283 }
284}
285
286#[derive(Debug, Serialize, Deserialize, Clone)]
287pub struct AttestationData {
288 pub public_key: Address,
289 pub signature: PrimitiveSignature,
290 pub timestamp: Timestamp,
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct PodReceiptResponse {
295 #[serde(flatten)]
296 pub receipt: TransactionReceipt,
297 pub pod_metadata: DetailedReceiptMetadata,
298}
299
300impl PodReceiptResponse {
301 pub fn verify(&self, committee: &Committee) -> Result<(), CommitteeError> {
302 let logs = self
303 .receipt
304 .inner
305 .logs()
306 .iter()
307 .map(|l| l.inner.clone())
308 .collect::<Vec<Log>>();
309
310 let logs_root = logs.to_merkle_tree().hash_custom();
311
312 let receipt = Receipt {
313 status: self.status(),
314 actual_gas_used: self.receipt.gas_used,
315 logs,
316 logs_root,
317 tx: self.pod_metadata.transaction.clone(),
318 contract_address: self.receipt.contract_address,
319 };
320
321 committee.verify_aggregate_attestation(
322 receipt.tx.hash_custom(),
323 &self
324 .pod_metadata
325 .attestations
326 .iter()
327 .map(|a| a.signature)
328 .collect(),
329 )
330 }
331
332 pub fn transaction(&self) -> &pod_types::Signed<Transaction> {
333 &self.pod_metadata.transaction
334 }
335}
336
337impl Deref for PodReceiptResponse {
338 type Target = TransactionReceipt;
339 fn deref(&self) -> &TransactionReceipt {
340 &self.receipt
341 }
342}
343
344impl Network for PodNetwork {
345 type TxType = TxType;
346 type TxEnvelope = alloy_consensus::TxEnvelope;
347 type UnsignedTx = TypedTransaction;
348 type ReceiptEnvelope = alloy_consensus::ReceiptEnvelope;
349 type Header = alloy_consensus::Header;
350 type TransactionRequest = PodTransactionRequest;
351 type TransactionResponse = alloy_rpc_types::Transaction;
352 type ReceiptResponse = PodReceiptResponse;
353 type HeaderResponse = alloy_rpc_types::Header;
354 type BlockResponse = alloy_rpc_types::Block;
355}
356
357impl RecommendedFillers for PodNetwork {
358 type RecommendedFillers = JoinFill<GasFiller, JoinFill<NonceFiller, ChainIdFiller>>;
359
360 fn recommended_fillers() -> Self::RecommendedFillers {
361 JoinFill::new(
362 GasFiller,
363 JoinFill::new(NonceFiller::default(), ChainIdFiller::default()),
364 )
365 }
366}