03 Resources
3.4 The ResourceManager
Resources on Radix are identifiable by their
ResourceAddress. Each resource created on Radix, whether it has a supply or not; or whether it is a fungible or non-fungible type will have its own ResourceAddress. The ResourceAddress is the source of truth which allows us to distinguish between different resources that exist on the Radix network. But just simply having the ResourceAddress won’t tell us very much about the resource we’re interacting with. Although, by having a resource’s ResourceAddress, we can map it to its ResourceManager.Every resource created automatically has an associated ResourceManager. The ResourceManager, as the name implies, provides functionality to retrieve information about the resource (such as its metadata, resource type, supply, and more) and configure the resource even after it was created (depending on what was specified in the resource definition). If you’ve set a resource behavior in your resource definition such as “mintable” or “burnable”, you can use the ResourceManager to perform those actions.//Using the ResourceManager
Recall when we create a resource with an initial supply, it is immediately deposited into a bucket. This is useful for managing the resource’s supply, because it allows us to move a resource’s supply around, but also retrieve its
ResourceManager to reveal some information about the resource as well as other things. However, when we create resources, we don’t always need to have an initial supply with it. A resource will still exist on the network even if its supply hasn’t yet been minted. It exist where its ResourceAddress is generated along with its corresponding ResourceManager.let special_tokens: ResourceManager = ResourceBuilder::new_fungible(OwnerRole::None) .create_with_no_initial_supply();When we create a resource with no initial supply, a
ResourceManager is returned to us and we can store that ResourceManager in our component state so that the component can make use of it later such as minting its first supply based on some logic that is triggered.use scrypto::prelude::*;#[blueprint]mod SpecialToken { struct SpecialToken { special_token_manager: ResourceManager, } impl SpecialToken { pub fn instantiate() -> Global<SpecialToken> { let special_tokens: ResourceManager = ResourceBuilder::new_fungible(OwnerRole::None) .mint_roles(mint_roles!( minter => rule!(allow_all); minter_updater => rule!(deny_all); )) .create_with_no_initial_supply(); Self { special_token_manager: special_tokens, } .instantiate() .prepare_to_globalize(OwnerRole::None) .globalize() } }}Storing
ResourceManager in the struct provides many benefits. Having references to the ResourceManager for the component to retrieve will allow your component to perform a resource actions and inspect resources that are passed in. Let’s see how it’s used in action.//Resource Actions
Now that we have the
ResourceManager in the component state, the component can take a reference to it in a method call to perform a resource action such as a minting additional supplies of it (so long as the resource has the resource behavior set up).pub fn mint_special_tokens(&mut self, amount: Decimal) -> Bucket { self.special_token_manager.mint(amount)}In addition to retrieving the reference to the
ResourceManager to mint, it allows the component to perform other resource actions available by the ResourceManager. You can learn more about other resource actions the ResourceManager exposes from the documentation links provided in Reference Material.//Inspecting Resource Properties
We can use the
ResourceManager to inspect a resource’s properties to create some assurances to resources entering our dApp. Below are a few short examples of how we can do that.Resource Type
Perhaps we have an NFT Marketplace where users can list their NFTs for sale. However, we want to ensure that the resources that users are listing are indeed NFTs. We can use the
ResourceManager to assert that the resource being listed is of a non-fungible type.pub fn list_nft(&mut self, nft_bucket: Bucket, price: Decimal) { // Inspecting resource type with ResourceManager assert_eq!( nft_bucket.resource_manager().resource_type(), ResourceType::NonFungible {..}, "The resource you passed in must be a NonFungible!" ); .. }Line 5: The complete variant for
ResourceType::NonFungible requires we specify the NonFungibleLocalIdType of the NonFungible (as we’ll explain more when we go over non-fungible resource). Entering .. in ResourceType::NonFungible { .. } is a shorthand to indicate we’re not concerned about its NonFungibleLocalIdType, rather whether the resource is simply a NonFungible or not.ResourceAddress
We can also be more granular with ResourceManager to inspect resources being passed in and validate it against the ResourceAddress we expect to have.
pub fn check_resource(&mut self, special_token: Bucket) -> Bucket { assert_eq!( special_token.resource_address(), self.special_token_manager.address(), "The resource you passed in isn't a Special Token!" ); ..}Fungible Resource Divisibility
Working with maths in a distributed ledger environment can be tricky. Perhaps your dApp prefers to only handle fungible resources with smaller fractions. We can ensure that fungible resources passed in needs to have a divisibility we prefer to work with.
pub fn check_divisibility(&mut self, special_token: Bucket) { assert!( special_token.resource_manager().resource_type().divisibility().unwrap() <= 2, "The resource you passed in must have less than or equal to 2 divisibility!" ); ..}//Calculating Supply
The
ResourceManager can inspect the supply of the resource it is mapped to by calling .total_supply() on the reference. One way we can use this information is for applications where we may want to calculate ownership percentages.pub fn deposit_to_fund(&mut self, bucket_of_tokens: Bucket) -> Bucket { // Retrieving reference of ResourceManager to calculate // Total amount invested let fund_amount = self.token_resource_manager.total_supply(); // Retrieve amount invested by user let amount_invested = bucket_of_tokens.amount(); // Calculate ownership percentage let share_of_fund = amount_invested / fund_amount; // Mint tokens to represent percent ownership of fund let ownership_stake = self.token_resource_manager.mint(share_of_fund); return ownership_stake}Say we have an index fund type of dApp where investors deposit money into the fund. To track and claim their returns, they are returned an amount of token which represents the ownership they have of the fund. We can use the
ResourceManager to calculate the ownership an investor has based on the investment they provided.//Retrieving ResourceManager from ResourceAddress
For various reasons, sometimes it’s preferable to request a
ResourceAddress to be passed in rather than a supply of resource in a bucket. This makes it more challenging to retrieve the resource’s ResourceManager. You can simply retrieve the ResourceManager by calling its constructor with ResourceManager::from().pub fn check_resource_address(&mut self, resource_address: ResourceAddress) { let resource_manager: ResourceManager = ResourceManager::from_address(resource_address); .. }