Skip to main content

ootle_rs/types/transaction/
request.rs

1//   Copyright 2026 The Tari Project
2//   SPDX-License-Identifier: BSD-3-Clause
3
4use tari_ootle_transaction::{Transaction, UnsealedTransaction, UnsignedTransaction};
5
6use crate::{
7    transaction::TransactionSealSigner,
8    wallet::{TransactionAuthorization, WalletResult},
9};
10
11#[derive(Clone, Debug, Default)]
12pub struct Initial;
13pub struct WithTx(UnsignedTransaction);
14
15/// A builder for constructing signed transactions ready for submission.
16///
17/// Follows a typestate pattern: start with [`TransactionRequest::new()`], attach an
18/// unsigned transaction with [`with_transaction()`](TransactionRequest::with_transaction),
19/// optionally add extra authorizations, then call [`build()`](TransactionRequest::build)
20/// to seal and sign.
21///
22/// ```rust,ignore
23/// let tx = TransactionRequest::default()
24///     .with_transaction(unsigned_tx)
25///     .build(provider.wallet())
26///     .await?;
27/// ```
28#[derive(Clone, Debug, Default)]
29pub struct TransactionRequest<State = Initial> {
30    state: State,
31    authorizations: Vec<TransactionAuthorization>,
32}
33
34impl TransactionRequest<Initial> {
35    pub fn new() -> Self {
36        Self {
37            state: Initial,
38            authorizations: Vec::new(),
39        }
40    }
41
42    pub fn with_transaction(self, builder: UnsignedTransaction) -> TransactionRequest<WithTx> {
43        TransactionRequest {
44            state: WithTx(builder),
45            authorizations: self.authorizations,
46        }
47    }
48}
49
50impl<State> TransactionRequest<State> {
51    pub fn add_authorization(mut self, auth: TransactionAuthorization) -> Self {
52        self.authorizations.push(auth);
53        self
54    }
55
56    pub fn with_authorizations<I>(mut self, auths: I) -> Self
57    where I: IntoIterator<Item = TransactionAuthorization> {
58        self.authorizations.extend(auths);
59        self
60    }
61}
62
63impl TransactionRequest<WithTx> {
64    /// The transaction with the caller's authorizations attached, but unsealed. Only [`build`](Self::build) can add
65    /// the seal signer's own authorizations, since those commit to the key it seals with.
66    pub fn build_unsealed(self) -> UnsealedTransaction {
67        self.state
68            .0
69            .with_signatures(self.authorizations.into_iter().map(|a| a.into_signature()).collect())
70    }
71
72    pub async fn build(self, seal_signer: &dyn TransactionSealSigner) -> WalletResult<Transaction> {
73        let unsigned = self.state.0;
74        let mut authorizations = self.authorizations;
75        authorizations.extend(seal_signer.authorizations_for(&unsigned).await?);
76
77        let unsealed = unsigned.with_signatures(authorizations.into_iter().map(|a| a.into_signature()).collect());
78        let final_tx = seal_signer.seal_transaction(unsealed).await?;
79        Ok(final_tx)
80    }
81}