chia_sdk_driver/primitives/
intermediate_launcher.rs

1use chia_protocol::{Bytes32, Coin, CoinSpend};
2use chia_puzzle_types::{Memos, nft::NftIntermediateLauncherArgs};
3use chia_puzzles::SINGLETON_LAUNCHER_HASH;
4use chia_sdk_types::{Conditions, announcement_id};
5use chia_sha2::Sha256;
6use clvmr::Allocator;
7
8use crate::{DriverError, SpendContext};
9
10use super::Launcher;
11
12/// An intermediate launcher is a coin that is created prior to the actual launcher coin.
13/// In this case, it automatically creates the launcher coin upon being spent.
14///
15/// The purpose of this is to allow multiple launcher coins to be created from a single parent.
16/// Without an intermediate launcher, they would all have the same coin id.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[must_use]
19pub struct IntermediateLauncher {
20    mint_number: usize,
21    mint_total: usize,
22    intermediate_coin: Coin,
23    launcher_coin: Coin,
24}
25
26impl IntermediateLauncher {
27    /// Create a new intermediate launcher with the given index. This makes the puzzle hash, and therefore coin id, unique.
28    pub fn new(parent_coin_id: Bytes32, mint_number: usize, mint_total: usize) -> Self {
29        let intermediate_puzzle_hash =
30            NftIntermediateLauncherArgs::curry_tree_hash(mint_number, mint_total).into();
31
32        let intermediate_coin = Coin::new(parent_coin_id, intermediate_puzzle_hash, 0);
33
34        let launcher_coin = Coin::new(
35            intermediate_coin.coin_id(),
36            SINGLETON_LAUNCHER_HASH.into(),
37            1,
38        );
39
40        Self {
41            mint_number,
42            mint_total,
43            intermediate_coin,
44            launcher_coin,
45        }
46    }
47
48    /// The intermediate coin that will be created when the parent is spent.
49    pub fn intermediate_coin(&self) -> Coin {
50        self.intermediate_coin
51    }
52
53    /// The singleton launcher coin that will be created when the intermediate coin is spent.
54    pub fn launcher_coin(&self) -> Coin {
55        self.launcher_coin
56    }
57
58    /// Spends the intermediate coin to create the launcher coin.
59    pub fn create(self, ctx: &mut SpendContext) -> Result<Launcher, DriverError> {
60        let mut parent = Conditions::new();
61
62        let puzzle = ctx.curry(NftIntermediateLauncherArgs::new(
63            self.mint_number,
64            self.mint_total,
65        ))?;
66
67        parent = parent.create_coin(self.intermediate_coin.puzzle_hash, 0, Memos::None);
68
69        let puzzle_reveal = ctx.serialize(&puzzle)?;
70        let solution = ctx.serialize(&())?;
71
72        ctx.insert(CoinSpend::new(
73            self.intermediate_coin,
74            puzzle_reveal,
75            solution,
76        ));
77
78        let mut index_message = Sha256::new();
79        index_message.update(usize_to_bytes(self.mint_number));
80        index_message.update(usize_to_bytes(self.mint_total));
81
82        Ok(Launcher::from_coin(
83            self.launcher_coin,
84            parent.assert_coin_announcement(announcement_id(
85                self.intermediate_coin.coin_id(),
86                index_message.finalize(),
87            )),
88        ))
89    }
90}
91
92fn usize_to_bytes(value: usize) -> Vec<u8> {
93    let mut allocator = Allocator::new();
94    let atom = allocator.new_number(value.into()).unwrap();
95    allocator.atom(atom).as_ref().to_vec()
96}