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}