signet_tx_cache/
client.rs1use crate::types::{
2 TxCacheOrdersResponse, TxCacheSendBundleResponse, TxCacheSendTransactionResponse,
3 TxCacheTransactionsResponse,
4};
5use alloy::consensus::TxEnvelope;
6use eyre::Error;
7use serde::{de::DeserializeOwned, Serialize};
8use signet_bundle::SignetEthBundle;
9use signet_constants::pecorino;
10use signet_types::SignedOrder;
11use tracing::{instrument, warn};
12
13const TRANSACTIONS: &str = "transactions";
15const BUNDLES: &str = "bundles";
16const ORDERS: &str = "orders";
17
18#[derive(Debug, Clone)]
21pub struct TxCache {
22 url: reqwest::Url,
24 client: reqwest::Client,
26}
27
28impl TxCache {
29 pub const fn new_with_client(url: reqwest::Url, client: reqwest::Client) -> Self {
31 Self { url, client }
32 }
33
34 pub fn new(url: reqwest::Url) -> Self {
36 Self { url, client: reqwest::Client::new() }
37 }
38
39 pub fn new_from_string(url: &str) -> Result<Self, Error> {
41 let url = reqwest::Url::parse(url)?;
42 Ok(Self::new(url))
43 }
44
45 pub fn pecorino() -> Self {
47 Self::new_from_string(pecorino::TX_CACHE_URL).expect("pecorino tx cache URL invalid")
48 }
49
50 pub fn pecorino_with_client(client: reqwest::Client) -> Self {
52 let url =
53 reqwest::Url::parse(pecorino::TX_CACHE_URL).expect("pecorino tx cache URL invalid");
54 Self::new_with_client(url, client)
55 }
56
57 pub const fn client(&self) -> &reqwest::Client {
59 &self.client
60 }
61
62 pub const fn url(&self) -> &reqwest::Url {
64 &self.url
65 }
66
67 async fn forward_inner<T: Serialize + Send, R: DeserializeOwned>(
68 &self,
69 join: &'static str,
70 obj: T,
71 ) -> Result<R, Error> {
72 self.forward_inner_raw(join, obj)
73 .await?
74 .json::<R>()
75 .await
76 .inspect_err(|e| warn!(%e, "Failed to parse response from transaction cache"))
77 .map_err(Into::into)
78 }
79
80 async fn forward_inner_raw<T: Serialize + Send>(
81 &self,
82 join: &'static str,
83 obj: T,
84 ) -> Result<reqwest::Response, Error> {
85 let url = self
87 .url
88 .join(join)
89 .inspect_err(|e| warn!(%e, "Failed to join URL. Not forwarding transaction."))?;
90
91 self.client.post(url).json(&obj).send().await?.error_for_status().map_err(Into::into)
93 }
94
95 async fn get_inner<T>(&self, join: &'static str) -> Result<T, Error>
96 where
97 T: DeserializeOwned,
98 {
99 let url = self
101 .url
102 .join(join)
103 .inspect_err(|e| warn!(%e, "Failed to join URL. Not querying transaction cache."))?;
104
105 self.client
107 .get(url)
108 .send()
109 .await
110 .inspect_err(|e| warn!(%e, "Failed to get object from transaction cache"))?
111 .json::<T>()
112 .await
113 .map_err(Into::into)
114 }
115
116 #[instrument(skip_all)]
118 pub async fn forward_raw_transaction(
119 &self,
120 tx: TxEnvelope,
121 ) -> Result<TxCacheSendTransactionResponse, Error> {
122 self.forward_inner(TRANSACTIONS, tx).await
123 }
124
125 #[instrument(skip_all)]
127 pub async fn forward_bundle(
128 &self,
129 bundle: SignetEthBundle,
130 ) -> Result<TxCacheSendBundleResponse, Error> {
131 self.forward_inner(BUNDLES, bundle).await
132 }
133
134 #[instrument(skip_all)]
136 pub async fn forward_order(&self, order: SignedOrder) -> Result<(), Error> {
137 self.forward_inner_raw(ORDERS, order).await.map(drop)
138 }
139
140 #[instrument(skip_all)]
142 pub async fn get_transactions(&self) -> Result<Vec<TxEnvelope>, Error> {
143 let response: TxCacheTransactionsResponse =
144 self.get_inner::<TxCacheTransactionsResponse>(TRANSACTIONS).await?;
145 Ok(response.transactions)
146 }
147
148 #[instrument(skip_all)]
150 pub async fn get_orders(&self) -> Result<Vec<SignedOrder>, Error> {
151 let response: TxCacheOrdersResponse =
152 self.get_inner::<TxCacheOrdersResponse>(ORDERS).await?;
153 Ok(response.orders)
154 }
155}