Skip to main content

ord_reqwest/decoder/
rune_decode.rs

1use std::fmt::Debug;
2
3use bitcoin::blockdata::transaction::Transaction;
4use ordinals::{Artifact, Etching, Runestone, SpacedRune};
5use crate::models::runes::{EtchingDetails, RuneTransaction, RuneTxDetails};
6
7// dervie copy for RuneTransactionDecoder
8#[derive(Debug, Clone)]
9pub struct RuneTransactionDecoder {}
10impl RuneTransactionDecoder {
11    pub fn new() -> Self {
12        RuneTransactionDecoder {}
13    }
14    fn process_etching(tx_id: &String, etching: Etching) -> RuneTransaction {
15        let rune_name = etching.rune.unwrap();
16        let spacers = etching.spacers;
17        let spaced_rune = if spacers.is_some() {
18            Some(SpacedRune::new(rune_name, etching.spacers.unwrap()))
19        } else {
20            None
21        };
22        let supply = etching.supply();
23        let rune_name = if spaced_rune.is_some() {
24            spaced_rune.unwrap().to_string()
25        } else {
26            rune_name.to_string()
27        };
28        return RuneTransaction::ETCHING(EtchingDetails {
29            tx_id: tx_id.to_string(),
30            rune_name,
31            supply,
32            mintable: etching.terms.is_some(),
33        });
34    }
35    fn process_runestone(tx_id: &String, rune: Runestone) -> RuneTransaction {
36        if let Some(etching) = rune.etching {
37            return Self::process_etching(tx_id, etching);
38        }
39        if let Some(mint) = rune.mint {
40            return RuneTransaction::MINT(mint);
41        }
42        return RuneTransaction::TRANSFER(rune.edicts);
43    }
44
45    pub fn decode_tx(&self, transaction: &Transaction) -> Option<RuneTxDetails> {
46        let rune_stone = Runestone::decipher(transaction);
47        if rune_stone.is_none() {
48            return None;
49        }
50        let txid = &transaction.compute_txid().to_string();
51        let rune_stone = rune_stone.unwrap();
52        match rune_stone {
53            Artifact::Runestone(rune) => {
54                let rune_tx = RuneTransactionDecoder::process_runestone(txid, rune);
55                // println!("Processed transaction {} in {:?}", txid, start.elapsed());
56                return Some(RuneTxDetails {
57                    tx_id: txid.to_string(),
58                    rune_tx,
59                });
60            }
61            _ => None,
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use bitcoin::consensus::deserialize;
69    use hex::decode as hex_decode;
70    use crate::data::transaction::{NON_RUNE_TX, RUNE_BUY_TX, RUNE_REVEAL_TX_HEX, SIGNET_RUNE_TX};
71
72    use crate::models::runes::RuneTransaction;
73
74    use super::*;
75
76    #[test]
77    fn test_decode_tx_runestone() {
78        let tx_bytes = hex_decode(RUNE_REVEAL_TX_HEX).expect("Invalid hex string");
79        let tx: Transaction = deserialize(&tx_bytes).expect("Failed to deserialize transaction");
80        let rune_tx_details = RuneTransactionDecoder::new().decode_tx(&tx).unwrap();
81        let runestone = rune_tx_details.rune_tx;
82        match runestone {
83            RuneTransaction::ETCHING(etching) => {
84                assert_eq!(etching.rune_name, "HOOOOOOOOTERS");
85            }
86            _ => panic!("Expected etching"),
87        }
88    }
89    #[test]
90    fn test_rune_tx_mint() {
91        let mint_tx = "02000000000101e1abe66835908ec28bab86af8914ea85458993da13822e326a3bb1c159dfd0090200000000fdffffff0300000000000000000a6a5d0714c0a23314a30222020000000000002251206bda50e97f9e9107d24774e12099e6ef6fb11047f52949e0cae98ae4aa0c8f8676b9000000000000225120528996bda1de76858fdecd34168c331e12a64f415427ec060ae1df72b4aaaafb0140f4def7a7945dbfdeecc285163a794bd624c603261a02a6a87e9ccc6d56ee1c6b9fe8e48c0bd30e540ca17327d6b0be3b215f5b8edee32b824aa42add2a283b7c00000000";
92        let tx_bytes = hex_decode(mint_tx).expect("Invalid hex string");
93        let tx: Transaction = deserialize(&tx_bytes).expect("Failed to deserialize transaction");
94        let rune_tx_details = RuneTransactionDecoder::new().decode_tx(&tx).unwrap();
95        let runestone = rune_tx_details.rune_tx;
96        match runestone {
97            RuneTransaction::MINT(rune_id) => {
98                assert_eq!(rune_id.block, 840000);
99                assert_eq!(rune_id.tx, 291);
100            }
101            _ => panic!("Expected minted rune"),
102        }
103    }
104    #[test]
105    fn test_decode_tx_non_rune_tx() {
106        let tx_bytes = hex_decode(NON_RUNE_TX).expect("Invalid hex string");
107        let tx: Transaction = deserialize(&tx_bytes).expect("Failed to deserialize transaction");
108        let rune_tx_details = RuneTransactionDecoder::new().decode_tx(&tx);
109        assert!(rune_tx_details.is_none());
110    }
111
112    #[test]
113    fn test_decode_signet_rune_tx() {
114        let tx_bytes = hex_decode(SIGNET_RUNE_TX).expect("Invalid hex string");
115        let tx: Transaction = deserialize(&tx_bytes).expect("Failed to deserialize transaction");
116        let rune_tx_details = RuneTransactionDecoder::new().decode_tx(&tx).unwrap();
117        let runestone = rune_tx_details.rune_tx;
118        match runestone {
119            RuneTransaction::ETCHING(etching) => {
120                assert_eq!(etching.rune_name, "MAOBY•THE•CUTEST•CAT");
121            }
122            _ => panic!("Expected minted rune"),
123        }
124    }
125
126    #[test]
127    fn test_decode_sell_tx() {
128        let tx_bytes = hex_decode(RUNE_BUY_TX).expect("Invalid hex string");
129        let tx: Transaction = deserialize(&tx_bytes).expect("Failed to deserialize transaction");
130        let rune_tx_details = RuneTransactionDecoder::new().decode_tx(&tx).unwrap();
131        let runestone = rune_tx_details.rune_tx;
132        match runestone {
133            RuneTransaction::TRANSFER(edicts) => {
134                assert_eq!(edicts.len(), 1);
135                assert_eq!(edicts.first().unwrap().id.block, 840010);
136                assert_eq!(edicts.first().unwrap().id.tx, 4);
137            }
138            _ => panic!("Expected transfer rune"),
139        }
140    }
141}