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 is invalid")
48 }
49
50 pub fn pecorino_with_client(client: reqwest::Client) -> Self {
52 Self::new_with_client(
53 pecorino::TX_CACHE_URL.parse().expect("pecorino tx cache URL is invalid"),
54 client,
55 )
56 }
57
58 pub const fn client(&self) -> &reqwest::Client {
60 &self.client
61 }
62
63 pub const fn url(&self) -> &reqwest::Url {
65 &self.url
66 }
67
68 async fn forward_inner<T: Serialize + Send, R: DeserializeOwned>(
69 &self,
70 join: &'static str,
71 obj: T,
72 ) -> Result<R, Error> {
73 self.forward_inner_raw(join, obj)
74 .await?
75 .json::<R>()
76 .await
77 .inspect_err(|e| warn!(%e, "Failed to parse response from transaction cache"))
78 .map_err(Into::into)
79 }
80
81 async fn forward_inner_raw<T: Serialize + Send>(
82 &self,
83 join: &'static str,
84 obj: T,
85 ) -> Result<reqwest::Response, Error> {
86 let url = self
88 .url
89 .join(join)
90 .inspect_err(|e| warn!(%e, "Failed to join URL. Not forwarding transaction."))?;
91
92 self.client.post(url).json(&obj).send().await?.error_for_status().map_err(Into::into)
94 }
95
96 async fn get_inner<T>(&self, join: &'static str) -> Result<T, Error>
97 where
98 T: DeserializeOwned,
99 {
100 let url = self
102 .url
103 .join(join)
104 .inspect_err(|e| warn!(%e, "Failed to join URL. Not querying transaction cache."))?;
105
106 self.client
108 .get(url)
109 .send()
110 .await
111 .inspect_err(|e| warn!(%e, "Failed to get object from transaction cache"))?
112 .json::<T>()
113 .await
114 .map_err(Into::into)
115 }
116
117 #[instrument(skip_all)]
119 pub async fn forward_raw_transaction(
120 &self,
121 tx: TxEnvelope,
122 ) -> Result<TxCacheSendTransactionResponse, Error> {
123 self.forward_inner(TRANSACTIONS, tx).await
124 }
125
126 #[instrument(skip_all)]
128 pub async fn forward_bundle(
129 &self,
130 bundle: SignetEthBundle,
131 ) -> Result<TxCacheSendBundleResponse, Error> {
132 self.forward_inner(BUNDLES, bundle).await
133 }
134
135 #[instrument(skip_all)]
137 pub async fn forward_order(&self, order: SignedOrder) -> Result<(), Error> {
138 self.forward_inner_raw(ORDERS, order).await.map(drop)
139 }
140
141 #[instrument(skip_all)]
143 pub async fn get_transactions(&self) -> Result<Vec<TxEnvelope>, Error> {
144 let response: TxCacheTransactionsResponse =
145 self.get_inner::<TxCacheTransactionsResponse>(TRANSACTIONS).await?;
146 Ok(response.transactions)
147 }
148
149 #[instrument(skip_all)]
151 pub async fn get_orders(&self) -> Result<Vec<SignedOrder>, Error> {
152 let response: TxCacheOrdersResponse =
153 self.get_inner::<TxCacheOrdersResponse>(ORDERS).await?;
154 Ok(response.orders)
155 }
156}