chia_sdk_driver/layers/
nft_state_layer.rs1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct NftStateLayer<M, I> {
14 pub metadata: M,
16 pub metadata_updater_puzzle_hash: Bytes32,
18 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}