Creating a "Regulated" Token

This example will walk you through the creation of a special token that has a 3-stage lifecycle, featuring different behavior at each stage. You’ll get a chance to familiarize yourself with how resource flags work, how mutable resource flags work, and how to correctly use resources passed in using the auth macro within a method.

You can find the code for this example on github here.

The blueprint we will build will perform several functions. It will create the token itself, it will sell the token to buyers, it will manage the token’s 3 stages of life, and it will permit the proper authority to toggle whether the token may be freely transferred.

Resources and Data

struct RegulatedToken {
    token_supply: Vault,
    internal_authority: Vault,
    collected_xrd: Vault,
    current_stage: u8,
    admin_badge_def: ResourceDef,
    freeze_badge_def: ResourceDef,
}

We’ll start with 3 vaults. token_supply will hold our "for sale" tokens, internal_authority will contain a badge that we’ll keep control of, and collected_xrd will hold our payments.

Our token has 3 stages, which we’ll track with an integer, and we’ll need to remember the ResourceDef for a couple of badges which we’re going to give to our instantiator. We will do something a little different in this example, in that the badges we’re returning will not be used solely to authorize access to a method…​they will have some control over the token we’re going to create, and their owners could exercise that control outside of our component if they so chose.

Getting Ready for Instantiation

To keep things simple, we won’t have any user-configurable instantiation parameters for our RegulatedToken, and upon instantiation we’ll return the component itself as well as two badges for administrative functions, giving us the following function signature:

pub fn new() -> (Component, Bucket, Bucket) {

We’re going to need 3 different badges with 3 distinct sets of permissions.

  1. A superadmin badge which has full permission to do anything.

  2. An internal admin badge which has the authority to mint more supply (when system rules permit), and bypass the restricted transfer flag (when it’s enabled).

  3. A badge used for selectively freezing the token supply, which will need permission to manipulate resource flags.

When we go to create our regulated token using ResourceBuilder, we will have to specify any associated badges at creation time, so we must now create those badges.

let general_admin: Bucket = ResourceBuilder::new_fungible(DIVISIBILITY_NONE)
    .metadata("name","RegulatedToken general admin badge")
    .flags(FREELY_BURNABLE)
    .initial_supply_fungible(1);

let freeze_admin: Bucket = ResourceBuilder::new_fungible(DIVISIBILITY_NONE)
    .metadata("name","RegulatedToken freeze-only badge")
    .flags(FREELY_BURNABLE)
    .initial_supply_fungible(1);

let internal_admin: Bucket = ResourceBuilder::new_fungible(DIVISIBILITY_NONE)
    .metadata("name","RegulatedToken internal authority badge")
    .flags(FREELY_BURNABLE)
    .initial_supply_fungible(1);

You’ll notice we are making each badge an indivisible singleton, and managing them directly. We expect that, in a real system, you would probably make use of a purpose-built blueprint for creating and managing them.

If you’ve been following Scrypto since the preview, please note that the Badge and Token types are gone! As of v0.2.0, everything’s just Fungible or Nonfungible, though we still use the term "badge" to describe a thing which will be predominantly used for authorization, and "token" for what you generally think of as an asset.

Now we’re ready to make our token! We’ll show the code first, and then explain it in detail below:

let my_bucket: Bucket = ResourceBuilder::new_fungible(DIVISIBILITY_MAXIMUM)
    .metadata("name", "Regulo")
    .metadata("symbol", "REG")
    .metadata("stage", "Stage 1 - Fixed supply, may be restricted transfer")
    .flags(SHARED_METADATA_MUTABLE | RESTRICTED_TRANSFER)
    .mutable_flags(MINTABLE | SHARED_METADATA_MUTABLE | RESTRICTED_TRANSFER)
    .badge(
        general_admin.resource_def(),
        ALL_PERMISSIONS
    )
    .badge(
        freeze_admin.resource_def(),
        MAY_MANAGE_RESOURCE_FLAGS
    )
    .badge(
        internal_admin.resource_def(),
        MAY_MINT | MAY_TRANSFER
    )
    .initial_supply_fungible(100);

There’s a lot going on there. After setting up some shared metadata, we call flags to specify the behaviors that our resource will have upon creation. Flagging logic is all handled through bitmasking, which lets us compactly store and easily manipulate a broad set of independent toggles. The default set of flags for a new resource is none (0), so only the things we specify here will be turned on. You can see the complete list of flags here. For stage 1 of our token, we will start with the token in a restricted transfer state (where the proper authority must be present in order to withdraw it from a Vault), and we will allow the metadata to be updatable (again, when the proper authority is present).

By default, any flags on a resource are immutable. Because we want to change some of our flags to different states in the future, we must explicitly specify which flags may be changed. A call to mutable_flags behaves similarly to flags, except rather than turning the flags on right now, it’s specifying that those flags may be changed in the future. Note that we’re giving ourselves the option to change the MINTABLE flag, even though at initial creation the token will have a fixed supply.

Next, we need to set up our badges with the proper permissions. Here again, bitmasking is used, and the complete set of valid permissions can be seen here. It’s worth pointing out that the permission MAY_MANAGE_RESOURCE_FLAGS both grants the ability to change the state of flags (if they were marked as mutable at creation), as well as to permanently change a given flag from mutable to immutable.

Finally, we create our supply of 100 tokens and place them in a Bucket. Whew! That may seem overwhelming on first read, but after you’ve done it a couple times, you’ll sail through it with hardly a thought.

All that’s left now is to actually instantiate our component, and then return it along with the badges we just created.

let component = Self {
    token_supply: Vault::with_bucket(my_bucket),
    internal_authority: Vault::with_bucket(internal_admin),
    collected_xrd: Vault::new(RADIX_TOKEN),
    current_stage: 1,
    admin_badge_def: general_admin.resource_def(),
    freeze_badge_def: freeze_admin.resource_def(),
}
.instantiate();

(component, general_admin, freeze_admin)

Freezing or unfreezing token transfers

Anyone in possession of either the superadmin badge or the freeze badge will have the ability to put the token into or out of a restricted transfer state. Let’s see how to accomplish that.

#[auth(admin_badge_def, freeze_badge_def, keep_auth)]
pub fn toggle_transfer_freeze(&self, set_frozen: bool) {
    let token_def = self.token_supply.resource_def();
    if set_frozen {
        token_def.enable_flags(RESTRICTED_TRANSFER, auth);
        info!("Token transfer is now RESTRICTED");
    }
    else {
        token_def.disable_flags(RESTRICTED_TRANSFER, auth);
        info!("Token is now freely transferrable");
    }
}

The first thing to note is that in our auth macro we are accepting either badge, because they both have the necessary authority. You’ll also see the keep_auth parameter. This signifies that we don’t want the macro to automatically drop our reference to auth after it authorized the badge…​ we want to use it to call the disable_flags and enable_flags method.

Based on the set_frozen boolean, we will either disable or enable the RESTRICTED_TRANSFER flag on our regulated token. Both the enable_flags and disable_flags methods work on the same bitmasking principle, though they alter only the flags you specify. In other words, if you had a resource with the MINTABLE flag already enabled, and you called enable_flags(BURNABLE | RESTRICTED_TRANSFER), then you’d wind up with a resource that was MINTABLE, BURNABLE, and RESTRICTED_TRANSFER. It’s perfectly fine to include an already enabled flag in a call to enable_flags (or vice-versa for disable_flags); it will not impact the result.

Also note that regardless of what badge the user passed in, we just use auth within the method to refer to it.

On any resource, the state of all flags and the mutability of all flags is always publicly shown. This permits implementors of both Scrypto blueprints as well as tools like wallets to understand not only what a resource’s current behavior is, but also what it may be in the future. In other words, a wallet could give a user a clear warning that a token is currently freely transferrable, but the RESTRICTED_TRANSFER flag is mutable, and hence there is no guarantee that it will always be freely transferrable.

Advancing the token to the next stage

There’s one more workhorse function to our blueprint, which is the logic to advance it through the next 2 stages of behavior. We’ll reproduce it here, and then break down what’s happening.

#[auth(admin_badge_def, keep_auth)]
pub fn advance_stage(&mut self) {
    assert!(self.current_stage <= 2, "Already at final stage");

    if self.current_stage == 1 {
        // Advance to stage 2
        // Token will still be restricted transfer upon admin demand, but we will mint beyond the initial supply as required
        self.current_stage = 2;
        let token_def = self.token_supply.resource_def();

        // Update token's metadata to reflect the current stage
        let mut metadata = token_def.metadata();
        metadata.insert("stage".into(), "Stage 2 - Unlimited supply, may be restricted transfer".into());
        token_def.update_metadata(metadata, auth.clone());

        // Enable minting for the token
        token_def.enable_flags(MINTABLE, auth.clone());
    }
    else {
        // Advance to stage 3
        // Token will no longer be regulated
        // Restricted transfer will be permanently turned off, supply will be made permanently immutable
        self.current_stage = 3;
        let token_def = self.token_supply.resource_def();

        // Update token's metadata to reflect the final stage
        let mut metadata = token_def.metadata();
        metadata.insert("stage".into(), "Stage 3 - Unregulated token, fixed supply".into());
        token_def.update_metadata(metadata, auth.clone());

        // Set our flags appropriately now that the regulated period has ended
        token_def.disable_flags(MINTABLE | RESTRICTED_TRANSFER | SHARED_METADATA_MUTABLE, auth.clone());

        // Permanently prevent the flags from changing
        token_def.lock_flags(ALL_FLAGS, auth.clone());

        self.internal_authority.take_all().burn();
    }

    // since we used "auth.clone() every time, we must manually drop "auth"
    auth.drop();
}

We will again use the auth macro along with keep_auth, though this time we’ll only be permitting the superadmin badge to initiate stage advancement.

When we advance to stage 2, we will update our metadata to indicate to the world that we’re at the next stage, and permit minting to occur in order to satisfy demand beyond the initial supply.

In order to update our "stage" metadata, we first get a copy of the existing metadata, and then use insert, which functions like an "upsert", to update the value we’re interested in. We then set our updated metadata to be written to the token’s ResourceDef, which is possible because the SHARED_METADATA_MUTABLE flag is set.

Because we will be using our auth reference multiple times, you’ll see that we clone the reference each time we use it, like so: token_def.update_metadata(metadata, auth.clone()). We’ll have to remember to explicitly drop it when we’re all done, using auth.drop().

Advancing to stage 3 works similarly to advancing to stage 2, except this time we are effectively "dumbing down" the token now that the regulated period has ended. We disable all of the flags so that it will be fixed supply, freely transferrable, and without mutable metadata. We then call lock_flags to permanently make all flags immutable. We use ALL_FLAGS as a convenience, but we could also have just locked the 3 that were mutable (MINTABLE | SHARED_METADATA_MUTABLE | RESTRICTED_TRANSFER).

Locking a flag is a one-way operation! Once it has been made immutable, you can never go back.

Finally, since our internal authority badge was only used for minting and bypassing restricted transfer rules, we can safely destroy it. It’s FREELY_BURNABLE, so we don’t need any authority to do so.

Selling tokens

Most of the buy_token method is just logic around pulling out the correct amount of tokens and minting any shortfall, so we won’t go into it line-by-line here. It’s worth examining this bit here:

let tokens = self.internal_authority.authorize(
    |auth| self.token_supply.take_with_auth(quantity, auth)
);

Note the use of the take_with_auth() method. If our token happens to have the RESTRICTED_TRANSFER flag enabled, and we were to simply call take() instead of take_with_auth(), we would receive a runtime error that the operation was not permitted. Because we are holding the appropriate authority to permit transfer of the token regardless of rules (and we want buyers to always be able to purchase them, even if they couldn’t send them on after depositing them in a Vault), we elect to always use take_with_auth(), which will work regardless of whether it is in the restricted transfer state.

We also try to mint tokens whenever there is a shortfall, like so:

let tokens = self.internal_authority.authorize(
    |auth| self.token_supply.resource_def().mint(extra_demand, auth)
);

That call to mint will fail in the event that the MINTABLE flag is not set, even though the proper minting authority badge is present. The flag takes precedence.

Closing thoughts

We did some things you probably wouldn’t want to do in a real system.

  • We gave out a badge intended for use for freezing which actually had permissions to manipulate any of the mutable flags, or lock flags at will. In our scenario, in which we wanted the holder to only have the ability to manipulate the RESTRICTED_TRANSFER flag, we should have kept that authority on an internal badge and just given out a badge which allowed them to access the appropriate method for toggling the state of the flag.

  • We destroyed our internal authority badge as soon as we advanced to stage 3, to show how FREELY_BURNABLE allows this. This introduces a bug! If we have any tokens remaining in our token_supply Vault when we go to stage 3, they would become forever inaccessible, because our logic uses take_with_auth() and we would have just destroyed the badge it intends to use.

  • We blindly try to do some things that may be impermissable based on the current state of the system, to allow you to experience the appropriate runtime errors if you’re playing with the example code. In a real system, it would be better to note that the operation was about to fail, and kick out an error explaining the situation.