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}