chia_sdk_driver/actions/
mint_nft.rs1use chia_protocol::Bytes32;
2
3use crate::{
4 Asset, Deltas, DriverError, HashedPtr, Id, SingletonSpends, SpendAction, SpendContext,
5 SpendKind, Spends,
6};
7
8#[derive(Debug, Clone, Copy)]
9pub struct MintNftAction {
10 pub parent_id: Id,
11 pub metadata: HashedPtr,
12 pub metadata_updater_puzzle_hash: Bytes32,
13 pub royalty_puzzle_hash: Bytes32,
14 pub royalty_basis_points: u16,
15 pub amount: u64,
16}
17
18impl MintNftAction {
19 pub fn new(
20 parent_id: Id,
21 metadata: HashedPtr,
22 metadata_updater_puzzle_hash: Bytes32,
23 royalty_puzzle_hash: Bytes32,
24 royalty_basis_points: u16,
25 amount: u64,
26 ) -> Self {
27 Self {
28 parent_id,
29 metadata,
30 metadata_updater_puzzle_hash,
31 royalty_puzzle_hash,
32 royalty_basis_points,
33 amount,
34 }
35 }
36}
37
38impl Default for MintNftAction {
39 fn default() -> Self {
40 Self::new(
41 Id::Xch,
42 HashedPtr::NIL,
43 Bytes32::default(),
44 Bytes32::default(),
45 0,
46 1,
47 )
48 }
49}
50
51impl SpendAction for MintNftAction {
52 fn calculate_delta(&self, deltas: &mut Deltas, index: usize) {
53 deltas.update(Id::Xch).output += self.amount;
54 deltas.update(Id::New(index)).input += self.amount;
55
56 if matches!(self.parent_id, Id::Xch) {
57 deltas.set_needed(Id::Xch);
58 } else {
59 let did = deltas.update(self.parent_id);
60 did.input += 1;
61 did.output += 1;
62 }
63 }
64
65 fn spend(
66 &self,
67 ctx: &mut SpendContext,
68 spends: &mut Spends,
69 index: usize,
70 ) -> Result<(), DriverError> {
71 let (p2_puzzle_hash, source_kind, launcher) = if matches!(self.parent_id, Id::Xch) {
72 let (source, launcher) = spends.xch.create_launcher(self.amount)?;
73 let source = &mut spends.xch.items[source];
74 (source.asset.p2_puzzle_hash(), &mut source.kind, launcher)
75 } else {
76 let did = spends
77 .dids
78 .get_mut(&self.parent_id)
79 .ok_or(DriverError::InvalidAssetId)?;
80 let (source, launcher) = did.create_launcher(self.amount)?;
81 let p2_puzzle_hash = did.last()?.asset.p2_puzzle_hash();
82 let source = &mut did.lineage[source];
83 (p2_puzzle_hash, &mut source.kind, launcher)
84 };
85
86 let (parent_conditions, eve_nft) = launcher.mint_eve_nft(
87 ctx,
88 p2_puzzle_hash,
89 self.metadata,
90 self.metadata_updater_puzzle_hash,
91 self.royalty_puzzle_hash,
92 self.royalty_basis_points,
93 )?;
94
95 match source_kind {
96 SpendKind::Conditions(spend) => {
97 spend.add_conditions(parent_conditions);
98 }
99 SpendKind::Settlement(_) => {
100 return Err(DriverError::CannotEmitConditions);
101 }
102 }
103
104 spends
105 .nfts
106 .insert(Id::New(index), SingletonSpends::new(eve_nft, true));
107
108 Ok(())
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use anyhow::Result;
115 use chia_sdk_test::Simulator;
116 use indexmap::indexmap;
117
118 use crate::{Action, Relation};
119
120 use super::*;
121
122 #[test]
123 fn test_action_mint_nft() -> Result<()> {
124 let mut sim = Simulator::new();
125 let mut ctx = SpendContext::new();
126
127 let alice = sim.bls(1);
128
129 let mut spends = Spends::new(alice.puzzle_hash);
130 spends.add(alice.coin);
131
132 let deltas = spends.apply(&mut ctx, &[Action::mint_empty_nft()])?;
133
134 let outputs = spends.finish_with_keys(
135 &mut ctx,
136 &deltas,
137 Relation::None,
138 &indexmap! { alice.puzzle_hash => alice.pk },
139 )?;
140
141 sim.spend_coins(ctx.take(), &[alice.sk])?;
142
143 let nft = outputs.nfts[&Id::New(0)];
144 assert_ne!(sim.coin_state(nft.coin.coin_id()), None);
145 assert_eq!(nft.info.p2_puzzle_hash, alice.puzzle_hash);
146
147 Ok(())
148 }
149
150 #[test]
151 fn test_action_mint_nft_from_did() -> Result<()> {
152 let mut sim = Simulator::new();
153 let mut ctx = SpendContext::new();
154
155 let alice = sim.bls(2);
156
157 let mut spends = Spends::new(alice.puzzle_hash);
158 spends.add(alice.coin);
159
160 let deltas = spends.apply(
161 &mut ctx,
162 &[
163 Action::create_empty_did(),
164 Action::mint_empty_nft_from_did(Id::New(0)),
165 ],
166 )?;
167
168 let outputs = spends.finish_with_keys(
169 &mut ctx,
170 &deltas,
171 Relation::None,
172 &indexmap! { alice.puzzle_hash => alice.pk },
173 )?;
174
175 sim.spend_coins(ctx.take(), &[alice.sk])?;
176
177 let did = outputs.dids[&Id::New(0)];
178 assert_ne!(sim.coin_state(did.coin.coin_id()), None);
179 assert_eq!(did.info.p2_puzzle_hash, alice.puzzle_hash);
180
181 let nft = outputs.nfts[&Id::New(1)];
182 assert_ne!(sim.coin_state(nft.coin.coin_id()), None);
183 assert_eq!(nft.info.p2_puzzle_hash, alice.puzzle_hash);
184
185 Ok(())
186 }
187}