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