1use chia_protocol::Bytes32;
2use chia_puzzle_types::{EveProof, Proof};
3use chia_sdk_types::Conditions;
4use clvm_traits::{clvm_quote, FromClvm, ToClvm};
5use clvm_utils::ToTreeHash;
6use clvmr::{Allocator, NodePtr};
7
8use crate::{assignment_puzzle_announcement_id, DriverError, Launcher, Spend, SpendContext};
9
10use super::{Nft, NftInfo, NftMint};
11
12impl Launcher {
13 pub fn mint_eve_nft<M>(
14 self,
15 ctx: &mut SpendContext,
16 p2_puzzle_hash: Bytes32,
17 metadata: M,
18 metadata_updater_puzzle_hash: Bytes32,
19 royalty_puzzle_hash: Bytes32,
20 royalty_basis_points: u16,
21 ) -> Result<(Conditions, Nft<M>), DriverError>
22 where
23 M: ToClvm<Allocator> + FromClvm<Allocator> + ToTreeHash + Clone,
24 {
25 let launcher_coin = self.coin();
26
27 let nft_info = NftInfo::new(
28 launcher_coin.coin_id(),
29 metadata,
30 metadata_updater_puzzle_hash,
31 None,
32 royalty_puzzle_hash,
33 royalty_basis_points,
34 p2_puzzle_hash,
35 );
36
37 let inner_puzzle_hash = nft_info.inner_puzzle_hash();
38 let (launch_singleton, eve_coin) = self.spend(ctx, inner_puzzle_hash.into(), ())?;
39
40 let proof = Proof::Eve(EveProof {
41 parent_parent_coin_info: launcher_coin.parent_coin_info,
42 parent_amount: launcher_coin.amount,
43 });
44
45 Ok((
46 launch_singleton.create_puzzle_announcement(launcher_coin.coin_id().to_vec().into()),
47 Nft::new(eve_coin, proof, nft_info),
48 ))
49 }
50
51 pub fn mint_nft<M>(
52 self,
53 ctx: &mut SpendContext,
54 mint: NftMint<M>,
55 ) -> Result<(Conditions, Nft<M>), DriverError>
56 where
57 M: ToClvm<Allocator> + FromClvm<Allocator> + ToTreeHash + Clone,
58 {
59 let memos = ctx.hint(mint.p2_puzzle_hash)?;
60 let conditions = Conditions::new()
61 .create_coin(mint.p2_puzzle_hash, self.singleton_amount(), memos)
62 .extend(mint.transfer_condition.clone());
63
64 let inner_puzzle = ctx.alloc(&clvm_quote!(conditions))?;
65 let p2_puzzle_hash = ctx.tree_hash(inner_puzzle).into();
66 let inner_spend = Spend::new(inner_puzzle, NodePtr::NIL);
67
68 let (mint_eve_nft, eve_nft) = self.mint_eve_nft(
69 ctx,
70 p2_puzzle_hash,
71 mint.metadata,
72 mint.metadata_updater_puzzle_hash,
73 mint.royalty_puzzle_hash,
74 mint.royalty_basis_points,
75 )?;
76
77 let child = eve_nft.spend(ctx, inner_spend)?;
78
79 let mut did_conditions = Conditions::new();
80
81 if let Some(transfer_condition) = mint.transfer_condition.clone() {
82 did_conditions = did_conditions.assert_puzzle_announcement(
83 assignment_puzzle_announcement_id(eve_nft.coin.puzzle_hash, &transfer_condition),
84 );
85 }
86
87 Ok((mint_eve_nft.extend(did_conditions), child))
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use crate::{IntermediateLauncher, Launcher, StandardLayer};
94
95 use super::*;
96
97 use chia_consensus::spendbundle_conditions::get_conditions_from_spendbundle;
98 use chia_protocol::{Coin, SpendBundle};
99 use chia_puzzle_types::{nft::NftMetadata, standard::StandardArgs, Memos};
100 use chia_sdk_test::{sign_transaction, BlsPair, Simulator};
101 use chia_sdk_types::{announcement_id, conditions::TransferNft, TESTNET11_CONSTANTS};
102
103 #[test]
104 fn test_nft_mint_cost() -> anyhow::Result<()> {
105 let ctx = &mut SpendContext::new();
106
107 let alice = BlsPair::default();
108 let p2 = StandardLayer::new(alice.pk);
109
110 let coin = Coin::new(Bytes32::new([0; 32]), alice.puzzle_hash, 1);
111
112 let (create_did, did) = Launcher::new(coin.coin_id(), 1).create_simple_did(ctx, &p2)?;
113 p2.spend(ctx, coin, create_did)?;
114
115 ctx.take();
117
118 let coin = Coin::new(Bytes32::new([1; 32]), alice.puzzle_hash, 1);
119 let (mint_nft, _nft) = IntermediateLauncher::new(did.coin.coin_id(), 0, 1)
120 .create(ctx)?
121 .mint_nft(
122 ctx,
123 NftMint::new(NftMetadata::default(), alice.puzzle_hash, 300, None),
124 )?;
125
126 let _ = did.update(
127 ctx,
128 &p2,
129 mint_nft.create_coin_announcement(b"$".to_vec().into()),
130 )?;
131
132 p2.spend(
133 ctx,
134 coin,
135 Conditions::new().assert_coin_announcement(announcement_id(did.coin.coin_id(), "$")),
136 )?;
137
138 let coin_spends = ctx.take();
139 let signature = sign_transaction(&coin_spends, &[alice.sk])?;
140 let spend_bundle = SpendBundle::new(coin_spends, signature);
141
142 let conds = get_conditions_from_spendbundle(
143 ctx,
144 &spend_bundle,
145 u64::MAX,
146 100_000_000,
147 &TESTNET11_CONSTANTS,
148 )?;
149
150 assert_eq!(conds.cost, 109_517_025);
151
152 Ok(())
153 }
154
155 #[test]
156 fn test_bulk_mint() -> anyhow::Result<()> {
157 let mut sim = Simulator::new();
158 let ctx = &mut SpendContext::new();
159
160 let alice = sim.bls(3);
161 let alice_p2 = StandardLayer::new(alice.pk);
162
163 let puzzle_hash = StandardArgs::curry_tree_hash(alice.pk).into();
164
165 let (create_did, did) =
166 Launcher::new(alice.coin.coin_id(), 1).create_simple_did(ctx, &alice_p2)?;
167 alice_p2.spend(ctx, alice.coin, create_did)?;
168
169 let mint = NftMint::new(
170 NftMetadata::default(),
171 puzzle_hash,
172 300,
173 Some(TransferNft::new(
174 Some(did.info.launcher_id),
175 Vec::new(),
176 Some(did.info.inner_puzzle_hash().into()),
177 )),
178 );
179
180 let mint_1 = IntermediateLauncher::new(did.coin.coin_id(), 0, 2)
181 .create(ctx)?
182 .mint_nft(ctx, mint.clone())?
183 .0;
184
185 let mint_2 = IntermediateLauncher::new(did.coin.coin_id(), 1, 2)
186 .create(ctx)?
187 .mint_nft(ctx, mint)?
188 .0;
189
190 let _ = did.update(ctx, &alice_p2, mint_1.extend(mint_2))?;
191
192 sim.spend_coins(ctx.take(), &[alice.sk])?;
193
194 Ok(())
195 }
196
197 #[test]
198 fn test_nonstandard_intermediate_mint() -> anyhow::Result<()> {
199 let mut sim = Simulator::new();
200 let ctx = &mut SpendContext::new();
201
202 let alice = sim.bls(3);
203 let alice_p2 = StandardLayer::new(alice.pk);
204
205 let (create_did, did) =
206 Launcher::new(alice.coin.coin_id(), 1).create_simple_did(ctx, &alice_p2)?;
207 alice_p2.spend(ctx, alice.coin, create_did)?;
208
209 let intermediate_coin = Coin::new(did.coin.coin_id(), alice.puzzle_hash, 0);
210
211 let (create_launcher, launcher) = Launcher::create_early(intermediate_coin.coin_id(), 1);
212
213 let mint = NftMint::new(
214 NftMetadata::default(),
215 alice.puzzle_hash,
216 300,
217 Some(TransferNft::new(
218 Some(did.info.launcher_id),
219 Vec::new(),
220 Some(did.info.inner_puzzle_hash().into()),
221 )),
222 );
223
224 let (mint_nft, _nft) = launcher.mint_nft(ctx, mint)?;
225
226 let _ = did.update(
227 ctx,
228 &alice_p2,
229 mint_nft.create_coin(alice.puzzle_hash, 0, Memos::None),
230 )?;
231 alice_p2.spend(
232 ctx,
233 intermediate_coin,
234 Conditions::new().with(create_launcher),
235 )?;
236
237 sim.spend_coins(ctx.take(), &[alice.sk])?;
238
239 Ok(())
240 }
241
242 #[test]
243 fn test_nonstandard_intermediate_mint_recreated_did() -> anyhow::Result<()> {
244 let mut sim = Simulator::new();
245 let ctx = &mut SpendContext::new();
246
247 let alice = sim.bls(3);
248 let alice_p2 = StandardLayer::new(alice.pk);
249
250 let (create_did, did) =
251 Launcher::new(alice.coin.coin_id(), 1).create_simple_did(ctx, &alice_p2)?;
252 alice_p2.spend(ctx, alice.coin, create_did)?;
253
254 let intermediate_coin = Coin::new(did.coin.coin_id(), alice.puzzle_hash, 0);
255
256 let (create_launcher, launcher) = Launcher::create_early(intermediate_coin.coin_id(), 1);
257 alice_p2.spend(
258 ctx,
259 intermediate_coin,
260 Conditions::new().with(create_launcher),
261 )?;
262
263 let mint = NftMint::new(
264 NftMetadata::default(),
265 alice.puzzle_hash,
266 300,
267 Some(TransferNft::new(
268 Some(did.info.launcher_id),
269 Vec::new(),
270 Some(did.info.inner_puzzle_hash().into()),
271 )),
272 );
273
274 let (mint_nft, _nft_info) = launcher.mint_nft(ctx, mint)?;
275
276 let did = did.update(
277 ctx,
278 &alice_p2,
279 Conditions::new().create_coin(alice.puzzle_hash, 0, Memos::None),
280 )?;
281
282 let _ = did.update(ctx, &alice_p2, mint_nft)?;
283
284 sim.spend_coins(ctx.take(), &[alice.sk])?;
285
286 Ok(())
287 }
288}