use crate::prelude::*;
use radix_common::math::Decimal;
use radix_common::traits::NonFungibleData;
use radix_engine_interface::object_modules::metadata::MetadataInit;
use radix_engine_interface::object_modules::role_assignment::RoleDefinition;
use radix_engine_interface::object_modules::ModuleConfig;
use radix_engine_interface::prelude::SystemApi;
use sbor::rust::marker::PhantomData;
pub const DIVISIBILITY_NONE: u8 = 0;
pub const DIVISIBILITY_MAXIMUM: u8 = 18;
pub struct ResourceBuilder;
impl ResourceBuilder {
pub fn new_fungible(owner_role: OwnerRole) -> InProgressResourceBuilder<FungibleResourceType> {
InProgressResourceBuilder::new(owner_role)
}
pub fn new_string_non_fungible<D: NonFungibleData>(
owner_role: OwnerRole,
) -> InProgressResourceBuilder<NonFungibleResourceType<StringNonFungibleLocalId, D>> {
InProgressResourceBuilder::new(owner_role)
}
pub fn new_integer_non_fungible<D: NonFungibleData>(
owner_role: OwnerRole,
) -> InProgressResourceBuilder<NonFungibleResourceType<IntegerNonFungibleLocalId, D>> {
InProgressResourceBuilder::new(owner_role)
}
pub fn new_bytes_non_fungible<D: NonFungibleData>(
owner_role: OwnerRole,
) -> InProgressResourceBuilder<NonFungibleResourceType<BytesNonFungibleLocalId, D>> {
InProgressResourceBuilder::new(owner_role)
}
pub fn new_ruid_non_fungible<D: NonFungibleData>(
owner_role: OwnerRole,
) -> InProgressResourceBuilder<NonFungibleResourceType<RUIDNonFungibleLocalId, D>> {
InProgressResourceBuilder::new(owner_role)
}
}
#[must_use]
pub struct InProgressResourceBuilder<T: AnyResourceType> {
owner_role: OwnerRole,
resource_type: T,
resource_roles: T::ResourceRoles,
metadata_config: Option<ModuleConfig<MetadataInit>>,
address_reservation: Option<GlobalAddressReservation>,
}
impl<T: AnyResourceType> InProgressResourceBuilder<T> {
fn new(owner_role: OwnerRole) -> Self {
Self {
owner_role,
resource_type: T::default(),
metadata_config: None,
address_reservation: None,
resource_roles: T::ResourceRoles::default(),
}
}
}
pub trait AnyResourceType: Default {
type ResourceRoles: Default;
}
pub struct FungibleResourceType {
divisibility: u8,
}
impl AnyResourceType for FungibleResourceType {
type ResourceRoles = FungibleResourceRoles;
}
impl Default for FungibleResourceType {
fn default() -> Self {
Self {
divisibility: DIVISIBILITY_MAXIMUM,
}
}
}
pub struct NonFungibleResourceType<T: IsNonFungibleLocalId, D: NonFungibleData>(
PhantomData<T>,
PhantomData<D>,
);
impl<T: IsNonFungibleLocalId, D: NonFungibleData> AnyResourceType
for NonFungibleResourceType<T, D>
{
type ResourceRoles = NonFungibleResourceRoles;
}
impl<T: IsNonFungibleLocalId, D: NonFungibleData> Default for NonFungibleResourceType<T, D> {
fn default() -> Self {
Self(PhantomData, PhantomData)
}
}
pub trait UpdateMetadataBuilder: private::CanSetMetadata {
fn metadata(self, metadata: ModuleConfig<MetadataInit>) -> Self::OutputBuilder {
self.set_metadata(metadata)
}
}
impl<B: private::CanSetMetadata> UpdateMetadataBuilder for B {}
pub trait SetAddressReservationBuilder: private::CanSetAddressReservation {
fn with_address(self, reservation: GlobalAddressReservation) -> Self::OutputBuilder {
self.set_address(reservation)
}
}
impl<B: private::CanSetAddressReservation> SetAddressReservationBuilder for B {}
pub trait UpdateAuthBuilder {
fn mint_roles(self, mint_roles: Option<MintRoles<RoleDefinition>>) -> Self;
fn burn_roles(self, burn_roles: Option<BurnRoles<RoleDefinition>>) -> Self;
fn recall_roles(self, recall_roles: Option<RecallRoles<RoleDefinition>>) -> Self;
fn freeze_roles(self, freeze_roles: Option<FreezeRoles<RoleDefinition>>) -> Self;
fn withdraw_roles(self, withdraw_roles: Option<WithdrawRoles<RoleDefinition>>) -> Self;
fn deposit_roles(self, deposit_roles: Option<DepositRoles<RoleDefinition>>) -> Self;
}
impl UpdateAuthBuilder for InProgressResourceBuilder<FungibleResourceType> {
fn mint_roles(mut self, mint_roles: Option<MintRoles<RoleDefinition>>) -> Self {
self.resource_roles.mint_roles = mint_roles;
self
}
fn burn_roles(mut self, burn_roles: Option<BurnRoles<RoleDefinition>>) -> Self {
self.resource_roles.burn_roles = burn_roles;
self
}
fn recall_roles(mut self, recall_roles: Option<RecallRoles<RoleDefinition>>) -> Self {
self.resource_roles.recall_roles = recall_roles;
self
}
fn freeze_roles(mut self, freeze_roles: Option<FreezeRoles<RoleDefinition>>) -> Self {
self.resource_roles.freeze_roles = freeze_roles;
self
}
fn withdraw_roles(mut self, withdraw_roles: Option<WithdrawRoles<RoleDefinition>>) -> Self {
self.resource_roles.withdraw_roles = withdraw_roles;
self
}
fn deposit_roles(mut self, deposit_roles: Option<DepositRoles<RoleDefinition>>) -> Self {
self.resource_roles.deposit_roles = deposit_roles;
self
}
}
impl<T: IsNonFungibleLocalId, D: NonFungibleData> UpdateAuthBuilder
for InProgressResourceBuilder<NonFungibleResourceType<T, D>>
{
fn mint_roles(mut self, mint_roles: Option<MintRoles<RoleDefinition>>) -> Self {
self.resource_roles.mint_roles = mint_roles;
self
}
fn burn_roles(mut self, burn_roles: Option<BurnRoles<RoleDefinition>>) -> Self {
self.resource_roles.burn_roles = burn_roles;
self
}
fn recall_roles(mut self, recall_roles: Option<RecallRoles<RoleDefinition>>) -> Self {
self.resource_roles.recall_roles = recall_roles;
self
}
fn freeze_roles(mut self, freeze_roles: Option<FreezeRoles<RoleDefinition>>) -> Self {
self.resource_roles.freeze_roles = freeze_roles;
self
}
fn withdraw_roles(mut self, withdraw_roles: Option<WithdrawRoles<RoleDefinition>>) -> Self {
self.resource_roles.withdraw_roles = withdraw_roles;
self
}
fn deposit_roles(mut self, deposit_roles: Option<DepositRoles<RoleDefinition>>) -> Self {
self.resource_roles.deposit_roles = deposit_roles;
self
}
}
impl<T: IsNonFungibleLocalId, D: NonFungibleData>
InProgressResourceBuilder<NonFungibleResourceType<T, D>>
{
pub fn non_fungible_data_update_roles(
mut self,
non_fungible_data_update_roles: Option<NonFungibleDataUpdateRoles<RoleDefinition>>,
) -> Self {
self.resource_roles.non_fungible_data_update_roles = non_fungible_data_update_roles;
self
}
}
pub trait SetOwnerBuilder: private::CanAddOwner {
fn owner_non_fungible_badge(self, owner_badge: NonFungibleGlobalId) -> Self::OutputBuilder {
self.set_owner(owner_badge)
}
}
impl<B: private::CanAddOwner> SetOwnerBuilder for B {}
pub trait CreateWithNoSupplyBuilder: private::CanCreateWithNoSupply {
fn create_with_no_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
self,
env: &mut Y,
) -> Result<ResourceManager, E> {
match self.into_create_with_no_supply_invocation() {
private::CreateWithNoSupply::Fungible {
owner_role,
divisibility,
resource_roles,
metadata,
address_reservation,
} => {
let metadata = metadata.unwrap_or_else(Default::default);
let bytes = env.call_function(
RESOURCE_PACKAGE,
FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
FUNGIBLE_RESOURCE_MANAGER_CREATE_IDENT,
scrypto_encode(&FungibleResourceManagerCreateInput {
owner_role,
divisibility,
track_total_supply: true,
metadata,
resource_roles,
address_reservation,
})
.unwrap(),
)?;
Ok(scrypto_decode(&bytes).unwrap())
}
private::CreateWithNoSupply::NonFungible {
owner_role,
id_type,
non_fungible_schema,
resource_roles,
metadata,
address_reservation,
} => {
let metadata = metadata.unwrap_or_else(Default::default);
let bytes = env.call_function(
RESOURCE_PACKAGE,
NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_IDENT,
scrypto_encode(&NonFungibleResourceManagerCreateInput {
owner_role,
id_type,
track_total_supply: true,
non_fungible_schema,
resource_roles,
metadata,
address_reservation,
})
.unwrap(),
)?;
Ok(scrypto_decode(&bytes).unwrap())
}
}
}
}
impl<B: private::CanCreateWithNoSupply> CreateWithNoSupplyBuilder for B {}
impl InProgressResourceBuilder<FungibleResourceType> {
pub fn divisibility(mut self, divisibility: u8) -> Self {
assert!(divisibility <= 18);
self.resource_type = FungibleResourceType { divisibility };
self
}
}
impl InProgressResourceBuilder<FungibleResourceType> {
pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
mut self,
amount: impl Into<Decimal>,
env: &mut Y,
) -> Result<FungibleBucket, E> {
let metadata = self.metadata_config.take().unwrap_or_default();
let bytes = env.call_function(
RESOURCE_PACKAGE,
FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
FUNGIBLE_RESOURCE_MANAGER_CREATE_WITH_INITIAL_SUPPLY_IDENT,
scrypto_encode(&FungibleResourceManagerCreateWithInitialSupplyInput {
owner_role: self.owner_role,
track_total_supply: true,
divisibility: self.resource_type.divisibility,
resource_roles: self.resource_roles,
metadata,
initial_supply: amount.into(),
address_reservation: self.address_reservation,
})
.unwrap(),
)?;
let result: (ResourceAddress, FungibleBucket) = scrypto_decode(&bytes).unwrap();
Ok(result.1)
}
}
impl<D: NonFungibleData>
InProgressResourceBuilder<NonFungibleResourceType<StringNonFungibleLocalId, D>>
{
pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
mut self,
entries: impl IntoIterator<Item = (StringNonFungibleLocalId, D)>,
env: &mut Y,
) -> Result<NonFungibleBucket, E> {
let non_fungible_schema =
NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
let metadata = self.metadata_config.take().unwrap_or_default();
let bytes = env.call_function(
RESOURCE_PACKAGE,
NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_WITH_INITIAL_SUPPLY_IDENT,
scrypto_encode(&NonFungibleResourceManagerCreateWithInitialSupplyInput {
owner_role: self.owner_role,
track_total_supply: true,
id_type: StringNonFungibleLocalId::id_type(),
non_fungible_schema,
resource_roles: self.resource_roles,
metadata,
entries: map_entries(entries),
address_reservation: self.address_reservation,
})
.unwrap(),
)?;
Ok(
scrypto_decode::<(ResourceAddress, NonFungibleBucket)>(&bytes)
.unwrap()
.1,
)
}
}
impl<D: NonFungibleData>
InProgressResourceBuilder<NonFungibleResourceType<IntegerNonFungibleLocalId, D>>
{
pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
mut self,
entries: impl IntoIterator<Item = (IntegerNonFungibleLocalId, D)>,
env: &mut Y,
) -> Result<NonFungibleBucket, E> {
let non_fungible_schema =
NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
let metadata = self.metadata_config.take().unwrap_or_default();
let bytes = env.call_function(
RESOURCE_PACKAGE,
NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_WITH_INITIAL_SUPPLY_IDENT,
scrypto_encode(&NonFungibleResourceManagerCreateWithInitialSupplyInput {
owner_role: self.owner_role,
track_total_supply: true,
id_type: IntegerNonFungibleLocalId::id_type(),
non_fungible_schema,
resource_roles: self.resource_roles,
metadata,
entries: map_entries(entries),
address_reservation: self.address_reservation,
})
.unwrap(),
)?;
Ok(
scrypto_decode::<(ResourceAddress, NonFungibleBucket)>(&bytes)
.unwrap()
.1,
)
}
}
impl<D: NonFungibleData>
InProgressResourceBuilder<NonFungibleResourceType<BytesNonFungibleLocalId, D>>
{
pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
mut self,
entries: impl IntoIterator<Item = (BytesNonFungibleLocalId, D)>,
env: &mut Y,
) -> Result<NonFungibleBucket, E> {
let non_fungible_schema =
NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
let metadata = self.metadata_config.take().unwrap_or_default();
let bytes = env.call_function(
RESOURCE_PACKAGE,
NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_WITH_INITIAL_SUPPLY_IDENT,
scrypto_encode(&NonFungibleResourceManagerCreateWithInitialSupplyInput {
owner_role: self.owner_role,
id_type: BytesNonFungibleLocalId::id_type(),
track_total_supply: true,
non_fungible_schema,
resource_roles: self.resource_roles,
metadata,
entries: map_entries(entries),
address_reservation: self.address_reservation,
})
.unwrap(),
)?;
Ok(
scrypto_decode::<(ResourceAddress, NonFungibleBucket)>(&bytes)
.unwrap()
.1,
)
}
}
impl<D: NonFungibleData>
InProgressResourceBuilder<NonFungibleResourceType<RUIDNonFungibleLocalId, D>>
{
pub fn mint_initial_supply<Y: SystemApi<E>, E: SystemApiError>(
mut self,
entries: impl IntoIterator<Item = D>,
env: &mut Y,
) -> Result<NonFungibleBucket, E> {
let non_fungible_schema =
NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
let metadata = self.metadata_config.take().unwrap_or_default();
let bytes = env.call_function(
RESOURCE_PACKAGE,
NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_RUID_WITH_INITIAL_SUPPLY_IDENT,
scrypto_encode(
&NonFungibleResourceManagerCreateRuidWithInitialSupplyInput {
owner_role: self.owner_role,
non_fungible_schema,
track_total_supply: true,
resource_roles: self.resource_roles,
metadata,
entries: entries
.into_iter()
.map(|data| {
let value: ScryptoValue =
scrypto_decode(&scrypto_encode(&data).unwrap()).unwrap();
(value,)
})
.collect(),
address_reservation: self.address_reservation,
},
)
.unwrap(),
)?;
Ok(
scrypto_decode::<(ResourceAddress, NonFungibleBucket)>(&bytes)
.unwrap()
.1,
)
}
}
fn map_entries<T: IntoIterator<Item = (Y, V)>, V: NonFungibleData, Y: IsNonFungibleLocalId>(
entries: T,
) -> IndexMap<NonFungibleLocalId, (ScryptoValue,)> {
entries
.into_iter()
.map(|(id, data)| {
let value: ScryptoValue = scrypto_decode(&scrypto_encode(&data).unwrap()).unwrap();
(id.into(), (value,))
})
.collect()
}
impl<T: AnyResourceType> private::CanSetMetadata for InProgressResourceBuilder<T> {
type OutputBuilder = Self;
fn set_metadata(mut self, metadata: ModuleConfig<MetadataInit>) -> Self::OutputBuilder {
self.metadata_config = Some(metadata);
self
}
}
impl<T: AnyResourceType> private::CanSetAddressReservation for InProgressResourceBuilder<T> {
type OutputBuilder = Self;
fn set_address(mut self, address_reservation: GlobalAddressReservation) -> Self::OutputBuilder {
self.address_reservation = Some(address_reservation);
self
}
}
impl private::CanCreateWithNoSupply for InProgressResourceBuilder<FungibleResourceType> {
fn into_create_with_no_supply_invocation(self) -> private::CreateWithNoSupply {
private::CreateWithNoSupply::Fungible {
owner_role: self.owner_role,
divisibility: self.resource_type.divisibility,
resource_roles: self.resource_roles,
metadata: self.metadata_config,
address_reservation: self.address_reservation,
}
}
}
impl<Y: IsNonFungibleLocalId, D: NonFungibleData> private::CanCreateWithNoSupply
for InProgressResourceBuilder<NonFungibleResourceType<Y, D>>
{
fn into_create_with_no_supply_invocation(self) -> private::CreateWithNoSupply {
let non_fungible_schema =
NonFungibleDataSchema::new_local_without_self_package_replacement::<D>();
private::CreateWithNoSupply::NonFungible {
owner_role: self.owner_role,
id_type: Y::id_type(),
non_fungible_schema,
resource_roles: self.resource_roles,
metadata: self.metadata_config,
address_reservation: self.address_reservation,
}
}
}
mod private {
use super::*;
use radix_common::types::NonFungibleGlobalId;
pub trait CanSetMetadata: Sized {
type OutputBuilder;
fn set_metadata(self, metadata: ModuleConfig<MetadataInit>) -> Self::OutputBuilder;
}
pub trait CanSetAddressReservation: Sized {
type OutputBuilder;
fn set_address(self, address_reservation: GlobalAddressReservation) -> Self::OutputBuilder;
}
pub trait CanAddOwner: Sized {
type OutputBuilder;
fn set_owner(self, owner_badge: NonFungibleGlobalId) -> Self::OutputBuilder;
}
pub trait CanCreateWithNoSupply: Sized {
fn into_create_with_no_supply_invocation(self) -> CreateWithNoSupply;
}
#[allow(clippy::large_enum_variant)]
pub enum CreateWithNoSupply {
Fungible {
owner_role: OwnerRole,
divisibility: u8,
resource_roles: FungibleResourceRoles,
metadata: Option<ModuleConfig<MetadataInit>>,
address_reservation: Option<GlobalAddressReservation>,
},
NonFungible {
owner_role: OwnerRole,
id_type: NonFungibleIdType,
non_fungible_schema: NonFungibleDataSchema,
resource_roles: NonFungibleResourceRoles,
metadata: Option<ModuleConfig<MetadataInit>>,
address_reservation: Option<GlobalAddressReservation>,
},
}
}