Skip to main content

bitcoin_cash_slp/
lib.rs

1use bitcoin_cash::{error, ByteArray, Hashed, Op, Opcode, Script, Sha256d, TaggedOp, TxOutput};
2
3#[derive(Clone, Debug, Hash)]
4pub struct TokenId(Sha256d);
5
6#[derive(Copy, Clone, Debug, Hash)]
7pub enum SlpTokenType {
8    Fungible = 1,
9}
10
11#[derive(Copy, Clone, Debug, Hash)]
12pub enum SlpTxType {
13    GENESIS,
14    SEND,
15    MINT,
16    COMMIT,
17}
18
19pub fn slp_amount_ops<'a>(output_amounts: impl IntoIterator<Item = &'a u64>) -> Vec<Op> {
20    output_amounts
21        .into_iter()
22        .enumerate()
23        .map(|(idx, &output_amount)| Op::PushByteArray {
24            array: ByteArray::new(
25                format!("token_output_quantity{}", idx + 1),
26                output_amount.to_be_bytes().to_vec(),
27            ),
28            is_minimal: false,
29        })
30        .collect()
31}
32
33pub fn slp_genesis_output(
34    slp_token_type: SlpTokenType,
35    token_ticker: &str,
36    token_name: &str,
37    token_document_url: &str,
38    token_document_hash: &str,
39    decimals: u8,
40    mint_baton_vout: Option<u8>,
41    initial_token_mint_quantity: u64,
42) -> TxOutput {
43    let byte_arrays = vec![
44        ByteArray::from_slice("lokad_id", b"SLP\0"),
45        ByteArray::new("token_type", vec![slp_token_type as u8]),
46        ByteArray::new("transaction_type", SlpTxType::GENESIS.to_string().into_bytes()),
47        ByteArray::new("token_ticker", token_ticker.as_bytes().to_vec()),
48        ByteArray::new("token_name", token_name.as_bytes().to_vec()),
49        ByteArray::new("token_document_url", token_document_url.as_bytes().to_vec()),
50        ByteArray::new("token_document_hash", token_document_hash.as_bytes().to_vec()),
51        ByteArray::new("decimals", decimals.to_be_bytes().as_ref()),
52        ByteArray::new("mint_baton_vout", mint_baton_vout.map(|vout| vout.to_be_bytes().to_vec()).unwrap_or(vec![])),
53        ByteArray::new("initial_token_mint_quantity", initial_token_mint_quantity.to_be_bytes().as_ref()),
54    ];
55    let mut ops = vec![
56        Op::Code(Opcode::OP_RETURN),
57    ];
58    for byte_array in byte_arrays {
59        ops.push(Op::PushByteArray {
60            array: byte_array,
61            is_minimal: false,
62        });
63    }
64    TxOutput {
65        value: 0,
66        script: Script::new(ops.into_iter().map(TaggedOp::from_op).collect::<Vec<_>>()),
67    }
68}
69
70pub fn slp_send_output(
71    slp_token_type: SlpTokenType,
72    token_id: &TokenId,
73    output_amounts: &[u64],
74) -> TxOutput {
75    let mut ops = vec![
76        Op::Code(Opcode::OP_RETURN),
77        Op::PushByteArray {
78            array: ByteArray::from_slice("lokad_id", b"SLP\0"),
79            is_minimal: false,
80        },
81        Op::PushByteArray {
82            array: ByteArray::new("token_type", vec![slp_token_type as u8]),
83            is_minimal: false,
84        },
85        Op::PushByteArray {
86            array: ByteArray::new("transaction_type", SlpTxType::SEND.to_string().into_bytes()),
87            is_minimal: false,
88        },
89        Op::PushByteArray {
90            array: ByteArray::new("token_id", token_id.to_vec()),
91            is_minimal: false,
92        },
93    ];
94    ops.extend(slp_amount_ops(output_amounts.iter()));
95    TxOutput {
96        value: 0,
97        script: Script::new(ops.into_iter().map(TaggedOp::from_op).collect::<Vec<_>>()),
98    }
99}
100
101impl std::fmt::Display for SlpTxType {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
103        write!(f, "{:?}", self)
104    }
105}
106
107impl TokenId {
108    pub fn from_slice(token_id: &[u8]) -> error::Result<Self> {
109        Ok(TokenId(Sha256d::from_slice_le(token_id)?))
110    }
111
112    pub fn as_slice_be(&self) -> &[u8] {
113        self.0.as_slice()
114    }
115
116    pub fn to_vec(&self) -> Vec<u8> {
117        self.0.to_vec_le()
118    }
119}