1use chia_protocol::Bytes32;
2use chia_puzzle_types::nft::{NftStateLayerArgs, NftStateLayerSolution};
3use chia_puzzles::NFT_STATE_LAYER_HASH;
4use chia_sdk_types::{
5 conditions::{NewMetadataOutput, UpdateNftMetadata},
6 run_puzzle,
7};
8use clvm_traits::{FromClvm, ToClvm};
9use clvm_utils::{CurriedProgram, ToTreeHash, TreeHash};
10use clvmr::{Allocator, NodePtr};
11
12use crate::{DriverError, Layer, Puzzle, SpendContext};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct NftStateLayer<M, I> {
18 pub metadata: M,
20 pub metadata_updater_puzzle_hash: Bytes32,
22 pub inner_puzzle: I,
25}
26
27impl<M, I> NftStateLayer<M, I> {
28 pub fn new(metadata: M, metadata_updater_puzzle_hash: Bytes32, inner_puzzle: I) -> Self {
29 Self {
30 metadata,
31 metadata_updater_puzzle_hash,
32 inner_puzzle,
33 }
34 }
35
36 pub fn with_metadata<N>(self, metadata: N) -> NftStateLayer<N, I> {
37 NftStateLayer {
38 metadata,
39 metadata_updater_puzzle_hash: self.metadata_updater_puzzle_hash,
40 inner_puzzle: self.inner_puzzle,
41 }
42 }
43}
44
45impl<M, I> Layer for NftStateLayer<M, I>
46where
47 M: ToClvm<Allocator> + FromClvm<Allocator>,
48 I: Layer,
49{
50 type Solution = NftStateLayerSolution<I::Solution>;
51
52 fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
53 let Some(puzzle) = puzzle.as_curried() else {
54 return Ok(None);
55 };
56
57 if puzzle.mod_hash != NFT_STATE_LAYER_HASH.into() {
58 return Ok(None);
59 }
60
61 let args = NftStateLayerArgs::<NodePtr, M>::from_clvm(allocator, puzzle.args)?;
62
63 if args.mod_hash != NFT_STATE_LAYER_HASH.into() {
64 return Err(DriverError::InvalidModHash);
65 }
66
67 let Some(inner_puzzle) =
68 I::parse_puzzle(allocator, Puzzle::parse(allocator, args.inner_puzzle))?
69 else {
70 return Ok(None);
71 };
72
73 Ok(Some(Self {
74 metadata: args.metadata,
75 metadata_updater_puzzle_hash: args.metadata_updater_puzzle_hash,
76 inner_puzzle,
77 }))
78 }
79
80 fn parse_solution(
81 allocator: &Allocator,
82 solution: NodePtr,
83 ) -> Result<Self::Solution, DriverError> {
84 let solution = NftStateLayerSolution::<NodePtr>::from_clvm(allocator, solution)?;
85 Ok(NftStateLayerSolution {
86 inner_solution: I::parse_solution(allocator, solution.inner_solution)?,
87 })
88 }
89
90 fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
91 let inner_puzzle = self.inner_puzzle.construct_puzzle(ctx)?;
92 ctx.curry(NftStateLayerArgs {
93 mod_hash: NFT_STATE_LAYER_HASH.into(),
94 metadata: &self.metadata,
95 metadata_updater_puzzle_hash: self.metadata_updater_puzzle_hash,
96 inner_puzzle,
97 })
98 }
99
100 fn construct_solution(
101 &self,
102 ctx: &mut SpendContext,
103 solution: Self::Solution,
104 ) -> Result<NodePtr, DriverError> {
105 let inner_solution = self
106 .inner_puzzle
107 .construct_solution(ctx, solution.inner_solution)?;
108 ctx.alloc(&NftStateLayerSolution { inner_solution })
109 }
110}
111
112impl<M, I> ToTreeHash for NftStateLayer<M, I>
113where
114 M: ToTreeHash,
115 I: ToTreeHash,
116{
117 fn tree_hash(&self) -> TreeHash {
118 let metadata_hash = self.metadata.tree_hash();
119 let inner_puzzle_hash = self.inner_puzzle.tree_hash();
120 CurriedProgram {
121 program: TreeHash::new(NFT_STATE_LAYER_HASH),
122 args: NftStateLayerArgs {
123 mod_hash: NFT_STATE_LAYER_HASH.into(),
124 metadata: metadata_hash,
125 metadata_updater_puzzle_hash: self.metadata_updater_puzzle_hash,
126 inner_puzzle: inner_puzzle_hash,
127 },
128 }
129 .tree_hash()
130 }
131}
132
133impl<M, I> NftStateLayer<M, I> {
134 pub fn get_next_metadata(
135 allocator: &mut Allocator,
136 current_metadata: &M,
137 curent_metadata_updater_puzzle_hash: Bytes32,
138 condition: UpdateNftMetadata<NodePtr, NodePtr>,
139 ) -> Result<M, DriverError>
140 where
141 M: ToClvm<Allocator> + FromClvm<Allocator>,
142 {
143 let real_metadata_updater_solution: Vec<NodePtr> = vec![
144 current_metadata.to_clvm(allocator)?,
145 curent_metadata_updater_puzzle_hash.to_clvm(allocator)?,
146 condition.updater_solution,
147 ];
148 let real_metadata_updater_solution = real_metadata_updater_solution.to_clvm(allocator)?;
149
150 let output = run_puzzle(
151 allocator,
152 condition.updater_puzzle_reveal,
153 real_metadata_updater_solution,
154 )?;
155
156 let parsed = NewMetadataOutput::<M, NodePtr>::from_clvm(allocator, output)?;
157
158 Ok(parsed.metadata_info.new_metadata)
159 }
160}