Access Control - Resources

Resources can have access permissions individually set on various actions, and by setting or adjusting those permissions a developer can change their behavior. A "fixed supply" resource is merely a resource in which the permissions to mint and burn have been permanently disabled. A "freezable" resource is one in which some authority has the ability to change the permissions on withdrawal and deposit.

Here are all the resource actions which can have permissions set:

Rule Description Default

mintable

Who may mint additional supply

deny_all

burnable

Who may destroy some quantity

deny_all

restrict_withdraw

Who may take the resource from a vault

allow_all

restrict_deposit

Who may put the resource in a vault

allow_all

updateable_metadata

Who may update the resource’s metadata

deny_all

(Not yet implemented) recallable

Who may remotely seize the resource from any vault

deny_all

updateable_non_fungible_data

(Non-fungible) Who may update individual metadata

deny_all

Each of these actions can have an arbitrary set of access rules defined, and the system exposes those rules in a manner that off-ledger products, such as wallets, can understand. That is, if a particular token can have more supply minted, but never burned, that’s something that a user can instantly see right within their wallet, without needing to go read Scrypto code. If a token is currently freely transferrable, but an authority exists which could change it to a frozen state, that’s also directly visible to the user.

Examples

Resource rules are best explained by examples, so let’s see a few.

We’ll start with a mutable supply token in which a single authority has the ability to mint new tokens, or burn held tokens:

ResourceBuilder::new_fungible()
  .metadata("name", "Mutable supply, single mint/burn authority")
  .mintable(rule!(require(badge_address)), LOCKED)
  .burnable(rule!(require(badge_address)), LOCKED)
  .initial_supply(100);

The rule! macro is where we specify the logic which much evaluate to "true" in order to perform the action. We can arbitrarily combine different conditions, like so:

  .mintable(rule!(require_n_of(2, "list_of_admins") || require(super_admin)), LOCKED)

In the above example, in order to mint you either need any 2 of the badges in the specified Vec variable, or the super admin badge.

Let’s check out a token in which minting is a privileged action, but any holder can freely burn it:

ResourceBuilder::new_fungible()
  .metadata("name", "Mutable supply, single mint authority, burnable by any holder")
  .mintable(rule!(require(admin)), LOCKED)
  .burnable(rule!(allow_all), LOCKED)
  .initial_supply(100);

Or, say you want to hand out a token that the user can safely tuck away, but will not be able to transfer it after they have done so:

ResourceBuilder::new_fungible()
  .metadata("name", "Token which can not be withdrawn from a vault once stored")
  .restrict_withdraw(rule!(deny_all), LOCKED)
  .initial_supply(1)

Updating the rules after creating resources

Up till now, we have specified all rules as LOCKED, which is also the default state for any unspecified rule. LOCKED means that it can never be changed, ever. It is also possible to make a rule MUTABLE, with an associated authority specified. That authority 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 rule to a LOCKED state, so that it may never again be changed. 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()
  .metadata("name", "Freezable token")
  .restrict_deposit(rule!(allow_all), MUTABLE(rule!(require(rule_admin))))
  .restrict_withdraw(rule!(allow_all), MUTABLE(rule!(require(rule_admin))))
  .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 = borrow_resource_manager!(resource_address);
  resource_manager.set_depositable(rule!(deny_all));
  resource_manager.set_withdrawable(rule!(deny_all));

  // ...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(rule!(allow_all));
  resource_manager.set_withdrawable(rule!(allow_all));

  // 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 auth zone 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.

Transient tokens

Let’s do something crazy, and make a token which can never be deposited:

ResourceBuilder::new_non_fungible()
  .metadata("name","Undepositable token")
  .mintable(rule!(require(admin)), LOCKED)
  .burnable(rule!(require(admin)), LOCKED)
  .restrict_deposit(rule!(deny_all), LOCKED)
  .no_initial_supply();

We call this a "transient" token. At this point you may be wondering what possible use such a thing can have. Well, remember that the Radix Engine guarantees that a token can never be "dangling" at the end of a transaction. Any resource floating around outside of a vault at conclusion of operations will cause the transaction to abort. By handing your caller a token which can never be deposited, but which you control the burn authority for, you can compel them to eventually call a method which can burn the token, else the transaction will fail.

This allows for really cool stuff like programming a flash loan in an asset-oriented way, and we have an example of that linked at the bottom of this page.

A flash loan is an obvious example of the utility of transient resources (loan the money, let the caller do whatever they like, but they must pay you back plus a fee before the transaction is done), but this pattern is also useful for operations where you have an ecosystem of components, and you wish to incentivize using them together.

For example, you might have an oracle which receives regular off-ledger price feeds and costs money to keep updated, and costs consumers a fee to fetch price information from. You also control some kind of token selling component. You might set up a specialized entry point on your oracle which will return a price for free, but also give out a transient token. The only way to burn the transient token is to then pass it to your token selling component, which will burn it as long as a purchase of high enough value is performed. In other words, you can make your oracle free to use for people who use that price information to then make a trade on your other component.

Github Examples

Resources are used in one way or another in every Scrypto example, but here are a few highlights which involve using rules to do interesting things: