Restricting Component Methods

Components contain methods, some methods will be public, and others require some degree of protected access. Roles are constructed to define the hierarchy of access to different methods. Some components will only require a simple auth model, in such case, only one role needs to be defined. This article will cover how auth is set up for your components.

Setting up Component Auth

Consider a TokenSale component we showed in the access control introduction. The TokenSale component is designed to sell tokens as the name implies. The TokenSale component has several methods which require different levels of access. These methods and the level of access we’d like to have for these methods are summarized in the table below:

Method

Description

Access Level

buy(payment)

Allows users to purchase a token.

Public

create_admin

Creates a single admin badge.

Super Admin & Owner

change_price(new_price_per_token)

Changes the price of the tokens for sale.

Admin, Super Admin, & Owner

redeem_profits(amount)

Take profits from collected sales.

Owner

These methods have different levels of access and we want to create some form boundaries between their access.

Setting up our Roles will require us to call the enable_method_auth! macro. We call this macro below our mod and above our blueprint struct. This will be where our auth will be defined. Below is an example that you’ve seen before in the previous page, but we’ll provide more commentary to illuminate some points.

#[blueprint]
mod token_sale
    enable_method_auth! {
        roles { (1)
            super_admin => updatable_by: [];
            admin => updatable_by: [super_admin];
        },
        methods {
            buy => PUBLIC; (2)
            create_admin => restrict_to: [super_admin, OWNER]; (3)
            change_price => restrict_to: [admin, super_admin, OWNER];
            redeem_profits => OWNER;
        }
    struct TokenSale {
        ..
    // -- snip --
    }
}
1 Roles are defined within this struct and creates a RoleList. The naming scheme are entirely up to you. The only thing to keep in mind is that the OWNER role doesn’t need to be defined and we will go over that later.
2 In the methods struct, methods are mapped to their associated roles. There are 5 different variants of Roles: PUBLIC, OWNER, SELF, NOBODY, and RoleList.
3 You can map a method to multiple Roles. This implies that both Roles have access to the method.

After defining your Roles and associating methods to their respective Roles, the next step is to build the constructor which defines the badges a role need to have to access a method. Before we get there, let’s first review the role varients and what they mean:

Variant

Description

PUBLIC

Associating a method to the PUBLIC role sets the method to be freely accessed by anyone.

OWNER

The OWNER is a unique role, which assumes control over the blueprint or component. Methods assigned to the OWNER role not only assumes access to the method, but also to the component modules such as Authority, Metadata, and Royalties.

SELF

The SELF role refers to the package or component itself. Scrypto has a concept of virtual badges which provides the package/component permission to act on behalf of itself.

NOBODY

Associating methods to NOBODY assumes that no one can access the method.

RoleList

The RoleList is the list of roles defined in the roles struct.

Setting up the Authority Module

After the Roles have been defined and methods are mapped to their respective role, we can now construct our Authority module.

In this module, we are now mapping each role to an AccessRule. AccessRule will be covered in more detail in the next section, but effectively, we are setting up the badges each role is required to have to access their respective methods. Think of this like handing over different security clearance to an employee. Some badges will have a higher security clearance than others.

Here’s an example:

// -- snip --
.instantiate()
.prepare_to_globalize( (1)
    OwnerRole::Fixed(
        rule!(require(owner_badge.resource_address())
    )
))
.roles(
    roles!(
        super_admin => rule!( (2)
            require_amount(dec!(2), super_admin_badge.resource_address()) (3)
        );
        admin => rule!(
            require(admin_manager.resource_address())
        );
    )
)
.globalize();
1 As mentioned, the OWNER role is unique, since not only can they have access to protected methods (if specified), but more importantly, they have access to the component’s modules such as Authority, Metadata, Royalty.
2 Roles are mapped to an AccessRule which specifies the badge requirement of the role.
3 You can have multiple badges associated to a role to decentralize the control of a particular role.

Since OWNER role is part of the Roles system, yet is constructed in a different part from the rest of the Roles, this can be a bit confusing. Roles such as OWNER and SELF are baked into the engine. They don’t need to be defined, but there does need to be some specification on badge(s) associated to them. Of course, your component doesn’t need to have an OWNER, in such case, you can simply pass OwnerRole::None which will lock you or anyone from configuring the Authority, Metadata, and Royalty module.

Setting up Blueprint Package Auth

The section above covers how auth is set up at the component level. However, we can also set up auth at the blueprint package level. With blueprint packages, Roles doesn’t need to be defined. We can simply map a method to its AccessRule.

Here’s an example:

mod fund_manager {
    enable_function_auth! {
        instantiate_fund => rule!(require(fund_manager_badge.resource_address()));
    }
 // -- snip --
}