signet_tx_cache/
client.rs1use crate::types::{
2 CacheObject, CacheResponse, OrderKey, TxCacheOrdersResponse, TxCacheSendBundleResponse,
3 TxCacheSendTransactionResponse, TxCacheTransactionsResponse, TxKey,
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, query: Option<T::Key>) -> Result<T, Error>
97 where
98 T: DeserializeOwned + CacheObject,
99 {
100 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
106 .get(url)
107 .query(&query)
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(
143 &self,
144 query: Option<TxKey>,
145 ) -> Result<CacheResponse<TxCacheTransactionsResponse>, Error> {
146 self.get_inner(TRANSACTIONS, query).await
147 }
148
149 #[instrument(skip_all)]
151 pub async fn get_orders(
152 &self,
153 query: Option<OrderKey>,
154 ) -> Result<CacheResponse<TxCacheOrdersResponse>, Error> {
155 self.get_inner(ORDERS, query).await
156 }
157}