ord_reqwest/decoder/
rune_decode.rs1use std::fmt::Debug;
2
3use bitcoin::blockdata::transaction::Transaction;
4use ordinals::{Artifact, Etching, Runestone, SpacedRune};
5use crate::models::runes::{EtchingDetails, RuneTransaction, RuneTxDetails};
6
7#[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 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}