03 Resources
3.3 Resource Behaviors
The previous section gave us the basic structure of how resources can be defined and created. There’s more to the
ResourceBuilder which makes resource creation with Scrypto incredibly powerful and that is with “resource behaviors”. When creating a new resource with the ResourceBuilder, we can also attribute characteristics (otherwise known as resource behaviors) to our resources. For example you can specify behaviors to mark a resource to “mintable”, “burnable”, or even restrict its withdrawal; making them “Soulbound” tokens.//Syntax and Structure of Resource Behaviors
Resource behaviors, make resources powerful, but with great power, comes with great responsibility. Applying a resource behavior has some nuance in how it’s set up. So let’s bring up the snippet example we had in the previous section.
let special_tokens: ResourceManager = ResourceBuilder::new_fungible(OwnerRole::None) .divisibility(DIVISIBILITY_NONE) .metadata(metadata! ( init { "name" => "Special Token", locked; "symbol" => "ST", locked; } )) .mint_roles(mint_roles!( minter => rule!(require(admin_badge)); minter_updater => rule!(deny_all); )) .mint_initial_supply(1000);Let’s say we want our resource to be “mintable”, to do so we’d call on
With every resource behavior, you’ll notice that the methods to call for them will end in some kind of
Each resource behavior will have two corresponding roles: one that is designated to perform the behavior and the other to update the preceding role. These roles then need to be configured, notice both on Line 10 and Line 11 that they are mapped to a
This may be confusing to understand at first as there’s many concepts we’ve yet to introduce that’s layered on top of each other. For now, don’t worry too much about their specifics. This section is not meant to dive into those specifics but more so to familiarize the general structure of each resource behaviors as we will build on top of these concepts as we progress.
mint_roles method from the ResourceBuilder. You’ll notice that the arguments we need to pass for mint_roles is a bit unique from other arguments we’ve seen. This is because we’re passing a mint_roles! macro. And all you need to know about macros mostly is that it allows us to convenient create constructs. So what does that really mean?With every resource behavior, you’ll notice that the methods to call for them will end in some kind of
_roles. This is because resource behaviors require you to specify the authorities allowed to perform those behaviors. For example, if you've set your resource to be "mintable", you must specify the "roles" of who can perform the resource minting. Roles are a core concept within how authorization works with Scrypto. More of this is covered in detail in Chapter 4: Introduction to Auth. But to give you enough working context to understand this section better, roles are essentially labels which describes their responsible action(s). For example, notice on Line 10 that we need a minter. On Line 11, we also have a minter_updater. The minter is a role that is allowed to mint new supply of the resource, whereas the minter_updater is the role that can update the conditions to assume the minter.Each resource behavior will have two corresponding roles: one that is designated to perform the behavior and the other to update the preceding role. These roles then need to be configured, notice both on Line 10 and Line 11 that they are mapped to a
rule!. These rules are conditions that must be met to assume the role and be allowed to perform the corresponding resource behavior. This may be confusing to understand at first as there’s many concepts we’ve yet to introduce that’s layered on top of each other. For now, don’t worry too much about their specifics. This section is not meant to dive into those specifics but more so to familiarize the general structure of each resource behaviors as we will build on top of these concepts as we progress.
//List of Resource Behaviors
Within the
ResourceBuilder, along with some basic configuration, the ResourceBuilder also provides resource behavior configuration as well as its role assignments. Method
Roles
Description
.metadata()
metadata_settermetadata_setter_updatermetadata_lockermetadata_locker_updater
.mint_roles()
minterminter_updater
.burn_roles()
burnerburner_updater
.withdraw_roles()
withdrawerwithdrawer_updater
.deposit_roles()
depositordepositor_updater
.non_fungible_data_update_roles()
non_fungible_data_updaternon_fungible_data_updater_updater
NonFungibleData to be updated. Only for non-fungible resources.
.recall_roles()
recallerrecaller_updater
.freeze_roles()
freezerfreezer_updater
Some resource behaviors may create some confusion, so let’s go over each one to describe their general function.
//Resource Metadata
While the resource metadata isn’t technically a resource behavior, the ability to update the resource metadata is.
let special_token = ResourceBuilder::new_fungible(OwnerRole::None) .metadata(metadata! ( roles { metadata_setter => rule!(allow_all); metadata_setter_updater => rule!(deny_all); metadata_locker => rule!(allow_all); metadata_locker_updater => rule!(deny_all); }, init { "name" => "Special Token", locked; "symbol" => "ST", locked; "description" => "This description can be updated", updatable; } )) .create_with_no_initial_supply();When we first showed examples of having resource metadata, we simply showed its
Metadata roles are a little different from other assigned roles that resource behaviors have such that it actually has four different roles, but can be broken up to two groups of roles: the
The
init (initialization) where we define its initial metadata when a resource is created. Next to each metadata entries we had locked, meaning that particular entry can’t be re-configured. Now, we have a surprise, we’ve now introduced that the metadata! macro can also include roles where its assigned roles can be configured. Metadata roles are a little different from other assigned roles that resource behaviors have such that it actually has four different roles, but can be broken up to two groups of roles: the
metadata_setter and its _updater and the metadata_locker and its _updater. The
metadata_setter is a role that has permission to update a metadata entry so long as the entry is updatable as show on Line 12. It can also add additional entries after the a resource has been created. Whereas the metadata_locker is the role that has permission to lock a metadata from being re-configured as shown in Line 10 and Line 11. These nuances will be further explored in Chapter 4: Introduction to Auth, but the takeaway is that metadata can be re-configured after it’s been created and there are special handling that can be specified. Below summarizes the role assignments and their responsibility.Roles
Description
metadata_setter
Has permission to update metadata so long as the metadata entry is updatable and can add additional metadata entries.
metadata_setter_updater
Updates the AccessRule condition of the metadata_setter and itself.
metadata_locker
Has permission to lock a metadata entry. Preventing the metadata entry from being updated.
metadata_locker_updater
Updates the AccessRule condition of the metadata_locker and itself.//Making a Resource Mintable
To make a resource “mintable” means that we allow the creation of additional supply of that resource. We can do this by simply calling
.mint_roles() from the ResourceBuilder to our resource definition.let special_token = ResourceBuilder::new_fungible(OwnerRole::None) // We are making the resource "mintable" and assigning roles. .mint_roles(mint_roles!( minter => rule!(require(minter_badge)); minter_updater => rule!(deny_all); )) .create_with_no_initial_supply();Roles
Description
minter
Has permission to mint additional supply of the resource.
minter_updater
Updates the AccessRule condition of the minter and itself.//Making a Resource Burnable
Having a resource burnable indicates that an specified supply of this resource can essentially be destroyed. If all the supply of that resource is burnt, the
ResourceManager will still exist. Additionally, if that resource is mintable then more of that resource can be created. Similar to our previous example, to make our resource burnable, we call the .burn_roles() method when we create our resource.let special_token = ResourceBuilder::new_fungible(OwnerRole::None) // We are making the resource "burnable" and assigning roles .burn_roles(burn_roles!( burner => rule!(require(burner_badge)); burner_updater => rule!(deny_all); )) .create_with_no_initial_supply();If all the supply of the resource is burnt, that resource will still exist. It is just the resource’s supply that is fully depleted.
Roles
Description
burner
Has permission to burn supply of the resource.
burner_updater
Updates the AccessRule condition of the burner and itself.Burning Resource within Scrypto
There are multiple ways to burn a resource, but resources must first be deposited in a bucket, before they can be burnt. To burn a resource within Scrypto:
pub fn burn(&self, bucket: Bucket) { assert!(bucket.resource_address() == self.resource_address); bucket.burn();}Line 2: It’s a good habit to have that every bucket of resource we have, we should check against the
ResourceAddress that is passed into our component.Line 3: We can burn resources right from the bucket the resource was passed in by calling the .burn() method.//Restricting Resource Withdrawal
Resources that are restricted from being withdrawn are effectively locked in the vault that contains it. This makes the resource soulbound. Its most common use-case is to attach some form of identification or reputation to the account that owns that resource. Additionally, we have an option to apply an
AccessRule to this resource where we can specify what badge needs to be present for the resource to be withdrawn. Otherwise, there won’t be any way for the resource to be withdrawn and will effectively forever be stored in the vault that contains it.let special_token = ResourceBuilder::new_fungible(OwnerRole::None) // We are restricting the resource from being withdrawn .withdraw_roles(withdraw_roles!( withdrawer => rule!(require(withdrawer_badge)); withdrawer_updater => rule!(deny_all); )) .create_with_no_initial_supply();Roles
Description
withdrawer
Has permission to withdraw the resource from the corresponding vault.
withdrawer_updater
Updates the AccessRule condition of the withdrawer and itself.//Restricting Resource Deposit
Resources that are restricted from being deposited are commonly called transient resources. This forces a dangling resource to exist. Therefore, if the resource can’t be deposited into a vault, the resource must be burnt, else we will encounter a dangling resource error. Transient resources are most commonly used as a means to force a specified condition to happen within a transaction. If that condition is met, we can permit the resource to be burned. Alternatively, if we specify an authorization requirement, we can allow this resource to be deposited (when the specified badge is present) if a certain condition is met.
let special_token = ResourceBuilder::new_fungible(OwnerRole::None) // We are restricting this resource from being withdrawn .deposit_roles(deposit_roles!( depositor => rule!(require(depositor_badge)); depositor_updater => rule!(deny_all); )) .create_with_no_initial_supply();An example of this can be found in the basic flash loan example found here.
Roles
Description
depositor
Has permission to deposit supply of resource into the corresponding vault.
depositor_updater
Updates the AccessRule condition of the depositor and itself.//Allowing the Resource’s NonFungibleData to be Updatable
Each non-fungible resource has unique data that can be attributed to it. Therefore, we can allow this data to be changed. We can also specify that a badge must be present in order for the data to be changed if we apply an authorization requirement to this resource flag.
let special_token = ResourceBuilder::new_integer_non_fungible::<NFTData>(OwnerRole::None) // We are allowing the data of the non-fungible resource // to be updatable. .non_fungible_data_update_roles(non_fungible_data_update_roles!( non_fungible_data_updater => rule!(require(nft_data_updater_badge)); non_fungible_data_updater_updater => rule!(deny_all); )) .create_with_no_initial_supply();Roles
Description
non_fungible_updater
Has permission to update the
NonFungibleData of a non-fungible resource.
non_fungible_updater_updater
Updates the AccessRule condition of the non_fungible_updater and itself.//Making a Resource Recallable
Having a resource to be recallable allows us to send our tokens to anybody, but have the ability for us to retrieve it if we desire. The most common use case for this is to allow for “Rental NFTs”. We can create conditions in how long this resource can essentially be borrowed for.
let special_token = ResourceBuilder::new_integer_non_fungible::<NFTData>(OwnerRole::None) // We are adding the recallable flag here .recall_roles(recall_roles!( recaller => rule!(require(recaller_badge)); recaller_updater => rule!(deny_all); .create_with_no_initial_supply();Roles
Description
recaller
Has permission to take back the resource from a specified vault.
recaller_updater
Updates the AccessRule condition of the recaller and itself.//Making a Resource Freezable
A freezable resource can seem peculiar at first glance, but its use case typically lies on the regulatory side. For example, an issuer of a fiat currency stablecoin will typically need to comply with certain regulatory requirement which allows the issuer to freeze the stablecoin in the vault its contained in if the account that’s utilizing this stablecoin is misusing it.
let special_token = ResourceBuilder::new_fungible(OwnerRole::None) // We are making the resource frozen from the vault its contained in .freeze_roles(freeze_roles!( freezer => rule!(require(freezer_badge)); freezer_updater => rule!(deny_all); )) .create_with_no_initial_supply();Roles
Description
freezer
Has permission to take freeze a resource in the vault its located in. Preventing the resource from being withdrawn nor deposited into the corresponding vault.
freezer_updater
Updates the AccessRule condition of the freezer and itself.