pod_sdk/network/
mod.rs

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!(), // Preventing usage of any other except EIP-1559 Tx
168        }
169    }
170
171    fn can_submit(&self) -> bool {
172        // value and data may be None. If they are, they will be set to default.
173        // gas fields and nonce may be None, if they are, they will be populated
174        // with default values by the RPC server
175        self.from.is_some()
176    }
177
178    fn can_build(&self) -> bool {
179        // Only supporting EIP-1559 Transactions
180        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        // For now not allowing deployments
223        None
224    }
225
226    fn status(&self) -> bool {
227        self.receipt.status()
228    }
229
230    fn block_hash(&self) -> Option<BlockHash> {
231        // todo
232        Some(BlockHash::default())
233    }
234
235    fn block_number(&self) -> Option<u64> {
236        // todo
237        None
238    }
239
240    fn transaction_hash(&self) -> TxHash {
241        self.receipt.transaction_hash()
242    }
243
244    fn transaction_index(&self) -> Option<u64> {
245        // todo
246        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        // todo
259        None
260    }
261
262    fn blob_gas_price(&self) -> Option<u128> {
263        // todo
264        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        // todo
277        self.receipt.cumulative_gas_used()
278    }
279
280    fn state_root(&self) -> Option<B256> {
281        // todo
282        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}