balius_sdk/
qol.rs

1use std::marker::PhantomData;
2
3use thiserror::Error;
4
5use crate::_internal::Handler;
6use crate::wit;
7
8pub use crate::_internal::global_get_public_keys as get_public_keys;
9
10#[derive(Debug, Error)]
11pub enum Error {
12    #[error("internal error: {0}")]
13    Internal(String),
14    #[error("bad config")]
15    BadConfig,
16    #[error("bad params")]
17    BadParams,
18    #[error("bad utxo")]
19    BadUtxo,
20    #[error("bad tx")]
21    BadTx,
22    #[error("event mismatch, expected {0}")]
23    EventMismatch(String),
24    #[error("kv error: {0}")]
25    KV(wit::balius::app::kv::KvError),
26    #[error("ledger error: {0}")]
27    Ledger(wit::balius::app::ledger::LedgerError),
28    #[error("sign error: {0}")]
29    Sign(wit::balius::app::sign::SignError),
30    #[error("http error: {0}")]
31    Http(wit::balius::app::http::ErrorCode),
32}
33
34impl From<Error> for wit::HandleError {
35    fn from(error: Error) -> Self {
36        match error {
37            Error::Internal(x) => wit::HandleError {
38                code: 0,
39                message: x,
40            },
41            Error::BadConfig => wit::HandleError {
42                code: 1,
43                message: "bad config".to_owned(),
44            },
45            Error::BadParams => wit::HandleError {
46                code: 2,
47                message: "bad params".to_owned(),
48            },
49            Error::KV(err) => wit::HandleError {
50                code: 3,
51                message: err.to_string(),
52            },
53            Error::Ledger(err) => wit::HandleError {
54                code: 4,
55                message: err.to_string(),
56            },
57            Error::Sign(err) => wit::HandleError {
58                code: 5,
59                message: err.to_string(),
60            },
61            Error::Http(err) => wit::HandleError {
62                code: 6,
63                message: err.to_string(),
64            },
65            Error::BadUtxo => wit::HandleError {
66                code: 7,
67                message: "bad utxo".to_owned(),
68            },
69            Error::BadTx => wit::HandleError {
70                code: 8,
71                message: "bad tx".to_owned(),
72            },
73            Error::EventMismatch(x) => wit::HandleError {
74                code: 9,
75                message: format!("event mismatch, expected {x}"),
76            },
77        }
78    }
79}
80
81impl From<serde_json::Error> for Error {
82    fn from(error: serde_json::Error) -> Self {
83        Error::Internal(error.to_string())
84    }
85}
86
87impl From<wit::balius::app::kv::KvError> for Error {
88    fn from(error: wit::balius::app::kv::KvError) -> Self {
89        Error::KV(error)
90    }
91}
92
93impl From<wit::balius::app::ledger::LedgerError> for Error {
94    fn from(error: wit::balius::app::ledger::LedgerError) -> Self {
95        Error::Ledger(error)
96    }
97}
98
99impl From<wit::balius::app::sign::SignError> for Error {
100    fn from(error: wit::balius::app::sign::SignError) -> Self {
101        Error::Sign(error)
102    }
103}
104
105impl From<wit::balius::app::http::ErrorCode> for Error {
106    fn from(error: wit::balius::app::http::ErrorCode) -> Self {
107        Error::Http(error)
108    }
109}
110
111impl From<crate::txbuilder::BuildError> for Error {
112    fn from(error: crate::txbuilder::BuildError) -> Self {
113        Error::Internal(error.to_string())
114    }
115}
116
117pub type WorkerResult<T> = std::result::Result<T, Error>;
118
119pub struct FnHandler<F, C, E, R>
120where
121    F: Fn(C, E) -> WorkerResult<R> + 'static,
122    C: TryFrom<wit::Config>,
123    E: TryFrom<wit::Event>,
124    R: TryInto<wit::Response>,
125{
126    func: F,
127    phantom: PhantomData<(C, E)>,
128}
129
130impl<F, C, E, R> Handler for FnHandler<F, C, E, R>
131where
132    C: TryFrom<wit::Config, Error = Error> + Send + Sync + 'static,
133    E: TryFrom<wit::Event, Error = Error> + Send + Sync + 'static,
134    R: TryInto<wit::Response, Error = Error> + Send + Sync + 'static,
135    F: Fn(C, E) -> WorkerResult<R> + Send + Sync + 'static,
136{
137    fn handle(
138        &self,
139        config: wit::Config,
140        event: wit::Event,
141    ) -> Result<wit::Response, wit::HandleError> {
142        let config: C = config.try_into()?;
143        let event: E = event.try_into()?;
144        let response = (self.func)(config, event)?;
145        Ok(response.try_into()?)
146    }
147}
148
149impl<F, C, E, R> From<F> for FnHandler<F, C, E, R>
150where
151    C: TryFrom<wit::Config, Error = Error> + Send + Sync + 'static,
152    E: TryFrom<wit::Event, Error = Error> + Send + Sync + 'static,
153    R: TryInto<wit::Response, Error = Error> + Send + Sync + 'static,
154    F: Fn(C, E) -> WorkerResult<R> + Send + Sync + 'static,
155{
156    fn from(func: F) -> Self {
157        FnHandler {
158            func,
159            phantom: PhantomData,
160        }
161    }
162}
163
164pub struct Ack;
165
166impl TryFrom<Ack> for wit::Response {
167    type Error = Error;
168
169    fn try_from(_: Ack) -> Result<Self, Self::Error> {
170        Ok(wit::Response::Acknowledge)
171    }
172}
173// Consider empty response as an acknowledgedment.
174impl TryFrom<()> for wit::Response {
175    type Error = Error;
176
177    fn try_from(_: ()) -> Result<Self, Self::Error> {
178        Ok(wit::Response::Acknowledge)
179    }
180}
181
182pub struct Config<T>(pub T);
183
184impl<T> TryFrom<wit::Config> for Config<T>
185where
186    T: serde::de::DeserializeOwned,
187{
188    type Error = Error;
189
190    fn try_from(config: wit::Config) -> Result<Self, Self::Error> {
191        let t = serde_json::from_slice(config.as_slice()).map_err(|_| Error::BadConfig)?;
192        Ok(Config(t))
193    }
194}
195
196impl<T> std::ops::Deref for Config<T> {
197    type Target = T;
198
199    fn deref(&self) -> &Self::Target {
200        &self.0
201    }
202}
203
204pub struct Params<T>(pub T);
205
206impl<T> TryFrom<wit::Event> for Params<T>
207where
208    T: serde::de::DeserializeOwned,
209{
210    type Error = Error;
211
212    fn try_from(value: wit::Event) -> Result<Self, Self::Error> {
213        let bytes = match value {
214            wit::Event::Request(x) => x,
215            _ => todo!(),
216        };
217
218        let t = serde_json::from_slice(bytes.as_slice()).map_err(|_| Error::BadParams)?;
219        Ok(Params(t))
220    }
221}
222
223impl<T> From<Params<T>> for wit::Response
224where
225    T: serde::Serialize,
226{
227    fn from(value: Params<T>) -> Self {
228        Self::Json(serde_json::to_vec(&value.0).unwrap())
229    }
230}
231
232impl<T> std::ops::Deref for Params<T> {
233    type Target = T;
234
235    fn deref(&self) -> &Self::Target {
236        &self.0
237    }
238}
239
240pub struct Json<T>(pub T);
241
242impl<T> TryFrom<Json<T>> for wit::Response
243where
244    T: serde::Serialize,
245{
246    type Error = Error;
247
248    fn try_from(value: Json<T>) -> Result<Self, Self::Error> {
249        let bytes = serde_json::to_vec(&value.0)?;
250        Ok(wit::Response::Json(bytes))
251    }
252}
253
254impl<T> std::ops::Deref for Json<T> {
255    type Target = T;
256
257    fn deref(&self) -> &Self::Target {
258        &self.0
259    }
260}
261
262pub struct Utxo<D> {
263    pub block_hash: Vec<u8>,
264    pub block_height: u64,
265    pub block_slot: u64,
266    pub tx_hash: Vec<u8>,
267    pub index: u64,
268    pub utxo: utxorpc_spec::utxorpc::v1alpha::cardano::TxOutput,
269    pub datum: Option<D>,
270}
271
272impl<D> TryFrom<wit::Event> for Utxo<D> {
273    type Error = Error;
274
275    fn try_from(value: wit::Event) -> Result<Self, Self::Error> {
276        use prost::Message;
277
278        let utxo = match value {
279            wit::Event::Utxo(x) => x,
280            wit::Event::UtxoUndo(x) => x,
281            _ => return Err(Error::EventMismatch("utxo|utxoundo".to_owned())),
282        };
283
284        let block_hash = utxo.block.block_hash;
285        let block_height = utxo.block.block_height;
286        let block_slot = utxo.block.block_slot;
287        let tx_hash = utxo.ref_.tx_hash;
288        let index = utxo.ref_.txo_index as u64;
289        let utxo = Message::decode(utxo.body.as_slice()).map_err(|_| Self::Error::BadUtxo)?;
290
291        Ok(Utxo {
292            block_hash,
293            block_height,
294            block_slot,
295            tx_hash,
296            index,
297            utxo,
298            datum: None,
299        })
300    }
301}
302
303pub struct Tx {
304    pub block_hash: Vec<u8>,
305    pub block_height: u64,
306    pub block_slot: u64,
307    pub hash: Vec<u8>,
308    pub tx: utxorpc_spec::utxorpc::v1alpha::cardano::Tx,
309}
310
311impl TryFrom<wit::Event> for Tx {
312    type Error = Error;
313
314    fn try_from(value: wit::Event) -> Result<Self, Self::Error> {
315        use prost::Message;
316
317        let tx = match value {
318            wit::Event::Tx(x) => x,
319            wit::Event::TxUndo(x) => x,
320            _ => return Err(Error::EventMismatch("tx|tx-undo".to_owned())),
321        };
322
323        let block_hash = tx.block.block_hash;
324        let block_height = tx.block.block_height;
325        let block_slot = tx.block.block_slot;
326        let hash = tx.hash;
327        let tx = Message::decode(tx.body.as_slice()).map_err(|_| Self::Error::BadUtxo)?;
328
329        Ok(Self {
330            block_hash,
331            block_height,
332            block_slot,
333            hash,
334            tx,
335        })
336    }
337}
338
339pub struct NewTx(pub Box<dyn crate::txbuilder::TxExpr>);
340
341impl TryInto<wit::Response> for NewTx {
342    type Error = Error;
343
344    fn try_into(self) -> Result<wit::Response, Self::Error> {
345        let ledger = crate::txbuilder::ExtLedgerFacade;
346        let tx = crate::txbuilder::build(self.0, ledger)?;
347        let cbor = pallas_codec::minicbor::to_vec(&tx).unwrap();
348        Ok(wit::Response::PartialTx(cbor))
349    }
350}
351
352pub struct UtxoMatcher {
353    address: Option<Vec<u8>>,
354}
355
356impl UtxoMatcher {
357    pub fn all() -> Self {
358        Self { address: None }
359    }
360
361    pub fn by_address(address: Vec<u8>) -> Self {
362        Self {
363            address: Some(address),
364        }
365    }
366}
367
368impl From<UtxoMatcher> for wit::balius::app::driver::UtxoPattern {
369    fn from(value: UtxoMatcher) -> Self {
370        Self {
371            address: value.address,
372            token: None,
373        }
374    }
375}
376
377impl crate::_internal::Worker {
378    pub fn new() -> Self {
379        Self::default()
380    }
381
382    pub(crate) fn init(&mut self, config: wit::Config) {
383        self.config = Some(config);
384    }
385
386    pub fn with_signer(mut self, key: &str, algorithm: &str) -> Self {
387        self.requested_signers
388            .insert(key.to_string(), algorithm.to_string());
389        self
390    }
391
392    pub fn with_request_handler(mut self, method: &str, handler: impl Handler + 'static) -> Self {
393        self.channels.insert(
394            self.channels.len() as u32,
395            crate::_internal::Channel {
396                handler: Box::new(handler),
397                pattern: wit::balius::app::driver::EventPattern::Request(method.to_owned()),
398            },
399        );
400
401        self
402    }
403
404    pub fn with_utxo_handler(
405        mut self,
406        pattern: impl Into<wit::balius::app::driver::UtxoPattern>,
407        handler: impl Handler + 'static,
408    ) -> Self {
409        self.channels.insert(
410            self.channels.len() as u32,
411            crate::_internal::Channel {
412                handler: Box::new(handler),
413                pattern: wit::balius::app::driver::EventPattern::Utxo(pattern.into()),
414            },
415        );
416
417        self
418    }
419
420    pub fn with_tx_handler(
421        mut self,
422        pattern: impl Into<wit::balius::app::driver::UtxoPattern>,
423        handler: impl Handler + 'static,
424    ) -> Self {
425        self.channels.insert(
426            self.channels.len() as u32,
427            crate::_internal::Channel {
428                handler: Box::new(handler),
429                pattern: wit::balius::app::driver::EventPattern::Tx(pattern.into()),
430            },
431        );
432
433        self
434    }
435}