Skip to main content

chia_sdk_driver/layers/
nft_state_layer.rs

1use chia_protocol::Bytes32;
2use chia_puzzle_types::nft::{NftStateLayerArgs, NftStateLayerSolution};
3use chia_puzzles::NFT_STATE_LAYER_HASH;
4use clvm_traits::{FromClvm, ToClvm};
5use clvm_utils::{CurriedProgram, ToTreeHash, TreeHash};
6use clvmr::{Allocator, NodePtr};
7
8use crate::{DriverError, Layer, Puzzle, SpendContext};
9
10/// The NFT state [`Layer`] keeps track of the current metadata of the NFT and how to change it.
11/// It's typically an inner layer of the [`SingletonLayer`](crate::SingletonLayer).
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct NftStateLayer<M, I> {
14    /// The NFT metadata. The standard metadata type is [`NftMetadata`](chia_puzzle_types::nft::NftMetadata).
15    pub metadata: M,
16    /// The tree hash of the metadata updater puzzle.
17    pub metadata_updater_puzzle_hash: Bytes32,
18    /// The inner puzzle layer. Typically, this is the [`NftOwnershipLayer`](crate::NftOwnershipLayer).
19    /// However, for the NFT0 standard this can be the p2 layer itself.
20    pub inner_puzzle: I,
21}
22
23impl<M, I> NftStateLayer<M, I> {
24    pub fn new(metadata: M, metadata_updater_puzzle_hash: Bytes32, inner_puzzle: I) -> Self {
25        Self {
26            metadata,
27            metadata_updater_puzzle_hash,
28            inner_puzzle,
29        }
30    }
31
32    pub fn with_metadata<N>(self, metadata: N) -> NftStateLayer<N, I> {
33        NftStateLayer {
34            metadata,
35            metadata_updater_puzzle_hash: self.metadata_updater_puzzle_hash,
36            inner_puzzle: self.inner_puzzle,
37        }
38    }
39}
40
41impl<M, I> Layer for NftStateLayer<M, I>
42where
43    M: ToClvm<Allocator> + FromClvm<Allocator>,
44    I: Layer,
45{
46    type Solution = NftStateLayerSolution<I::Solution>;
47
48    fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
49        let Some(puzzle) = puzzle.as_curried() else {
50            return Ok(None);
51        };
52
53        if puzzle.mod_hash != NFT_STATE_LAYER_HASH.into() {
54            return Ok(None);
55        }
56
57        let args = NftStateLayerArgs::<NodePtr, M>::from_clvm(allocator, puzzle.args)?;
58
59        if args.mod_hash != NFT_STATE_LAYER_HASH.into() {
60            return Err(DriverError::InvalidModHash);
61        }
62
63        let Some(inner_puzzle) =
64            I::parse_puzzle(allocator, Puzzle::parse(allocator, args.inner_puzzle))?
65        else {
66            return Ok(None);
67        };
68
69        Ok(Some(Self {
70            metadata: args.metadata,
71            metadata_updater_puzzle_hash: args.metadata_updater_puzzle_hash,
72            inner_puzzle,
73        }))
74    }
75
76    fn parse_solution(
77        allocator: &Allocator,
78        solution: NodePtr,
79    ) -> Result<Self::Solution, DriverError> {
80        let solution = NftStateLayerSolution::<NodePtr>::from_clvm(allocator, solution)?;
81        Ok(NftStateLayerSolution {
82            inner_solution: I::parse_solution(allocator, solution.inner_solution)?,
83        })
84    }
85
86    fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
87        let inner_puzzle = self.inner_puzzle.construct_puzzle(ctx)?;
88        ctx.curry(NftStateLayerArgs {
89            mod_hash: NFT_STATE_LAYER_HASH.into(),
90            metadata: &self.metadata,
91            metadata_updater_puzzle_hash: self.metadata_updater_puzzle_hash,
92            inner_puzzle,
93        })
94    }
95
96    fn construct_solution(
97        &self,
98        ctx: &mut SpendContext,
99        solution: Self::Solution,
100    ) -> Result<NodePtr, DriverError> {
101        let inner_solution = self
102            .inner_puzzle
103            .construct_solution(ctx, solution.inner_solution)?;
104        ctx.alloc(&NftStateLayerSolution { inner_solution })
105    }
106}
107
108impl<M, I> ToTreeHash for NftStateLayer<M, I>
109where
110    M: ToTreeHash,
111    I: ToTreeHash,
112{
113    fn tree_hash(&self) -> TreeHash {
114        let metadata_hash = self.metadata.tree_hash();
115        let inner_puzzle_hash = self.inner_puzzle.tree_hash();
116        CurriedProgram {
117            program: TreeHash::new(NFT_STATE_LAYER_HASH),
118            args: NftStateLayerArgs {
119                mod_hash: NFT_STATE_LAYER_HASH.into(),
120                metadata: metadata_hash,
121                metadata_updater_puzzle_hash: self.metadata_updater_puzzle_hash,
122                inner_puzzle: inner_puzzle_hash,
123            },
124        }
125        .tree_hash()
126    }
127}