titan_types/
tx_out.rs

1use {
2    crate::rune::RuneAmount,
3    bitcoin::{hashes::Hash, Txid},
4    borsh::{BorshDeserialize, BorshSerialize},
5    serde::{Deserialize, Serialize},
6    std::io::{Read, Result, Write},
7};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SpenderReference {
11    pub txid: Txid,
12    pub vin: u32,
13}
14
15impl BorshSerialize for SpenderReference {
16    fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
17        BorshSerialize::serialize(&self.txid.as_raw_hash().to_byte_array(), writer)?;
18        BorshSerialize::serialize(&self.vin, writer)?;
19        Ok(())
20    }
21}
22
23impl BorshDeserialize for SpenderReference {
24    fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
25        let txid_bytes = <[u8; 32]>::deserialize_reader(reader)?;
26        let txid = bitcoin::Txid::from_byte_array(txid_bytes);
27        let vin = u32::deserialize_reader(reader)?;
28        Ok(Self { txid, vin })
29    }
30}
31
32#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
33pub enum SpentStatus {
34    Unspent,
35    Spent(SpenderReference),
36}
37
38// Intermediate structure for JSON serialization
39#[derive(Serialize, Deserialize)]
40struct SpentStatusJson {
41    spent: bool,
42    #[serde(skip_serializing_if = "Option::is_none")]
43    vin: Option<SpenderReference>,
44}
45
46impl Serialize for SpentStatus {
47    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
48    where
49        S: serde::Serializer,
50    {
51        match self {
52            SpentStatus::Unspent => SpentStatusJson {
53                spent: false,
54                vin: None,
55            },
56            SpentStatus::Spent(vin) => SpentStatusJson {
57                spent: true,
58                vin: Some(vin.clone()),
59            },
60        }
61        .serialize(serializer)
62    }
63}
64
65impl<'de> Deserialize<'de> for SpentStatus {
66    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
67    where
68        D: serde::Deserializer<'de>,
69    {
70        let json = SpentStatusJson::deserialize(deserializer)?;
71        Ok(if json.spent {
72            SpentStatus::Spent(json.vin.ok_or_else(|| {
73                serde::de::Error::custom("missing vin field for spent transaction")
74            })?)
75        } else {
76            SpentStatus::Unspent
77        })
78    }
79}
80
81#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
82pub struct TxOutEntry {
83    pub runes: Vec<RuneAmount>,
84    pub risky_runes: Vec<RuneAmount>,
85    pub value: u64,
86    pub spent: SpentStatus,
87}
88
89impl TxOutEntry {
90    pub fn has_runes(&self) -> bool {
91        !self.runes.is_empty()
92    }
93}