pod_sdk/auctions/
client.rs

1use std::time::SystemTime;
2
3use crate::{
4    network::{PodNetwork, PodReceiptResponse},
5    provider::PodProvider,
6    Address, U256,
7};
8use alloy_eips::BlockNumberOrTag;
9use anyhow::Context;
10
11use pod_contracts::auction::Auction::AuctionInstance;
12use pod_types::Timestamp;
13
14pub struct AuctionClient {
15    pub auction: AuctionInstance<PodProvider, PodNetwork>,
16}
17
18pub struct Bid {
19    pub amount: U256,
20    pub bidder: Address,
21    pub data: Vec<u8>,
22}
23
24impl AuctionClient {
25    // Convert SystemTime -> microseconds as `u128`.
26    fn micros_from_system_time(deadline: SystemTime) -> u128 {
27        Timestamp::from(deadline).as_micros()
28    }
29
30    // Convert SystemTime -> microseconds as `u64`, failing with a clear message on overflow.
31    fn micros_u64_from_system_time(deadline: SystemTime) -> anyhow::Result<u64> {
32        let micros = Self::micros_from_system_time(deadline);
33        micros
34            .try_into()
35            .context("deadline microseconds must fit in u64")
36    }
37
38    // Convert SystemTime -> microseconds as `U256` for contract topics.
39    fn micros_u256_from_system_time(deadline: SystemTime) -> U256 {
40        U256::from(Self::micros_from_system_time(deadline))
41    }
42
43    pub fn new(provider: PodProvider, contract: Address) -> Self {
44        AuctionClient {
45            auction: AuctionInstance::new(contract, provider),
46        }
47    }
48
49    #[tracing::instrument(skip(self))]
50    pub async fn wait_for_auction_end(&self, deadline: SystemTime) -> anyhow::Result<()> {
51        Ok(self
52            .auction
53            .provider()
54            .wait_past_perfect_time(deadline.into())
55            .await?)
56    }
57
58    #[tracing::instrument(skip(self))]
59    pub async fn fetch_bids(&self, auction_id: U256) -> anyhow::Result<Vec<Bid>> {
60        let logs = self
61            .auction
62            .BidSubmitted_filter()
63            .topic1(auction_id)
64            .to_block(BlockNumberOrTag::Latest)
65            .query()
66            .await
67            .context("fetching bid logs")?;
68
69        logs.into_iter()
70            .map(|(event, _)| {
71                Ok(Bid {
72                    amount: event.value,
73                    bidder: event.bidder,
74                    data: event.data.to_vec(),
75                })
76            })
77            .collect()
78    }
79
80    #[tracing::instrument(skip(self))]
81    pub async fn fetch_bids_for_deadline(&self, deadline: SystemTime) -> anyhow::Result<Vec<Bid>> {
82        let deadline_us = Self::micros_u256_from_system_time(deadline);
83
84        let logs = self
85            .auction
86            .BidSubmitted_filter()
87            .topic3(deadline_us)
88            .to_block(BlockNumberOrTag::Latest)
89            .query()
90            .await
91            .context("fetching bid logs")?;
92
93        logs.into_iter()
94            .map(|(event, _)| {
95                Ok(Bid {
96                    amount: event.value,
97                    bidder: event.bidder,
98                    data: event.data.to_vec(),
99                })
100            })
101            .collect()
102    }
103
104    pub async fn submit_bid(
105        &self,
106        auction_id: U256,
107        deadline: SystemTime,
108        bid: U256,
109        data: Vec<u8>,
110    ) -> anyhow::Result<PodReceiptResponse> {
111        let deadline = Self::micros_u64_from_system_time(deadline)?;
112
113        let pending_tx = self
114            .auction
115            .submitBid(auction_id, deadline, bid, data.into())
116            .max_priority_fee_per_gas(0)
117            .send()
118            .await
119            .context("sending bid TX")?;
120
121        let receipt = pending_tx
122            .get_receipt()
123            .await
124            .context("awaiting for bid TX confirmation")?;
125
126        anyhow::ensure!(receipt.status(), "failed to submit bid TX");
127        Ok(receipt)
128    }
129}