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