- 04 Jul 2024
- 7 Minutes to read
- DarkLight
- PDF
Resource Behaviors
- Updated on 04 Jul 2024
- 7 Minutes to read
- DarkLight
- PDF
New resources created with the ResourceBuilder can also be attributed with special characteristics. For example, you can specify behaviors to mark a resource as mintable, burnable, or even restrict its ability to be withdrawn, making them “Soulbound” tokens. AccessRules can also be specified with resource behaviors, which allows you to determine the conditions need to be present before the behavior action can be performed. We can also determine the mutability of these authorization rules to have the ability to change the authorization rules in the future or lock its mutability to prevent its rules from being changed.
Resource Behaviors
Method | Description |
---|---|
| Specify the |
| Specify the |
| Specify the |
| Specify the |
| Specify the |
| Specify the |
| The divisibility is a number between 0 and 18 and it represents the number of decimal places that this resource can be split into. For example, if you set the divisibility to 0, people will only be able to send whole amounts of that resource. Default: 18 You can also use the constants |
Mintable
To make a resource mintable means that we allow the creation of additional supply of that resource. We can do this by simply adding .mint_roles() when we create our resource and map the AccessRule to each BurnRoles.
// Note our resource takes and OwnerRole argument this can be Fixed, Updatable, or None
let my_token = ResourceBuilder::new_fungible(OwnerRole::Fixed(rule!(require(access_rule))))
.metadata(metadata!{
init {
"name" => "My Token", locked;
"symbol" => "TKN", locked;
}
})
.mint_roles(mint_roles!{ // #1
minter => rule!(allow_all); // #2
minter_updater => rule!(deny_all); // #3
})
.create_with_no_initial_supply();
To make a resource mintable, you simply have to make a call to the
mint_roles()
method during the resource creation which requires that we map the AccessRule for two roles.Here we set the
AccessRule
for theminter
role,allow_all
makes minting public. We can of course and often will want to restrict minting to a particular badge here instead with something likeminter => rule!(require(badge_address));
instead.The
minter_updater
is how we control the mutability of theminter
role.deny_all
locks theminter
role, we can again also pass in a badge address to create an authority which can change theminter
role likeminter_updater => rule!(require(badge_address));
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 and map the AccessRule
to each BurnRoles.
// Note our resource takes and OwnerRole argument this can be Fixed, Updatable, or None
let my_token = ResourceBuilder::new_fungible(OwnerRole::None)
.metadata(metadata!{
init {
"name" => "My Token", locked;
"symbol" => "TKN", locked;
}
})
.burn_roles(burn_roles!{
burner => rule!(allow_all);
burner_updater => rule!(deny_all);
})
.create_with_no_initial_supply();
Restrict Withdraw
Resources restricted from being withdrawn are effectively locked in the Vault
that contains it. This makes the resource soulbound and its most common use-case is to attach some form of identification or reputation to the account that owns that resource.
// Note our resource takes and OwnerRole argument this can be Fixed, Updatable, or None
let my_token = ResourceBuilder::new_fungible(OwnerRole::Fixed(rule!(require(access_rule))))
.metadata(metadata!{
init {
"name" => "My Token", locked;
"symbol" => "TKN", locked;
}
})
.withdraw_roles(withdraw_roles!{
withdrawer => rule!(deny_all);
withdrawer_updater => rule!(deny_all);
})
.create_with_no_initial_supply();
Restrict Deposit
Resources restricted from being deposited are commonly called transient resources. This forces a dangling resource to exist. 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 if a specified condition is met.
// Note our resource takes and OwnerRole argument this can be Fixed, Updatable, or None
let my_token = ResourceBuilder::new_fungible(OwnerRole::Fixed(rule!(require(access_rule))))
.metadata(metadata!{
init {
"name" => "My Token", locked;
"symbol" => "TKN", locked;
}
})
.deposit_roles(deposit_roles!{
depositor => rule!(deny_all);
depositor_updater => rule!(deny_all);
})
.create_with_no_initial_supply();
Recallable Token
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.
// Note our resource takes and OwnerRole argument this can be Fixed, Updatable, or None
let my_token = ResourceBuilder::new_fungible(OwnerRole::Fixed(rule!(require(access_rule))))
.metadata(metadata!{
init {
"name" => "My Token", locked;
"symbol" => "TKN", locked;
}
})
.recall_roles(recall_roles!{
recaller => rule!(allow_all);
recaller_updater => rule!(deny_all);
})
.create_with_no_initial_supply();
Freezable Token
When building regulated assets you may need to have the ability to freeze those assets so of course Scrypto has this functionality built in for you to compose for your own use case.
// Note our resource takes and OwnerRole argument this can be Fixed, Updatable, or None
let freezer_token =
ResourceBuilder::new_fungible(OwnerRole::Fixed(rule!(require(access_rule))))
.metadata(metadata! {
init {
"name" => "My Token", locked;
"symbol" => "TKN", locked;
}
})
.freeze_roles(freeze_roles! {
freezer => rule!(allow_all);
freezer_updater => rule!(deny_all);
})
.mint_initial_supply(1000)
.into();
Updating the rules after creating resources
Up till now, we have specified all rules with an _updater
rule set to rule!(denyall)
.
This means that it can never be changed, ever. Instead of rule!(deny_all)
you could provide a custom rule with the usual rule!()
macro. The authority you provide here has the ability to update the rule in the future. They can change the rule at will, and at any point they also have the right to change the _updater
role’s rule to rule!(denyall)
, so that it may never again be changed. This means that it will display as fixed in the wallet.
Locking is a one-way process… there’s no going back to a mutable rule once it has been locked.
Here’s an example of playing with some rules around freezing a token:
// Initial creation, rule_admin is some badge address we have previously defined
let resource_address = ResourceBuilder::new_fungible(OwnerRole::Updatable(rule!(require(access_rule))))
.metadata(metadata!(
init {
"name" => "Globally freezable token", locked;
}
))
.withdraw_roles(withdraw_roles! {
withdrawer => rule!(allow_all);
withdrawer_updater => rule!(require(rule_admin));
})
.deposit_roles(deposit_roles! {
depositor => rule!(allow_all);
depositor_updater => rule!(require(rule_admin));
})
.create_with_no_initial_supply();
... // Later in the code
// `rule_admin_vault` is a vault that contains the badge allowed to make the following changes.
self.rule_admin_vault.authorize(|| {
// Freeze the token, so no one may withdraw or deposit it
let resource_manager = ResourceManager::from_address(resource_address);
resource_manager.set_depositable(AccessRule::DenyAll);
resource_manager.set_withdrawable(AccessRule::DenyAll);
// ...or, make it so only a person presenting the proper badge can withdraw or deposit
resource_manager.set_depositable(rule!(require(transfer_badge)));
resource_manager.set_withdrawable(rule!(require(transfer_badge)));
// Unfreeze the token!
resource_manager.set_depositable(AccessRule::AllowAll);
resource_manager.set_withdrawable(AccessRule::AllowAll);
// Lock the token in the unfrozen state, so it may never again be changed
resource_manager.lock_depositable();
resource_manager.lock_withdrawable();
});
The authorize()
method
In the previous example, you saw the use of the authorize()
method on the rule_admin_vault
. This method is available on both Vaults
and Buckets
and is used to temporarily put a proof
of the underlying resources on the authzone
for authorization.
The method takes as parameter a closure with no argument and runs it after putting the proofs on the auth zone. After running the closure, the proofs are removed from the auth zone.
Default Rules
All roles have defaults they are set to when unspecified or set to None
. They are:
Roles | Role Rule | Updater Role Rule |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|