chia_sdk_driver/primitives/action_layer/
catalog_registry_info.rs

1use chia_protocol::Bytes32;
2use chia_puzzle_types::singleton::SingletonArgs;
3use chia_sdk_types::{
4    MerkleTree,
5    puzzles::{ActionLayerArgs, DefaultFinalizer2ndCurryArgs},
6};
7use clvm_traits::{FromClvm, ToClvm};
8use clvm_utils::{ToTreeHash, TreeHash};
9use clvmr::Allocator;
10use hex_literal::hex;
11
12use crate::{
13    ActionLayer, CatalogRefundAction, CatalogRegisterAction, DelegatedStateAction, DriverError,
14    Finalizer, Layer, Puzzle, SingletonAction, SingletonLayer,
15};
16
17use super::CatalogRegistry;
18
19pub type CatalogRegistryLayers = SingletonLayer<ActionLayer<CatalogRegistryState>>;
20
21#[must_use]
22#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm, Copy)]
23#[clvm(list)]
24pub struct CatalogRegistryState {
25    pub cat_maker_puzzle_hash: Bytes32,
26    #[clvm(rest)]
27    pub registration_price: u64,
28}
29
30#[must_use]
31#[derive(Debug, Clone, PartialEq, Eq, Copy)]
32pub struct CatalogRegistryConstants {
33    pub launcher_id: Bytes32,
34    pub royalty_address: Bytes32,
35    pub royalty_basis_points: u16,
36    pub precommit_payout_puzzle_hash: Bytes32,
37    pub relative_block_height: u32,
38    pub price_singleton_launcher_id: Bytes32,
39}
40
41impl CatalogRegistryConstants {
42    pub fn get(testnet11: bool) -> Self {
43        if testnet11 {
44            return CatalogRegistryConstants {
45                launcher_id: Bytes32::from(hex!(
46                    "0b705afb0d848794311970de0cb98722468fad6c8f687337735ab9e5286d7704"
47                )),
48                royalty_address: Bytes32::from(hex!(
49                    "b3aea098428b2b5e6d57cf3bff6ee82e3950dec338b17df6d8ee20944787def5"
50                )),
51                royalty_basis_points: 100,
52                precommit_payout_puzzle_hash: Bytes32::from(hex!(
53                    "b3aea098428b2b5e6d57cf3bff6ee82e3950dec338b17df6d8ee20944787def5"
54                )),
55                relative_block_height: 4,
56                price_singleton_launcher_id: Bytes32::from(hex!(
57                    "45dff01375d9bd681d36a3a186ab3d0c86eb809d7f85fff950f0b37f068ec664"
58                )),
59            };
60        }
61
62        todo!("oops - catalog constants for mainnet are not yet available");
63    }
64
65    pub fn with_price_singleton(mut self, price_singleton_launcher_id: Bytes32) -> Self {
66        self.price_singleton_launcher_id = price_singleton_launcher_id;
67        self
68    }
69
70    pub fn with_launcher_id(mut self, launcher_id: Bytes32) -> Self {
71        self.launcher_id = launcher_id;
72        self
73    }
74}
75
76#[must_use]
77#[derive(Debug, Clone, PartialEq, Eq, Copy)]
78pub struct CatalogRegistryInfo {
79    pub state: CatalogRegistryState,
80
81    pub constants: CatalogRegistryConstants,
82}
83
84impl CatalogRegistryInfo {
85    pub fn new(state: CatalogRegistryState, constants: CatalogRegistryConstants) -> Self {
86        Self { state, constants }
87    }
88
89    pub fn with_state(mut self, state: CatalogRegistryState) -> Self {
90        self.state = state;
91        self
92    }
93
94    pub fn action_puzzle_hashes(constants: &CatalogRegistryConstants) -> [Bytes32; 3] {
95        [
96            CatalogRegisterAction::from_constants(constants)
97                .tree_hash()
98                .into(),
99            CatalogRefundAction::from_constants(constants)
100                .tree_hash()
101                .into(),
102            <DelegatedStateAction as SingletonAction<CatalogRegistry>>::from_constants(constants)
103                .tree_hash()
104                .into(),
105        ]
106    }
107
108    #[must_use]
109    pub fn into_layers(self) -> CatalogRegistryLayers {
110        SingletonLayer::new(
111            self.constants.launcher_id,
112            ActionLayer::from_action_puzzle_hashes(
113                &Self::action_puzzle_hashes(&self.constants),
114                self.state,
115                Finalizer::Default {
116                    hint: self.constants.launcher_id,
117                },
118            ),
119        )
120    }
121
122    pub fn parse(
123        allocator: &mut Allocator,
124        puzzle: Puzzle,
125        constants: CatalogRegistryConstants,
126    ) -> Result<Option<Self>, DriverError> {
127        let Some(layers) = CatalogRegistryLayers::parse_puzzle(allocator, puzzle)? else {
128            return Ok(None);
129        };
130
131        let action_puzzle_hashes = Self::action_puzzle_hashes(&constants);
132        let merkle_root = MerkleTree::new(&action_puzzle_hashes).root();
133        if layers.inner_puzzle.merkle_root != merkle_root {
134            return Ok(None);
135        }
136
137        Ok(Some(Self::from_layers(&layers, constants)))
138    }
139
140    pub fn from_layers(
141        layers: &CatalogRegistryLayers,
142        constants: CatalogRegistryConstants,
143    ) -> Self {
144        Self {
145            state: layers.inner_puzzle.state,
146            constants,
147        }
148    }
149
150    pub fn puzzle_hash(&self) -> TreeHash {
151        SingletonArgs::curry_tree_hash(self.constants.launcher_id, self.inner_puzzle_hash())
152    }
153
154    pub fn inner_puzzle_hash(&self) -> TreeHash {
155        ActionLayerArgs::curry_tree_hash(
156            DefaultFinalizer2ndCurryArgs::curry_tree_hash(self.constants.launcher_id),
157            MerkleTree::new(&Self::action_puzzle_hashes(&self.constants)).root(),
158            self.state.tree_hash(),
159        )
160    }
161}