scrypto_test/sdk/resource/resource_builder.rs
1// TODO: Need to deduplicate this code.
2
3use crate::prelude::*;
4use radix_common::math::Decimal;
5use radix_common::traits::NonFungibleData;
6use radix_engine_interface::object_modules::metadata::MetadataInit;
7use radix_engine_interface::object_modules::role_assignment::RoleDefinition;
8use radix_engine_interface::object_modules::ModuleConfig;
9use radix_engine_interface::prelude::SystemApi;
10use sbor::rust::marker::PhantomData;
11
12/// Not divisible.
13pub const DIVISIBILITY_NONE: u8 = 0;
14/// The maximum divisibility supported.
15pub const DIVISIBILITY_MAXIMUM: u8 = 18;
16
17/// Utility for setting up a new resource.
18///
19/// * You start the building process with one of the methods starting with `new_`.
20/// * The allowed methods change depending on which methods have already been called. For example,
21/// you can either use `owner_non_fungible_badge` or set access rules individually, but not both.
22/// * You can complete the building process using either `create_with_no_initial_supply()` or
23/// `mint_initial_supply(..)`.
24///
25/// ### Example
26/// ```no_run
27/// use scrypto_test::prelude::*;
28///
29/// let mut env = TestEnvironment::new();
30///
31/// let bucket = ResourceBuilder::new_fungible(OwnerRole::None)
32/// .mint_initial_supply(5, &mut env);
33/// ```
34pub struct ResourceBuilder;
35
36impl ResourceBuilder {
37 /// Starts a new builder to create a fungible resource.
38 pub fn new_fungible(owner_role: OwnerRole) -> InProgressResourceBuilder<FungibleResourceType> {
39 InProgressResourceBuilder::new(owner_role)
40 }
41
42 /// Starts a new builder to create a non-fungible resource with a `NonFungibleIdType::String`
43 pub fn new_string_non_fungible<D: NonFungibleData>(
44 owner_role: OwnerRole,
45 ) -> InProgressResourceBuilder<NonFungibleResourceType<StringNonFungibleLocalId, D>> {
46 InProgressResourceBuilder::new(owner_role)
47 }
48
49 /// Starts a new builder to create a non-fungible resource with a `NonFungibleIdType::Integer`
50 pub fn new_integer_non_fungible<D: NonFungibleData>(
51 owner_role: OwnerRole,
52 ) -> InProgressResourceBuilder<NonFungibleResourceType<IntegerNonFungibleLocalId, D>> {
53 InProgressResourceBuilder::new(owner_role)
54 }
55
56 /// Starts a new builder to create a non-fungible resource with a `NonFungibleIdType::Bytes`
57 pub fn new_bytes_non_fungible<D: NonFungibleData>(
58 owner_role: OwnerRole,
59 ) -> InProgressResourceBuilder<NonFungibleResourceType<BytesNonFungibleLocalId, D>> {
60 InProgressResourceBuilder::new(owner_role)
61 }
62
63 /// Starts a new builder to create a non-fungible resource with a `NonFungibleIdType::RUID`
64 pub fn new_ruid_non_fungible<D: NonFungibleData>(
65 owner_role: OwnerRole,
66 ) -> InProgressResourceBuilder<NonFungibleResourceType<RUIDNonFungibleLocalId, D>> {
67 InProgressResourceBuilder::new(owner_role)
68 }
69}
70
71/// Utility for setting up a new resource, which has building in progress.
72///
73/// * You start the building process with one of the methods starting with `ResourceBuilder::new_`.
74/// * The allowed methods change depending on which methods have already been called. For example,
75/// you can either use `owner_non_fungible_badge` or set access rules individually, but not both.
76/// * You can complete the building process using either `create_with_no_initial_supply()` or
77/// `mint_initial_supply(..)`.
78///
79/// ### Example
80/// ```
81/// use scrypto_test::prelude::*;
82///
83/// let mut env = TestEnvironment::new();
84///
85/// let bucket = ResourceBuilder::new_fungible(OwnerRole::None)
86/// .mint_initial_supply(5, &mut env)?;
87///
88/// # Ok::<(), RuntimeError>(())
89/// ```
90#[must_use]
91pub struct InProgressResourceBuilder<T: AnyResourceType> {
92 owner_role: OwnerRole,
93 resource_type: T,
94 resource_roles: T::ResourceRoles,
95 metadata_config: Option<ModuleConfig<MetadataInit>>,
96 address_reservation: Option<GlobalAddressReservation>,
97}
98
99impl<T: AnyResourceType> InProgressResourceBuilder<T> {
100 fn new(owner_role: OwnerRole) -> Self {
101 Self {
102 owner_role,
103 resource_type: T::default(),
104 metadata_config: None,
105 address_reservation: None,
106 resource_roles: T::ResourceRoles::default(),
107 }
108 }
109}
110
111// Various types for ResourceType
112pub trait AnyResourceType: Default {
113 type ResourceRoles: Default;
114}
115
116pub struct FungibleResourceType {
117 divisibility: u8,
118}
119impl AnyResourceType for FungibleResourceType {
120 type ResourceRoles = FungibleResourceRoles;
121}
122impl Default for FungibleResourceType {
123 fn default() -> Self {
124 Self {
125 divisibility: DIVISIBILITY_MAXIMUM,
126 }
127 }
128}
129
130pub struct NonFungibleResourceType<T: IsNonFungibleLocalId, D: NonFungibleData>(
131 PhantomData<T>,
132 PhantomData<D>,
133);
134impl<T: IsNonFungibleLocalId, D: NonFungibleData> AnyResourceType
135 for NonFungibleResourceType<T, D>
136{
137 type ResourceRoles = NonFungibleResourceRoles;
138}
139impl<T: IsNonFungibleLocalId, D: NonFungibleData> Default for NonFungibleResourceType<T, D> {
140 fn default() -> Self {
141 Self(PhantomData, PhantomData)
142 }
143}
144
145// //////////////////////////////////////////////////////////
146// PUBLIC TRAITS AND METHODS
147// All public methods first - these all need good rust docs
148// //////////////////////////////////////////////////////////
149
150pub trait UpdateMetadataBuilder: private::CanSetMetadata {
151 fn metadata(self, metadata: ModuleConfig<MetadataInit>) -> Self::OutputBuilder {
152 self.set_metadata(metadata)
153 }
154}
155impl<B: private::CanSetMetadata> UpdateMetadataBuilder for B {}
156
157pub trait SetAddressReservationBuilder: private::CanSetAddressReservation {
158 /// Sets the address reservation
159 fn with_address(self, reservation: GlobalAddressReservation) -> Self::OutputBuilder {
160 self.set_address(reservation)
161 }
162}
163impl<B: private::CanSetAddressReservation> SetAddressReservationBuilder for B {}
164
165pub trait UpdateAuthBuilder {
166 /// Sets the resource to be mintable
167 ///
168 /// * The first parameter is the access rule which allows minting of the resource.
169 /// * The second parameter is the mutability / access rule which controls if and how the access
170 /// rule can be updated.
171 ///
172 /// ### Examples
173 ///
174 /// ```no_run
175 /// use radix_engine_interface::mint_roles;
176 /// use scrypto_test::prelude::*;
177 ///
178 /// # let resource_address = XRD;
179 /// // Sets the resource to be mintable with a proof of a specific resource, and this is locked forever.
180 /// ResourceBuilder::new_fungible(OwnerRole::None)
181 /// .mint_roles(mint_roles! {
182 /// minter => rule!(require(resource_address));
183 /// minter_updater => rule!(deny_all);
184 /// });
185 ///
186 /// # let resource_address = XRD;
187 /// // Sets the resource to not be mintable, but this is can be changed in future by the second rule
188 /// ResourceBuilder::new_fungible(OwnerRole::None)
189 /// .mint_roles(mint_roles! {
190 /// minter => rule!(deny_all);
191 /// minter_updater => rule!(require(resource_address));
192 /// });
193 /// ```
194 fn mint_roles(self, mint_roles: Option<MintRoles<RoleDefinition>>) -> Self;
195
196 /// Sets the resource to be burnable.
197 ///
198 /// * The first parameter is the access rule which allows minting of the resource.
199 /// * The second parameter is the mutability / access rule which controls if and how the access
200 /// rule can be updated.
201 ///
202 /// ### Examples
203 ///
204 /// ```no_run
205 /// use radix_engine_interface::burn_roles;
206 /// use scrypto_test::prelude::*;
207 ///
208 /// # let resource_address = XRD;
209 /// // Sets the resource to be burnable with a proof of a specific resource, and this is locked forever.
210 /// ResourceBuilder::new_fungible(OwnerRole::None)
211 /// .burn_roles(burn_roles! {
212 /// burner => rule!(require(resource_address));
213 /// burner_updater => rule!(deny_all);
214 /// });
215 ///
216 /// # let resource_address = XRD;
217 /// // Sets the resource to be freely burnable, but this is can be changed in future by the second rule.
218 /// ResourceBuilder::new_fungible(OwnerRole::None)
219 /// .burn_roles(burn_roles! {
220 /// burner => rule!(allow_all);
221 /// burner_updater => rule!(require(resource_address));
222 /// });
223 /// ```
224 fn burn_roles(self, burn_roles: Option<BurnRoles<RoleDefinition>>) -> Self;
225
226 /// Sets the resource to be recallable from vaults.
227 ///
228 /// * The first parameter is the access rule which allows recalling of the resource.
229 /// * The second parameter is the mutability / access rule which controls if and how the access
230 /// rule can be updated.
231 ///
232 /// ### Examples
233 ///
234 /// ```no_run
235 /// use scrypto_test::prelude::*;
236 ///
237 /// # let resource_address = XRD;
238 /// // Sets the resource to be recallable with a proof of a specific resource, and this is locked forever.
239 /// ResourceBuilder::new_fungible(OwnerRole::None)
240 /// .recall_roles(recall_roles! {
241 /// recaller => rule!(require(resource_address));
242 /// recaller_updater => rule!(deny_all);
243 /// });
244 ///
245 /// # let resource_address = XRD;
246 /// // Sets the resource to not be recallable, but this is can be changed in future by the second rule
247 /// ResourceBuilder::new_fungible(OwnerRole::None)
248 /// .recall_roles(recall_roles! {
249 /// recaller => rule!(deny_all);
250 /// recaller_updater => rule!(require(resource_address));
251 /// });
252 /// ```
253 fn recall_roles(self, recall_roles: Option<RecallRoles<RoleDefinition>>) -> Self;
254
255 /// Sets the resource to have vaults be freezable.
256 ///
257 /// * The first parameter is the access rule which allows freezing of the vault.
258 /// * The second parameter is the mutability / access rule which controls if and how the access
259 /// rule can be updated.
260 ///
261 /// ### Examples
262 ///
263 /// ```no_run
264 /// use radix_engine_interface::freeze_roles;
265 /// use scrypto_test::prelude::*;
266 ///
267 /// # let resource_address = XRD;
268 /// // Sets the resource to be freezeable with a proof of a specific resource, and this is locked forever.
269 /// ResourceBuilder::new_fungible(OwnerRole::None)
270 /// .freeze_roles(freeze_roles! {
271 /// freezer => rule!(require(resource_address));
272 /// freezer_updater => rule!(deny_all);
273 /// });
274 ///
275 /// # let resource_address = XRD;
276 /// // Sets the resource to not be freezeable, but this is can be changed in future by the second rule
277 /// ResourceBuilder::new_fungible(OwnerRole::None)
278 /// .freeze_roles(freeze_roles! {
279 /// freezer => rule!(deny_all);
280 /// freezer_updater => rule!(require(resource_address));
281 /// });
282 /// ```
283 fn freeze_roles(self, freeze_roles: Option<FreezeRoles<RoleDefinition>>) -> Self;
284
285 /// Sets the role rules of withdrawing from a vault of this resource.
286 ///
287 /// * The first parameter is the access rule which allows withdrawing from a vault.
288 /// * The second parameter is the mutability / access rule which controls if and how the access
289 /// rule can be updated.
290 ///
291 /// ### Examples
292 ///
293 /// ```no_run
294 /// use radix_engine_interface::withdraw_roles;
295 /// use scrypto_test::prelude::*;
296 ///
297 /// # let resource_address = XRD;
298 /// // Sets the resource to be withdrawable with a proof of a specific resource, and this is locked forever.
299 /// ResourceBuilder::new_fungible(OwnerRole::None)
300 /// .withdraw_roles(withdraw_roles! {
301 /// withdrawer => rule!(require(resource_address));
302 /// withdrawer_updater => rule!(deny_all);
303 /// });
304 ///
305 /// # let resource_address = XRD;
306 /// // Sets the resource to not be withdrawable, but this is can be changed in future by the second rule
307 /// ResourceBuilder::new_fungible(OwnerRole::None)
308 /// .withdraw_roles(withdraw_roles! {
309 /// withdrawer => rule!(deny_all);
310 /// withdrawer_updater => rule!(require(resource_address));
311 /// });
312 /// ```
313 fn withdraw_roles(self, withdraw_roles: Option<WithdrawRoles<RoleDefinition>>) -> Self;
314
315 /// Sets the roles rules of depositing this resource into a vault.
316 ///
317 /// * The first parameter is the access rule which allows depositing into a vault.
318 /// * The second parameter is the mutability / access rule which controls if and how the access
319 /// rule can be updated.
320 ///
321 /// ### Examples
322 ///
323 /// ```no_run
324 /// use scrypto_test::prelude::*;
325 ///
326 /// # let resource_address = XRD;
327 /// // Sets the resource to be depositable with a proof of a specific resource, and this is locked forever.
328 /// ResourceBuilder::new_fungible(OwnerRole::None)
329 /// .deposit_roles(deposit_roles! {
330 /// depositor => rule!(require(resource_address));
331 /// depositor_updater => rule!(deny_all);
332 /// });
333 ///
334 /// # let resource_address = XRD;
335 /// // Sets the resource to not be depositable, but this is can be changed in future by the second rule
336 /// ResourceBuilder::new_fungible(OwnerRole::None)
337 /// .deposit_roles(deposit_roles! {
338 /// depositor => rule!(deny_all);
339 /// depositor_updater => rule!(require(resource_address));
340 /// });
341 /// ```
342 fn deposit_roles(self, deposit_roles: Option<DepositRoles<RoleDefinition>>) -> Self;
343}
344
345impl UpdateAuthBuilder for InProgressResourceBuilder<FungibleResourceType> {
346 fn mint_roles(mut self, mint_roles: Option<MintRoles<RoleDefinition>>) -> Self {
347 self.resource_roles.mint_roles = mint_roles;
348 self
349 }
350
351 fn burn_roles(mut self, burn_roles: Option<BurnRoles<RoleDefinition>>) -> Self {
352 self.resource_roles.burn_roles = burn_roles;
353 self
354 }
355
356 fn recall_roles(mut self, recall_roles: Option<RecallRoles<RoleDefinition>>) -> Self {
357 self.resource_roles.recall_roles = recall_roles;
358 self
359 }
360
361 fn freeze_roles(mut self, freeze_roles: Option<FreezeRoles<RoleDefinition>>) -> Self {
362 self.resource_roles.freeze_roles = freeze_roles;
363 self
364 }
365
366 fn withdraw_roles(mut self, withdraw_roles: Option<WithdrawRoles<RoleDefinition>>) -> Self {
367 self.resource_roles.withdraw_roles = withdraw_roles;
368 self
369 }
370
371 fn deposit_roles(mut self, deposit_roles: Option<DepositRoles<RoleDefinition>>) -> Self {
372 self.resource_roles.deposit_roles = deposit_roles;
373 self
374 }
375}
376
377impl<T: IsNonFungibleLocalId, D: NonFungibleData> UpdateAuthBuilder
378 for InProgressResourceBuilder<NonFungibleResourceType<T, D>>
379{
380 fn mint_roles(mut self, mint_roles: Option<MintRoles<RoleDefinition>>) -> Self {
381 self.resource_roles.mint_roles = mint_roles;
382 self
383 }
384
385 fn burn_roles(mut self, burn_roles: Option<BurnRoles<RoleDefinition>>) -> Self {
386 self.resource_roles.burn_roles = burn_roles;
387 self
388 }
389
390 fn recall_roles(mut self, recall_roles: Option<RecallRoles<RoleDefinition>>) -> Self {
391 self.resource_roles.recall_roles = recall_roles;
392 self
393 }
394
395 fn freeze_roles(mut self, freeze_roles: Option<FreezeRoles<RoleDefinition>>) -> Self {
396 self.resource_roles.freeze_roles = freeze_roles;
397 self
398 }
399
400 fn withdraw_roles(mut self, withdraw_roles: Option<WithdrawRoles<RoleDefinition>>) -> Self {
401 self.resource_roles.withdraw_roles = withdraw_roles;
402 self
403 }
404
405 fn deposit_roles(mut self, deposit_roles: Option<DepositRoles<RoleDefinition>>) -> Self {
406 self.resource_roles.deposit_roles = deposit_roles;
407 self
408 }
409}
410
411impl<T: IsNonFungibleLocalId, D: NonFungibleData>
412 InProgressResourceBuilder<NonFungibleResourceType<T, D>>
413{
414 /// Sets how each non-fungible's mutable data can be updated.
415 ///
416 /// * The first parameter is the access rule which allows updating the mutable data of each
417 /// non-fungible.
418 /// * The second parameter is the mutability / access rule which controls if and how the access
419 /// rule can be updated.
420 ///
421 /// ### Examples
422 ///
423 /// ```rust
424 /// use scrypto_test::prelude::*;
425 /// # let resource_address = XRD;
426 ///
427 /// #[derive(ScryptoSbor, NonFungibleData)]
428 /// struct NFData {
429 /// pub name: String,
430 /// #[mutable]
431 /// pub flag: bool,
432 /// }
433 /// // Permits the updating of non-fungible mutable data with a proof of a specific resource, and this is locked forever.
434 /// ResourceBuilder::new_ruid_non_fungible::<NFData>(OwnerRole::None)
435 /// .non_fungible_data_update_roles(non_fungible_data_update_roles! {
436 /// non_fungible_data_updater => rule!(require(resource_address));
437 /// non_fungible_data_updater_updater => rule!(deny_all);
438 /// });
439 ///
440 /// # let resource_address = XRD;
441 /// // Does not currently permit the updating of non-fungible mutable data, but this is can be changed in future by the second rule.
442 /// ResourceBuilder::new_ruid_non_fungible::<NFData>(OwnerRole::None)
443 /// .non_fungible_data_update_roles(non_fungible_data_update_roles! {
444 /// non_fungible_data_updater => rule!(deny_all);
445 /// non_fungible_data_updater_updater => rule!(require(resource_address));
446 /// });
447 /// ```
448 pub fn non_fungible_data_update_roles(
449 mut self,
450 non_fungible_data_update_roles: Option<NonFungibleDataUpdateRoles<RoleDefinition>>,
451 ) -> Self {
452 self.resource_roles.non_fungible_data_update_roles = non_fungible_data_update_roles;
453 self
454 }
455}
456
457pub trait SetOwnerBuilder: private::CanAddOwner {
458 /// Sets the owner badge to be the given non-fungible.
459 ///
460 /// The owner badge is given starting permissions to update the metadata/data associated with
461 /// the resource, and to change any of the access rules after creation.
462 fn owner_non_fungible_badge(self, owner_badge: NonFungibleGlobalId) -> Self::OutputBuilder {
463 self.set_owner(owner_badge)
464 }
465}
466impl<B: private::CanAddOwner> SetOwnerBuilder for B {}
467
468pub trait CreateWithNoSupplyBuilder: private::CanCreateWithNoSupply {
469 /// Creates the resource with no initial supply.
470 ///
471 /// The resource's address is returned.
472 fn create_with_no_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
473 self,
474 env: &mut Y,
475 ) -> Result<ResourceManager, E> {
476 match self.into_create_with_no_supply_invocation() {
477 private::CreateWithNoSupply::Fungible {
478 owner_role,
479 divisibility,
480 resource_roles,
481 metadata,
482 address_reservation,
483 } => {
484 let metadata = metadata.unwrap_or_else(Default::default);
485
486 let bytes = env.call_function(
487 RESOURCE_PACKAGE,
488 FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
489 FUNGIBLE_RESOURCE_MANAGER_CREATE_IDENT,
490 scrypto_encode(&FungibleResourceManagerCreateInput {
491 owner_role,
492 divisibility,
493 track_total_supply: true,
494 metadata,
495 resource_roles,
496 address_reservation,
497 })
498 .unwrap(),
499 )?;
500 Ok(scrypto_decode(&bytes).unwrap())
501 }
502 private::CreateWithNoSupply::NonFungible {
503 owner_role,
504 id_type,
505 non_fungible_schema,
506 resource_roles,
507 metadata,
508 address_reservation,
509 } => {
510 let metadata = metadata.unwrap_or_else(Default::default);
511
512 let bytes = env.call_function(
513 RESOURCE_PACKAGE,
514 NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
515 NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_IDENT,
516 scrypto_encode(&NonFungibleResourceManagerCreateInput {
517 owner_role,
518 id_type,
519 track_total_supply: true,
520 non_fungible_schema,
521 resource_roles,
522 metadata,
523 address_reservation,
524 })
525 .unwrap(),
526 )?;
527 Ok(scrypto_decode(&bytes).unwrap())
528 }
529 }
530 }
531}
532impl<B: private::CanCreateWithNoSupply> CreateWithNoSupplyBuilder for B {}
533
534impl InProgressResourceBuilder<FungibleResourceType> {
535 /// Set the resource's divisibility: the number of digits of precision after the decimal point
536 /// in its balances.
537 ///
538 /// * `0` means the resource is not divisible (balances are always whole numbers)
539 /// * `18` is the maximum divisibility, and the default.
540 ///
541 /// ### Examples
542 ///
543 /// ```no_run
544 /// use scrypto_test::prelude::*;
545 ///
546 /// // Only permits whole-number balances.
547 /// ResourceBuilder::new_fungible(OwnerRole::None)
548 /// .divisibility(0);
549 ///
550 /// // Only permits balances to 3 decimal places.
551 /// ResourceBuilder::new_fungible(OwnerRole::None)
552 /// .divisibility(3);
553 /// ```
554 pub fn divisibility(mut self, divisibility: u8) -> Self {
555 assert!(divisibility <= 18);
556 self.resource_type = FungibleResourceType { divisibility };
557 self
558 }
559}
560
561impl InProgressResourceBuilder<FungibleResourceType> {
562 /// Creates resource with the given initial supply.
563 ///
564 /// # Example
565 /// ```no_run
566 /// use scrypto_test::prelude::*;
567 ///
568 /// let mut env = TestEnvironment::new();
569 ///
570 /// let bucket: FungibleBucket = ResourceBuilder::new_fungible(OwnerRole::None)
571 /// .mint_initial_supply(5, &mut env)?;
572 ///
573 /// # Ok::<(), RuntimeError>(())
574 /// ```
575 pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
576 mut self,
577 amount: impl Into<Decimal>,
578 env: &mut Y,
579 ) -> Result<FungibleBucket, E> {
580 let metadata = self.metadata_config.take().unwrap_or_default();
581
582 let bytes = env.call_function(
583 RESOURCE_PACKAGE,
584 FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
585 FUNGIBLE_RESOURCE_MANAGER_CREATE_WITH_INITIAL_SUPPLY_IDENT,
586 scrypto_encode(&FungibleResourceManagerCreateWithInitialSupplyInput {
587 owner_role: self.owner_role,
588 track_total_supply: true,
589 divisibility: self.resource_type.divisibility,
590 resource_roles: self.resource_roles,
591 metadata,
592 initial_supply: amount.into(),
593 address_reservation: self.address_reservation,
594 })
595 .unwrap(),
596 )?;
597
598 let result: (ResourceAddress, FungibleBucket) = scrypto_decode(&bytes).unwrap();
599 Ok(result.1)
600 }
601}
602
603impl<D: NonFungibleData>
604 InProgressResourceBuilder<NonFungibleResourceType<StringNonFungibleLocalId, D>>
605{
606 /// Creates the non-fungible resource, and mints an individual non-fungible for each key/data
607 /// pair provided.
608 ///
609 /// ### Example
610 /// ```
611 /// use scrypto_test::prelude::*;
612 ///
613 /// let mut env = TestEnvironment::new();
614 ///
615 /// #[derive(ScryptoSbor, NonFungibleData)]
616 /// struct NFData {
617 /// pub name: String,
618 /// #[mutable]
619 /// pub flag: bool,
620 /// }
621 ///
622 /// let bucket: NonFungibleBucket = ResourceBuilder::new_string_non_fungible::<NFData>(OwnerRole::None)
623 /// .mint_initial_supply(
624 /// [
625 /// ("One".try_into().unwrap(), NFData { name: "NF One".to_owned(), flag: true }),
626 /// ("Two".try_into().unwrap(), NFData { name: "NF Two".to_owned(), flag: true }),
627 /// ],
628 /// &mut env,
629 /// )?;
630 ///
631 /// # // Make it so `?` works correctly
632 /// # Ok::<(), RuntimeError>(())
633 /// ```
634 pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
635 mut self,
636 entries: impl IntoIterator<Item = (StringNonFungibleLocalId, D)>,
637 env: &mut Y,
638 ) -> Result<NonFungibleBucket, E> {
639 let non_fungible_schema =
640 NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
641
642 let metadata = self.metadata_config.take().unwrap_or_default();
643
644 let bytes = env.call_function(
645 RESOURCE_PACKAGE,
646 NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
647 NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_WITH_INITIAL_SUPPLY_IDENT,
648 scrypto_encode(&NonFungibleResourceManagerCreateWithInitialSupplyInput {
649 owner_role: self.owner_role,
650 track_total_supply: true,
651 id_type: StringNonFungibleLocalId::id_type(),
652 non_fungible_schema,
653 resource_roles: self.resource_roles,
654 metadata,
655 entries: map_entries(entries),
656 address_reservation: self.address_reservation,
657 })
658 .unwrap(),
659 )?;
660 Ok(
661 scrypto_decode::<(ResourceAddress, NonFungibleBucket)>(&bytes)
662 .unwrap()
663 .1,
664 )
665 }
666}
667
668impl<D: NonFungibleData>
669 InProgressResourceBuilder<NonFungibleResourceType<IntegerNonFungibleLocalId, D>>
670{
671 /// Creates the non-fungible resource, and mints an individual non-fungible for each key/data
672 /// pair provided.
673 ///
674 /// ### Example
675 /// ```
676 /// use scrypto_test::prelude::*;
677 ///
678 /// let mut env = TestEnvironment::new();
679 ///
680 /// #[derive(ScryptoSbor, NonFungibleData)]
681 /// struct NFData {
682 /// pub name: String,
683 /// #[mutable]
684 /// pub flag: bool,
685 /// }
686 ///
687 /// let bucket: NonFungibleBucket = ResourceBuilder::new_integer_non_fungible(OwnerRole::None)
688 /// .mint_initial_supply(
689 /// [
690 /// (1u64.into(), NFData { name: "NF One".to_owned(), flag: true }),
691 /// (2u64.into(), NFData { name: "NF Two".to_owned(), flag: true }),
692 /// ],
693 /// &mut env,
694 /// )?;
695 ///
696 /// # Ok::<(), RuntimeError>(())
697 /// ```
698 pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
699 mut self,
700 entries: impl IntoIterator<Item = (IntegerNonFungibleLocalId, D)>,
701 env: &mut Y,
702 ) -> Result<NonFungibleBucket, E> {
703 let non_fungible_schema =
704 NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
705
706 let metadata = self.metadata_config.take().unwrap_or_default();
707
708 let bytes = env.call_function(
709 RESOURCE_PACKAGE,
710 NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
711 NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_WITH_INITIAL_SUPPLY_IDENT,
712 scrypto_encode(&NonFungibleResourceManagerCreateWithInitialSupplyInput {
713 owner_role: self.owner_role,
714 track_total_supply: true,
715 id_type: IntegerNonFungibleLocalId::id_type(),
716 non_fungible_schema,
717 resource_roles: self.resource_roles,
718 metadata,
719 entries: map_entries(entries),
720 address_reservation: self.address_reservation,
721 })
722 .unwrap(),
723 )?;
724 Ok(
725 scrypto_decode::<(ResourceAddress, NonFungibleBucket)>(&bytes)
726 .unwrap()
727 .1,
728 )
729 }
730}
731
732impl<D: NonFungibleData>
733 InProgressResourceBuilder<NonFungibleResourceType<BytesNonFungibleLocalId, D>>
734{
735 /// Creates the non-fungible resource, and mints an individual non-fungible for each key/data
736 /// pair provided.
737 ///
738 /// ### Example
739 /// ```
740 /// use scrypto_test::prelude::*;
741 ///
742 /// let mut env = TestEnvironment::new();
743 ///
744 /// #[derive(ScryptoSbor, NonFungibleData)]
745 /// struct NFData {
746 /// pub name: String,
747 /// #[mutable]
748 /// pub flag: bool,
749 /// }
750 ///
751 /// let bucket: NonFungibleBucket = ResourceBuilder::new_bytes_non_fungible::<NFData>(OwnerRole::None)
752 /// .mint_initial_supply(
753 /// [
754 /// (vec![1u8].try_into().unwrap(), NFData { name: "NF One".to_owned(), flag: true }),
755 /// (vec![2u8].try_into().unwrap(), NFData { name: "NF Two".to_owned(), flag: true }),
756 /// ],
757 /// &mut env,
758 /// )?;
759 /// # Ok::<(), RuntimeError>(())
760 /// ```
761 pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
762 mut self,
763 entries: impl IntoIterator<Item = (BytesNonFungibleLocalId, D)>,
764 env: &mut Y,
765 ) -> Result<NonFungibleBucket, E> {
766 let non_fungible_schema =
767 NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
768
769 let metadata = self.metadata_config.take().unwrap_or_default();
770
771 let bytes = env.call_function(
772 RESOURCE_PACKAGE,
773 NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
774 NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_WITH_INITIAL_SUPPLY_IDENT,
775 scrypto_encode(&NonFungibleResourceManagerCreateWithInitialSupplyInput {
776 owner_role: self.owner_role,
777 id_type: BytesNonFungibleLocalId::id_type(),
778 track_total_supply: true,
779 non_fungible_schema,
780 resource_roles: self.resource_roles,
781 metadata,
782 entries: map_entries(entries),
783 address_reservation: self.address_reservation,
784 })
785 .unwrap(),
786 )?;
787 Ok(
788 scrypto_decode::<(ResourceAddress, NonFungibleBucket)>(&bytes)
789 .unwrap()
790 .1,
791 )
792 }
793}
794
795impl<D: NonFungibleData>
796 InProgressResourceBuilder<NonFungibleResourceType<RUIDNonFungibleLocalId, D>>
797{
798 /// Creates the RUID non-fungible resource, and mints an individual non-fungible for each piece
799 /// of data provided.
800 ///
801 /// The system automatically generates a new RUID `NonFungibleLocalId` for each non-fungible,
802 /// and assigns the given data to each.
803 ///
804 /// ### Example
805 /// ```
806 /// use scrypto_test::prelude::*;
807 /// # let mut env = TestEnvironment::new();
808 ///
809 /// #[derive(ScryptoSbor, NonFungibleData)]
810 /// struct NFData {
811 /// pub name: String,
812 /// #[mutable]
813 /// pub flag: bool,
814 /// }
815 ///
816 /// let bucket: NonFungibleBucket = ResourceBuilder::new_ruid_non_fungible::<NFData>(OwnerRole::None)
817 /// .mint_initial_supply(
818 /// [
819 /// (NFData { name: "NF One".to_owned(), flag: true }),
820 /// (NFData { name: "NF Two".to_owned(), flag: true }),
821 /// ],
822 /// &mut env,
823 /// )?;
824 /// # // Make it so `?` works correctly
825 /// # Ok::<(), RuntimeError>(())
826 /// ```
827 pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
828 mut self,
829 entries: impl IntoIterator<Item = D>,
830 env: &mut Y,
831 ) -> Result<NonFungibleBucket, E> {
832 let non_fungible_schema =
833 NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
834
835 let metadata = self.metadata_config.take().unwrap_or_default();
836
837 let bytes = env.call_function(
838 RESOURCE_PACKAGE,
839 NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
840 NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_RUID_WITH_INITIAL_SUPPLY_IDENT,
841 scrypto_encode(
842 &NonFungibleResourceManagerCreateRuidWithInitialSupplyInput {
843 owner_role: self.owner_role,
844 non_fungible_schema,
845 track_total_supply: true,
846 resource_roles: self.resource_roles,
847 metadata,
848 entries: entries
849 .into_iter()
850 .map(|data| {
851 let value: ScryptoValue =
852 scrypto_decode(&scrypto_encode(&data).unwrap()).unwrap();
853 (value,)
854 })
855 .collect(),
856 address_reservation: self.address_reservation,
857 },
858 )
859 .unwrap(),
860 )?;
861 Ok(
862 scrypto_decode::<(ResourceAddress, NonFungibleBucket)>(&bytes)
863 .unwrap()
864 .1,
865 )
866 }
867}
868
869// /////////////////////////////////
870// PRIVATE TRAIT IMPLEMENTATIONS
871// These don't need good rust docs
872// /////////////////////////////////
873
874fn map_entries<T: IntoIterator<Item = (Y, V)>, V: NonFungibleData, Y: IsNonFungibleLocalId>(
875 entries: T,
876) -> IndexMap<NonFungibleLocalId, (ScryptoValue,)> {
877 entries
878 .into_iter()
879 .map(|(id, data)| {
880 let value: ScryptoValue = scrypto_decode(&scrypto_encode(&data).unwrap()).unwrap();
881 (id.into(), (value,))
882 })
883 .collect()
884}
885
886impl<T: AnyResourceType> private::CanSetMetadata for InProgressResourceBuilder<T> {
887 type OutputBuilder = Self;
888
889 fn set_metadata(mut self, metadata: ModuleConfig<MetadataInit>) -> Self::OutputBuilder {
890 self.metadata_config = Some(metadata);
891 self
892 }
893}
894
895impl<T: AnyResourceType> private::CanSetAddressReservation for InProgressResourceBuilder<T> {
896 type OutputBuilder = Self;
897
898 fn set_address(mut self, address_reservation: GlobalAddressReservation) -> Self::OutputBuilder {
899 self.address_reservation = Some(address_reservation);
900 self
901 }
902}
903
904impl private::CanCreateWithNoSupply for InProgressResourceBuilder<FungibleResourceType> {
905 fn into_create_with_no_supply_invocation(self) -> private::CreateWithNoSupply {
906 private::CreateWithNoSupply::Fungible {
907 owner_role: self.owner_role,
908 divisibility: self.resource_type.divisibility,
909 resource_roles: self.resource_roles,
910 metadata: self.metadata_config,
911 address_reservation: self.address_reservation,
912 }
913 }
914}
915
916impl<Y: IsNonFungibleLocalId, D: NonFungibleData> private::CanCreateWithNoSupply
917 for InProgressResourceBuilder<NonFungibleResourceType<Y, D>>
918{
919 fn into_create_with_no_supply_invocation(self) -> private::CreateWithNoSupply {
920 let non_fungible_schema =
921 NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
922
923 private::CreateWithNoSupply::NonFungible {
924 owner_role: self.owner_role,
925 id_type: Y::id_type(),
926 non_fungible_schema,
927 resource_roles: self.resource_roles,
928 metadata: self.metadata_config,
929 address_reservation: self.address_reservation,
930 }
931 }
932}
933
934/// This file was experiencing combinatorial explosion - as part of the clean-up, we've used private
935/// traits to keep things simple.
936///
937/// Each public method has essentially one implementation, and one Rust doc (where there weren't
938/// clashes due to Rust trait issues - eg with the `mint_initial_supply` methods).
939///
940/// Internally, the various builders implement these private traits, and then automatically
941/// implement the "nice" public traits. The methods defined in the private traits are less nice, and
942/// so are hidden in order to not pollute the user facing API.
943///
944/// As users will nearly always use `scrypto_test::prelude::*`, as long as we make sure that the
945/// public traits are exported, this will be seamless for the user.
946///
947/// See https://stackoverflow.com/a/53207767 for more information on this.
948mod private {
949 use super::*;
950 use radix_common::types::NonFungibleGlobalId;
951
952 pub trait CanSetMetadata: Sized {
953 type OutputBuilder;
954
955 fn set_metadata(self, metadata: ModuleConfig<MetadataInit>) -> Self::OutputBuilder;
956 }
957
958 pub trait CanSetAddressReservation: Sized {
959 type OutputBuilder;
960
961 fn set_address(self, address_reservation: GlobalAddressReservation) -> Self::OutputBuilder;
962 }
963
964 pub trait CanAddOwner: Sized {
965 type OutputBuilder;
966
967 fn set_owner(self, owner_badge: NonFungibleGlobalId) -> Self::OutputBuilder;
968 }
969
970 pub trait CanCreateWithNoSupply: Sized {
971 fn into_create_with_no_supply_invocation(self) -> CreateWithNoSupply;
972 }
973
974 #[allow(clippy::large_enum_variant)]
975 pub enum CreateWithNoSupply {
976 Fungible {
977 owner_role: OwnerRole,
978 divisibility: u8,
979 resource_roles: FungibleResourceRoles,
980 metadata: Option<ModuleConfig<MetadataInit>>,
981 address_reservation: Option<GlobalAddressReservation>,
982 },
983 NonFungible {
984 owner_role: OwnerRole,
985 id_type: NonFungibleIdType,
986 non_fungible_schema: NonFungibleDataSchema,
987 resource_roles: NonFungibleResourceRoles,
988 metadata: Option<ModuleConfig<MetadataInit>>,
989 address_reservation: Option<GlobalAddressReservation>,
990 },
991 }
992}