chia_sdk_driver/layers/action_layer/actions/catalog/
register.rs

1use chia_protocol::Bytes32;
2use chia_puzzle_types::singleton::SingletonStruct;
3use chia_sdk_types::{
4    Conditions, Mod, announcement_id,
5    puzzles::{
6        ANY_METADATA_UPDATER_HASH, CatalogRegisterActionArgs, CatalogRegisterActionSolution,
7        CatalogSlotValue, DefaultCatMakerArgs, NftPack, PrecommitSpendMode,
8    },
9};
10use clvm_traits::{FromClvm, ToClvm, clvm_tuple};
11use clvm_utils::{ToTreeHash, TreeHash};
12use clvmr::NodePtr;
13
14use crate::{
15    CatalogPrecommitValue, CatalogRegistry, CatalogRegistryConstants, DriverError, HashedPtr,
16    PrecommitCoin, PrecommitLayer, SingletonAction, Slot, Spend, SpendContext,
17    UniquenessPrelauncher,
18};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct CatalogRegisterAction {
22    pub launcher_id: Bytes32,
23    pub royalty_puzzle_hash_hash: Bytes32,
24    pub trade_price_percentage: u16,
25    pub relative_block_height: u32,
26    pub payout_puzzle_hash: Bytes32,
27}
28
29impl ToTreeHash for CatalogRegisterAction {
30    fn tree_hash(&self) -> TreeHash {
31        Self::new_args(
32            self.launcher_id,
33            self.royalty_puzzle_hash_hash,
34            self.trade_price_percentage,
35            self.relative_block_height,
36            self.payout_puzzle_hash,
37        )
38        .curry_tree_hash()
39    }
40}
41
42impl SingletonAction<CatalogRegistry> for CatalogRegisterAction {
43    fn from_constants(constants: &CatalogRegistryConstants) -> Self {
44        Self {
45            launcher_id: constants.launcher_id,
46            royalty_puzzle_hash_hash: constants.royalty_address.tree_hash().into(),
47            trade_price_percentage: constants.royalty_basis_points,
48            relative_block_height: constants.relative_block_height,
49            payout_puzzle_hash: constants.precommit_payout_puzzle_hash,
50        }
51    }
52}
53
54impl CatalogRegisterAction {
55    pub fn new_args(
56        launcher_id: Bytes32,
57        royalty_puzzle_hash_hash: Bytes32,
58        trade_price_percentage: u16,
59        relative_block_height: u32,
60        payout_puzzle_hash: Bytes32,
61    ) -> CatalogRegisterActionArgs {
62        CatalogRegisterActionArgs {
63            nft_pack: NftPack::new(royalty_puzzle_hash_hash, trade_price_percentage),
64            uniqueness_prelauncher_1st_curry_hash: UniquenessPrelauncher::<()>::first_curry_hash()
65                .into(),
66            precommit_1st_curry_hash: PrecommitLayer::<()>::first_curry_hash(
67                SingletonStruct::new(launcher_id).tree_hash().into(),
68                relative_block_height,
69                payout_puzzle_hash,
70            )
71            .into(),
72            slot_1st_curry_hash: Slot::<CatalogSlotValue>::first_curry_hash(launcher_id, 0).into(),
73        }
74    }
75
76    pub fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
77        ctx.curry(Self::new_args(
78            self.launcher_id,
79            self.royalty_puzzle_hash_hash,
80            self.trade_price_percentage,
81            self.relative_block_height,
82            self.payout_puzzle_hash,
83        ))
84    }
85
86    pub fn spent_slot_values(
87        &self,
88        ctx: &SpendContext,
89        solution: NodePtr,
90    ) -> Result<[CatalogSlotValue; 2], DriverError> {
91        let params = CatalogRegisterActionSolution::<NodePtr, ()>::from_clvm(ctx, solution)?;
92
93        Ok([
94            CatalogSlotValue::new(
95                params.left_tail_hash,
96                params.left_left_tail_hash,
97                params.right_tail_hash,
98            ),
99            CatalogSlotValue::new(
100                params.right_tail_hash,
101                params.left_tail_hash,
102                params.right_right_tail_hash,
103            ),
104        ])
105    }
106
107    pub fn created_slot_values(
108        &self,
109        ctx: &SpendContext,
110        solution: NodePtr,
111    ) -> Result<[CatalogSlotValue; 3], DriverError> {
112        let params = CatalogRegisterActionSolution::<NodePtr, ()>::from_clvm(ctx, solution)?;
113
114        Ok([
115            CatalogSlotValue::new(
116                params.left_tail_hash,
117                params.left_left_tail_hash,
118                params.tail_hash,
119            ),
120            CatalogSlotValue::new(
121                params.tail_hash,
122                params.left_tail_hash,
123                params.right_tail_hash,
124            ),
125            CatalogSlotValue::new(
126                params.right_tail_hash,
127                params.tail_hash,
128                params.right_right_tail_hash,
129            ),
130        ])
131    }
132
133    #[allow(clippy::too_many_arguments)]
134    pub fn spend(
135        self,
136        ctx: &mut SpendContext,
137        catalog: &mut CatalogRegistry,
138        tail_hash: Bytes32,
139        left_slot: Slot<CatalogSlotValue>,
140        right_slot: Slot<CatalogSlotValue>,
141        precommit_coin: &PrecommitCoin<CatalogPrecommitValue>,
142        eve_nft_inner_spend: Spend,
143    ) -> Result<Conditions, DriverError> {
144        // calculate announcement
145        let mut register_announcement =
146            clvm_tuple!(tail_hash, precommit_coin.value.initial_inner_puzzle_hash)
147                .tree_hash()
148                .to_vec();
149        register_announcement.insert(0, b'r');
150
151        // spend precommit coin
152        let initial_inner_puzzle_hash = precommit_coin.value.initial_inner_puzzle_hash;
153        let my_inner_puzzle_hash = catalog.info.inner_puzzle_hash().into();
154        precommit_coin.spend(ctx, PrecommitSpendMode::REGISTER, my_inner_puzzle_hash)?;
155
156        // spend uniqueness prelauncher
157        let uniqueness_prelauncher =
158            UniquenessPrelauncher::<Bytes32>::new(ctx, catalog.coin.coin_id(), tail_hash)?;
159        let nft_launcher = uniqueness_prelauncher.spend(ctx)?;
160
161        // launch eve nft
162        let (_, nft) = nft_launcher.mint_eve_nft(
163            ctx,
164            initial_inner_puzzle_hash,
165            HashedPtr::NIL,
166            ANY_METADATA_UPDATER_HASH.into(),
167            catalog.info.constants.royalty_address,
168            catalog.info.constants.royalty_basis_points,
169        )?;
170
171        // spend nft launcher
172        let _new_nft = nft.spend(ctx, eve_nft_inner_spend)?;
173
174        // finally, spend self
175        let (left_slot, right_slot) = catalog.actual_neigbors(tail_hash, left_slot, right_slot);
176        let my_solution = CatalogRegisterActionSolution {
177            cat_maker_reveal: ctx.curry(DefaultCatMakerArgs::new(
178                precommit_coin.asset_id.tree_hash().into(),
179            ))?,
180            cat_maker_solution: (),
181            tail_hash,
182            initial_nft_owner_ph: initial_inner_puzzle_hash,
183            refund_puzzle_hash_hash: precommit_coin.refund_puzzle_hash.tree_hash().into(),
184            left_tail_hash: left_slot.info.value.asset_id,
185            left_left_tail_hash: left_slot.info.value.neighbors.left_value,
186            right_tail_hash: right_slot.info.value.asset_id,
187            right_right_tail_hash: right_slot.info.value.neighbors.right_value,
188            my_id: catalog.coin.coin_id(),
189        };
190        let my_solution = my_solution.to_clvm(ctx)?;
191        let my_puzzle = self.construct_puzzle(ctx)?;
192
193        catalog.insert_action_spend(ctx, Spend::new(my_puzzle, my_solution))?;
194
195        // spend slots
196        left_slot.spend(ctx, my_inner_puzzle_hash)?;
197        right_slot.spend(ctx, my_inner_puzzle_hash)?;
198
199        Ok(
200            Conditions::new().assert_puzzle_announcement(announcement_id(
201                catalog.coin.puzzle_hash,
202                register_announcement,
203            )),
204        )
205    }
206}