Migrating from Scrypto v0.9.0 to v0.10.0/v0.11.0

Developers should use v0.11.0, which is compatible with the current RCnet v2 phase 2 environment (ansharnet).

There are no developer facing changes between v0.10.0 and v0.11.0.

The below migration guide was written for the Scrypto v0.10.0 release, which came out with RCnet v2 phase 1, but is also a correct guide for updating to v0.11.0 for RCnet v2 phase 2.

New Features

Scrypto v0.10 is packed with updates; this page covers the key changes but is not an exhaustive list.

Authorization Model Refresh

In order to simplify the most common use cases and prevent some simple mistakes, the manner of configuring the auth on your blueprints, components, and resources has undergone a redesign.

Authorization now uses a role-based system, where permissions are assigned to roles, and AccessRules specify what is necessary to meet those role requirements. Resources have a pre-defined set of roles which you can populate the rules for, while blueprints allow for fully custom role definitions.

In the case of components, the new model looks like this:

  1. Roles are first defined, e.g: "admin", "staff", "supervisor". The naming convention will be up to you as it relates to your own use case.

  2. Method(s) are then mapped to each role (you can also map methods to multiple roles or a single role depending on your needs).

  3. Finally, the roles itself are then mapped to an AccessRule, specifying the badge(s) required to call the permissioned method(s).

The syntax to set up auth may feel a little different, but fundamentally, the concepts will be familiar to what you are used to. Below is a diagram that may help visualize this process:

Old auth model: old auth

New auth model: new auth

Let’s go over a few scenarios to provide tangible explanations:

Scenario 1: No Auth
This scenario shows a case where a blueprint doesn’t need auth at all and can run autonomously on its own.

use scrypto::prelude::*;

#[blueprint]
mod hello {
    struct Hello {
        sample_vault: Vault,
    }

    impl Hello {
        pub fn instantiate_hello() -> Global<Hello> {

            let my_bucket: Bucket = ResourceBuilder::new_fungible(OwnerRole::None) (1)
            // -- snip --

            Self {
                sample_vault: Vault::with_bucket(my_bucket),
            }
            .instantiate()
            .prepare_to_globalize(OwnerRole::None) (1)
            .globalize()
        }

        pub fn free_token(&mut self) -> Bucket {
            info!(
                "My balance is: {} HelloToken. Now giving away a token!",
                self.sample_vault.amount()
            );
            self.sample_vault.take(1)
        }
    }
}
1 By specifying OwnerRole::None, the resource/component will not receive a default permission set. In this example, as no permissions are explicitly specified, that means that nothing will have the ability to make any changes (e.g., there will be no way to update their metadata).

Scenario 2: Single Admin Authority
This scenario shows a case where the blueprint package does not need to have any sort of complex auth and a single admin role is sufficient. Using the pre-defined OWNER role will suffice for its needs.

use scrypto::prelude::*;

#[blueprint]
mod gumball_machine {
    struct GumballMachine {
    // -- snip --
    }

    impl GumballMachine {
        pub fn new(price_per_token: Decimal, owner_badge: ResourceAddress) -> (Global<GumballMachine>, Bucket) {

            // Creating a new token called "UsefulToken"
            let useful_token = ResourceBuilder::new_fungible(OwnerRole::None) (1)
            // -- snip --

            let component = Self {
            // --  snip --
            }

            .instantiate()
            .prepare_to_globalize(
                OwnerRole::Fixed( (2)
                    rule!(require(owner_badge)
                )
            ))
            .globalize();

            return component
        }

        pub fn buy(&mut self, funds: Bucket) -> Bucket {
        // -- snip --
        }
    }
}
1 Note that while the resource OWNER is not specified, the component OWNER is. These two OWNER role do not cross over.
2 A component OWNER role is specified. The OWNER will, by default, receive permission to update and lock metadata items.

Scenario 3: Complex Authority Scheme
This scenario shows a case where the blueprint package requires multiple actors with a hierarchy set of permissions.

use scrypto::prelude::*;

#[blueprint]
mod gumball_machine {
    enable_method_auth! { (1)
        roles { (2)
            super_admin => updatable_by: [OWNER]; (3)
            admin => updatable_by: [super_admin, OWNER]; (4)
        },
        methods { (5)
            buy => PUBLIC;
            change_price => restrict_to: [admin];
            redeem_profits => restrict_to: [super_admin, OWNER]; (6)
        }
    }
    struct GumballMachine {
    // -- snip --
    }

    impl GumballMachine {
        pub fn new(price_per_token: Decimal, owner_badge: ResourceAddress) -> (Global<GumballMachine>, Bucket) {

            // Creating a super admin badge for super_admin role
            let super_admin_badge = ResourceBuilder::new_fungible(OwnerRole::None) (7)
            // -- snip --

            // Creating an admin badge for the admin role
            let admin_badge = ResourceBuilder::new_fungible(OwnerRole::None)
            // -- snip --

            // Creating a new token called "UsefulToken"
            let useful_token = ResourceBuilder::new_fungible(OwnerRole::None)
            // -- snip --

            let component = Self {
            // --  snip --
            }

            .instantiate()
            .prepare_to_globalize(
                OwnerRole::Fixed( (8)
                    rule!(require(owner_badge)
                )
            ))
            .roles( (9)
                roles!(
                    super_admin => rule!(require(super_admin_badge.resource_address())); (10)
                    admin => rule!(require(admin_badge.resource_address()));
                )
            )
            .globalize();

            return (component, admin_badge)
        }

        pub fn buy(&mut self, funds: Bucket) -> Bucket {
        // -- snip --
        }

        pub fn change_price(&mut self, new_price: Decimal) {
        // -- snip --
        }

        pub fn redeem_profits(&mut self, amount: Decimal) -> Bucket {
        // -- snip --
        }
    }
}
1 Access to individual methods can be controlled by adding the enable_method_auth! macro. Once specified, this mapping of roles to methods is immutable.
2 Within the roles struct, we can define roles we would like to have permissions. Note that there is a special component OWNER role which does not need to be defined (but of course need to later be configured with an AccessRule).
3 Each role we define, we can also specify if we wish to have another role of which can update its AccessRule.
4 We can specify multiple roles which can update a preceding role. This means either role (super_admin & OWNER) has the authority to update the AccessRule of that role (admin).
5 Once you have chosen to enable method auth, every method of a component must be mapped to at least one or more role(s). PUBLIC may be used for methods that are meant to be called by anyone.
6 Methods can be mapped to multiple roles, which provide premission to access the method by any of those roles.
7 Resources created within the component can also have a dedicated resource OWNER role, more on this in Roles within the ResourceBuilder.
8 Even when using custom roles, the pre-defined OWNER role is still present and can be intermingled with custom roles.
9 The .roles method is used to map a role to an AccessRule where you can specify the required badge(s) associated to each role.

The addition of roles in the new auth model addresses the need to have fine-grained control with multiple actors within a blueprint package. The example snippet presented in Scenario 3 represents a blueprint configuration which has an auth scheme where multiple actors are required to operate the GumballMachine blueprint. These actors are the admin, super_admin, and OWNER. The auth scheme suggest that there is a hierarchy between these roles. While the example is contrived, we can imagine blueprint packages with much more complex mechanics and the new auth model aims to help developers better reason about multi-actor auth controls while still providing a straightforward auth configuration for blueprints with simpler auth scheme by having everything defaulted to the OWNER or even no one at all.

When it comes to defining component auth, it’s important to reiterate that while you are able to define roles and specify how each roles AccessRule can be updated, those definitions will be immutable once it is deployed to the network.

More example of this will be provided in the coming weeks. The main takeaway from the auth model refresh are:

  • Mapping methods to roles rather than directly to an AccessRule provide more flexibility and intuitive design for complex auth models.

  • There are clearer distinctions between an OwnerRole and Roles defined within a blueprint.

  • Provides better security as the IDE will be able to detect and inform methods that do not have an AccessRule, preventing accidental mishaps.

  • The owner, if set, will by default receive authority over any new modules added to Scrypto in the future, so it’s a good idea to always set an owner.

OwnerRole/OWNER Expanded

The concept of a blueprint package and component are not new, but there are finer details to how the OWNER role works within the new auth model works. Intuitively, an OWNER is a role which has ownership over something. A blueprint may define a number of OWNER role which may be associated with component(s) instantiated and resource(s) created within a blueprint. For components, OWNER has, by default and unless explicitly divested, will have configuration power over the Royalty, Authority, and Metadata modules. Resource(s) created within a component can have an OWNER role specified as well. Each resource created within the component can have different OWNER roles specified; with each OWNER mapped with an AccessRule to one, multiple, same, or different badges. A resource OWNER can have configuration power over the ResourceManager (e.g the ability to configure resource metadata, resource behavior, and so forth). Finally, unlike other roles, the OWNER role is one of the few that can update itself, not requiring an additional "updater" role to change the configuration of the OWNER.

Role delegation

With a simpler dApp, you may choose to have the component OWNER to be mapped to the same badge, or none, if preferred. But a dApp can have complex auth scheme which may need different roles responsible for different aspects of the dApp. In this case, responsibility can be delegated. For component methods, as you saw in the TokenSale blueprint example, roles can be created and be mapped with an AccessRule to specified badge(s). However, for other modules, there are already pre-defined roles which the dApp creator can delegate responsibilities to. The subsequent sections will provide an overview of what these pre-defined roles are and how they work.

New Syntax for Globalizing Components & Component Modules

As you may have noticed, globalizing a component now requires a preparation step with .prepare_to_globalize(owner_role), which requires the dApp creator to configure the OWNER role. The preparation method is a phase where developers can configure the permissions before a component is globalized by calling .roles(roles!()), .metadata(metadata!()), .enable_component_royalties(royalties!()). Choosing to configure these modules are optional. Otherwise, choosing not to configure these modules will default the metadata authority to the OWNER role and other permissions will be locked off.

See example below:

#[blueprint]
mod blueprint {
    // -- snip --
        let component: Global<Blueprint> = Self {
        }
        .instantiate()
        .prepare_to_globalize(OwnerRole::None)
        .roles(..)
        .enable_component_royalties(..)
        .metadata(..)
        .globalize()
    }
}

Royalties

Configuring component royalties is done through the Royalty module by calling .enable_component_royalties method within the globalizing process. Here’s an example:

struct Example {}

impl Example {
    pub fn new(owner_badge: ResourceAddress) -> Global<Example> {
        // -- snip --
        Self {}
            .instantiate()
            .prepare_to_globalize(OwnerRole::Fixed(rule!(require(owner_badge))))
            .enable_component_royalties(component_royalties! {
                roles { (1)
                    royalty_setter => rule!(require(setter_badge.resource_address())); (2)
                    royalty_setter_updater => OWNER; (3)
                    royalty_locker => rule!(require(locker_badge.resource_address()));
                    royalty_locker_updater => rule!(deny_all); (4)
                    royalty_claimer => rule!(require(claimer_badge.resource_address()));
                    royalty_claimer_updater => rule!(claimer_updater_badge.resource_address()); (5)
                },
                init {
                    public_method => Xrd(1.into()), locked; (6)
                    protected_method => Free, updatable;
                }
            }
        )
        .globalize()
1 Unlike configuring method auth, royalties already have pre-defined RoyaltyRoles and are directly mapped to an AccessRule.
2 Each pre-defined RoyaltyRoles are mapped to an AccessRule which specify required badge(s), same as before. An input whether the role is updatable or locked is also required.
3 You can use the AccessRule configuration for OWNER as a fallback.
4 If it’s decided that you don’t want a royalty role to be updatable, you can map the _updater role to deny_all and have that role also locked.
5 _updater roles which are updatable can be updated by themselves. This only applies to _updater roles.
6 Methods can then be mapped to their royalty configuration and specify whether they are updatable or not by the royalty_setter role.

Unlike setting component auth, auth for royalties have pre-defined RoyaltyRoles, here’s a summary description of the roles:

Role

Description

royalty_setter

Sets and update a method royalty configuration.

royalty_setter_updater

Updates the AccessRule of the royalty_setter and itself. Note that updating the AccessRule of both the royalty_setter and itself to DenyAll will result in permanently disabling permissions for both roles.

royalty_locker

Has the ability to set the mutability of a method’s royalty configuration.

royalty_locker_updater

Updates the AccessRule of the royalty_locker and itself. Note that updating the AccessRule of both the royalty_locker and itself to DenyAll will result in permanently disabling permissions for both roles.

royalty_claimer

Has the ability to claim component royalties.

royalty_claimer_updater

Updates the AccessRule of the royalty_claimer and itself. Note that updating the AccessRule of both the royalty_claimer and itself to DenyAll will result in permanently disabling permissions for both roles.

Metadata

Configuring auth for the Metadata module is similar to configuring the Royalty module. there are pre-defined roles which you can define to divest from the OWNER if chosen to do so. Here’s an example:

struct Example {}

impl Example {
    pub fn new() -> Global<Example> {
        Self {}
            .instantiate()
            .prepare_to_globalize(OwnerRole::Fixed(rule!(require(owner_badge.address()))))
            .metadata(metadata! {
                roles { (1)
                    metadata_setter => OWNER;
                    metadata_setter_updater => rule!(deny_all);
                    metadata_locker => rule!(allow_all);
                    metadata_locker_updater => rule!(allow_all);
                },
                init {
                    "some_key" => "string_value", updatable; (2)
                    "empty_locked" => EMPTY, updatable; (3)
                }
            }
        )
        .globalize()
1 Just as there are pre-defined RoyaltyRoles, enabling metadata for components have pre-configured MetadataRoles for handling metadata for wallet display and verification.
2 Metadata keys can then be mapped to its value.
3 Metadata with an empty placeholder value can be specified with EMPTY.

We can see a pattern emerging for these pre-defined MetadataRoles:

Role

Description

metadata_setter

Sets and update a component metadata configuration.

metadata_setter_updater

Updates the AccessRule of the metadata_setter and itself. Note that updating the AccessRule of both the metadata_setter and itself to DenyAll will result in permanently disabling permissions for both roles.

metadata_locker

Has the ability to set the mutability of a component’s metadata configuration.

metadata_locker_updater

Updates the AccessRule of the metadata_locker and itself. Note that updating the AccessRule of both the metadata_locker and itself to DenyAll will result in permanently disabling permissions for both roles.

Roles within the ResourceBuilder

There are parallel concepts to how auth works for resources as they do for components. There exist the concept of an OWNER role and pre-defined resource behavior roles to divest from the OWNER if chosen to do so. The only difference is that unlike component method auth, you can’t define your own roles, and you can’t delegate to roles created for your component via enable_method_auth!. Here’s an example:

ResourceBuilder::new_fungible(OwnerRole::Fixed(rule!(require(admin_badge.address()))) (1)
  .divisibility(DIVISIBILITY_NONE)
  .metadata(metadata! { (2)
        roles {
            metadata_setter => OWNER;
            metadata_setter_updater => rule!(deny_all);
            metadata_locker => rule!(require(XRD));
            metadata_locker_updater => rule!(allow_all);
        },
        init {
            "name" => "My Token", updatable;
        }
  .mint_roles(mint_roles! {
    minter => OWNER; (3)
    minter_updater => rule!(deny_all);
  })
  .burn_roles(burn_roles! {
    burner => rule!(require(XRD));
    burner_updater => OWNER;
  })
  .create_with_no_initial_supply();
1 Resource metadata is slightly different from other resource behavior as what used to be updatable_metadata has now been merged with the metadata method.
2 A resource OWNER can be specified, which is different from the component OWNER role defined.
3 Resource behavior roles are mapped to an AccessRule as normal, but can also use the resource OWNER as a fallback.

A resource OWNER has the power to configure the ResourceManager to configure the resource metadata and resource behavior. Additionally, each resource behavior now have different naming scheme to reflect the new roles auth model:

Old Method

New Method

mintable

mint_roles

burnable

burn_roles

restrict_withdraw

withdraw_roles

restrict_deposit

deposit_roles

updateable_metadata

metadata

recallable

recall_roles

updateable_non_fungible_data

non_fungible_data_update_roles

freezable

freeze_roles

Each of these resource behaviors have supporting macros to construct the roles: mint_roles!, burn_roles!, withdraw_roles!, deposit_roles!, freeze_roles!, recall_roles!, non_fungible_data_updatable_roles!. With their own pre-defined role pairs:

Roles

Description

  • minter

  • minter_updater

  • Has permission to mint a resource. Requires minter_updater to update its AccessRule.

  • Has permission to update the AccessRule of the minter. Can update its own AccessRule.

  • Note that updating the AccessRule of both the minter and minter_updater to DenyAll will result in permanently disabling permissions for both roles.

  • burner

  • burner_updater

  • Has permission to burn a resource. Requires burner_updater to updates its AccessRule.

  • Has permission to update the AccessRule of the burner. Can update its own AccessRule.

  • Note that updating the AccessRule of both the burner and burner_updater to DenyAll will result in permanently disabling permissions for both roles.

  • withdrawer

  • withdrawer_updater

  • Has permission to burn a resource. Requires withdrawer_updater to updates its AccessRule.

  • Has permission to update the AccessRule of the withdrawer. Can update its own AccessRule.

  • Note that updating the AccessRule of both the withdrawer and withdrawer_updater to DenyAll will result in permanently disabling permissions for both roles.

  • depositor

  • depositor_updater

  • Has permission to burn a resource. Requires depositor_updater to updates its AccessRule.

  • Has permission to Update the AccessRule of the depositor. Can update its own AccessRule.

  • Note that updating the AccessRule of both the depositor and depositor_updater to DenyAll will result in permanently disabling permissions for both roles.

  • freezer

  • freezer_updater

  • Has permission to burn a resource. Requires freezer_updater to updates its AccessRule.

  • Has permission to update the AccessRule of the freezer. Can update its own AccessRule.

  • Note that updating the AccessRule of both the freezer and freezer_updater to DenyAll will result in permanently disabling permissions for both roles.

  • recaller

  • recaller_updater

  • Has permission to burn a resource. Requires recaller_updater to updates its AccessRule.

  • Has permission to change the AccessRule of the recaller. Can update its own AccessRule.

  • Note that updating the AccessRule of both the recaller and recaller_updater to DenyAll will result in permanently disabling permissions for both roles.

  • non_fungible_data_updater

  • non_fungible_data_updater_updater

  • Has permission to burn a resource. Requires non_fungible_data_updater_updater to updates its AccessRule.

  • Has permission to update the AccessRule of the non_fungible_data_updater. Can update its own AccessRule.

  • Note that updating the AccessRule of both the non_fungible_data_updater and non_fungible_updater_updater to DenyAll will result in permanently disabling permissions for both roles.

Blueprint Function Auth

Scrypto v.10 added an enable_function_auth! macro which you can specify above the blueprint struct to specify an AccessRule for blueprint function(s). This way, you can control how your blueprints can be instantiated. Roles are not needed for this.

#[blueprint]
mod function_access_rules {
        enable_function_auth! {
            protected_function => rule!(require(RADIX_TOKEN));
            public_function => rule!(allow_all);
        }

    struct FunctionAccessRules {}

    impl FunctionAccessRules {
        pub fn protected_function() {}

        pub fn public_function() {}
    }
}

That’s it for now with how the new auth model works. More details and examples will come as the rest of the documentation will be updated. Throughout RCnet v2, the auth model may receive small adjustments, but the core concepts around roles will remain.

Stronger Type System

Added Blueprint Stubs

As you may have noticed in the example snippet in the previous section, we’ve included a new way to reference components. Instead of ComponentAddress, we may now utilize Global<T> where T is the stub interface of some object (blueprint name). This feature adds granularity in the type of components that a method may expect. For example, previously, when passing a ComponentAddress, there was no way of knowing what kind of component it is and where it may come from. Say that we have a method that expects a native account component we may guarantee that components passed into this method will in fact be a native account component. We can have a method signature that looks something like this:

pub fn register_airdrop(&mut self, account_component: Global<Account>) {
    // -- snip
}

Additionally, other fully typed objects can now be passed into methods and structs (e.g. Package and ResourceManager). Previously, in cases where we need to mint or inspect a resource where we have a ResourceAddress, we’d need to convert it into a ResourceManager before we can inspect or perform some kind of resource action. We no longer need to do that and pass the object itself directly; take for example:

#[blueprint]
mod example_blueprint {
    struct ExampleBlueprint {
        token_manager: ResourceManager, (1)
    }

    impl ExampleBlueprint {
        pub fn new(
        ) -> Global<ExampleBlueprint> {
            let token_manager = ResourceBuilder::new_fungible()
                .divisibility(DIVISIBILITY_NONE)
                .metadata(metadata!(
                    init {
                        "name" => "Admin Badge", locked;
                    }
                ))
                .mint_roles(mint_roles!(
                    minter => rule!(allow_all);
                    minter_updater => rule!(deny_all);
                ))
                .create_with_no_initial_supply();

            let component = Self {
                token_manager: token_manager,
            }
            .instantiate()
            .prepare_to_globalize(OwnerRole::None)
            .globalize();

            return component
        }

        pub fn mint(&mut self) -> Bucket {
            // Retrieve the ResourceManager for our token
            let token_manager: ResourceManager = self.token_manager; (2)

            // Mint our token
            token_manager.mint(1) (3)
        }
    }
1 We can store ResourceManager in a struct to fully retrieve it directly.
2 We can retrieve the ResourceManager directly as we would expect.
3 We can then perform a resource action as we would expect.

Granular Bucket, Vault, and Proof types

With Bucket, Vault, and Proof types, we’ve now added granular typing. The table below summarizes the additions.

Type

Additions

Bucket

  • FungibleBucket

  • NonFungibleBucket

Vault

  • FungibleVault

  • NonFungibleVault

Proof

  • FungibleProof

  • NonFungibleProof

These granular types enable our components to be more specific of what we expect. Take the example below:

pub fn deposit_fungible_tokens(&mut self, fungible_bucket: FungibleBucket) { (1)
    self.fungible_vault.put(fungible_bucket);
}
1 This method only accepts a Bucket containing fungible resources.

Type-safe Consensus Epoch Number

Added Epoch and Round.

Epoch methods:

Method

Description

zero

Creates a zero epoch (i.e. pre-genesis).

of

Creates an epoch of the given number.

next

Creates an epoch immediately following this one. Panics if this epoch’s number is [u64::MAX].

after(epoch_count)

Creates an epoch following this one after the given number of epochs. Panics if this epoch’s number is greater than [u64::MAX].

previous

Creates an epoch immediately preceding this one. Panics if this epoch’s number is 0.

relative(epoch_count)

Creates an epoch of a number relative to this one. Panics if the resulting number does not fit within u64.

Round methods:

Method

Description

zero

Creates a zero round (i.e. a state right after progressing to a next epoch).

of

Creates a round of the given number.

number

Returns a raw round number.

calculate_progress(from, to)

Returns a number of rounds between from and to, or None if there was no progress (i.e. their difference was not positive).

Added support for Actor Virtual Badge

Previously, an internal badge pattern was commonly used for the purposes of performing resource actions. With the support for the actor virtual badge, you no longer need to store an internal badge in your component just for the purposes of performing resource actions. For example:

pub fn instantiate_hello() -> Global<Hello> {
        let (address_reservation, component_address) =
            Runtime::allocate_component_address(Runtime::blueprint_id()); (1)

        let token_manager = ResourceBuilder::new_fungible(OwnerRole::None)
            .metadata(metadata! {
                init {
                    "name" => "Hello Token", locked;
                }
            )
            .mint_roles(mint_roles! (
                minter => rule!(require(global_caller(component_address))); (2)
                minter_updater => rule!(deny_all);
            ))
            .create_with_no_initial_supply();

            Self {
                token_manager: token_manager,
            }
            .instantiate()
            .prepare_to_globalize(OwnerRole::None)
            .with_address(address_reservation) (3)
            .globalize()
        }

        pub fn mint(&mut self) -> Bucket {
            self.token_manager.mint(1) (4)
        }
1 The ComponentAddress has to first be allocated.
2 We can now specify the ComponentAddress as the required AccessRule with global_caller().
3 Because we allocated a ComponentAddress we must now globalize with the address_reservation.
4 The Radix Engine provides the auth handling behind the scenes when performing resource actions.

Updated Native Account Blueprint

With Scrypto v0.10 comes with new additional features to the native account blueprint. Accounts can be configured to accept or reject deposits of certain resources when they are deposited by external third-parties through unprivileged methods.

Account Deposit Modes

Each account comes with a resource deposit rules (ResourceDepositRule) mapping which can be thought of as an "allow list" and "deny list" of resources. More specifically, it maps the ResourceAddress to an enum with variants Allowed, Disallowed, and Neither. Therefore, resources that are specified as Allowed is guaranteed to be deposited to the account and resources specified as Disallowed is guaranteed to be rejected by the account. When the ResourceDepositRule associated with a resource is Neither then another concept comes into the picture which is the concept of the account’s default treatment of resource deposits (AccountDepositRule). An account can be configures such that it treats resources not in the resource deposit rule mapping in the following way:

  • Accept: If a resource doesn’t have a resource specific rule then permit the deposit of the resource.

  • Reject: If a resource doesn’t have a resource specific rule then reject the deposit of the resource.

  • Allow Existing: If a resource doesn’t have a resource specific rule then permit the deposit of the resource if it’s XRD or if the account already has a Vault for this resource.

    • Any resource that the account has a Vault for can be deposited into the account while in this mode. If the user wishes to get rid of a resource and never accept deposits of it again then add it as a Disallowed resource to their resource ResourceDepositRule.

Account Securification

Account securification is the process by which the account’s owner access rule is updated from whatever it is to a new one which requires the owner badge of the account. Effectively, this is a one-time process that is done at the inception of an account (from a virtual account to a component account) by switching the account signing requirement from signature mode to badge mode. Thus, once the account has been securified, an owner badge will be returned. The wallet will have a dedicated flow for securifying accounts (switching them from signature mode to badge mode) and then, in the same manifest instruction, creating an access controller and storing the account’s owner badge in the access controller component.

As a result of these new concepts, there are now changes and additions to the account methods summarized in the table below:

Method

Change Notes

securify

Securifies the account, transitioning it from operating in signature mode to operating in badge mode. This method securifies the account minting a new account owner badge and changing the account’s current owner AccessRule to a new AccessRule of the account owner badge and returns the minted owner badge. The returned badge then must be stored somewhere, ideally in an access controller.This method is only callable by the Securify role. When a virtual account is first created, the Securify role requires the Owner authority. However, after the account has been securified the Securify role changes to being DenyAll such that securification can never happen again.

configure_resource_deposit_rule

Configures the deposit rule of a specific resource. This method sets the deposit rule for the specified resource which is the set of rules that take precedence over the default rules specified by the change_account_default_deposit_rule method.

change_account_default_deposit_rule

Changes the default deposit rule of the account. This method changes the default deposit rule of the account changing the behavior of how the account handles deposits from third-parties of resources that it does not have a specific rule for.

deposit

This method is only callable by the account owner and does not do any of the checks discussed in the Account Deposit Modes section. It permits all deposits since it requires the owner authority to be present when calling it.

deposit_batch

Deposits multiple buckets of resources into the account. This method is identical to deposit but deposits a vector of buckets instead of depositing a single bucket.

try_deposit_or_refund

Attempts to deposit resources in the account, refunds them returns them if the deposit fails. This method attempts to deposit a Bucket of resources into the account, if the account is configured to disallow deposits of this resource then they’re returned and refunded back as a Bucket.

try_deposit_batch_or_refund

Attempts to deposit buckets of resources into the account and refunds all of them if any of them can’t be deposited. This method attempts to deposit buckets of resources into the account, if the account is configured to disallow deposit of the resources then they’re all returned and refunded back as buckets.

try_deposit_or_abort

Attempts to deposit resources in the account, aborts the transaction if the deposit fails. This method attempts to deposit a Bucket of resources into the account, if the account is configured to disallow deposits of this resource then the transaction is aborted.

try_deposit_batch_or_abort

Attempts to deposit buckets of resources into the account aborts the transaction if any of them can’t be deposited. This method attempts to deposit buckets of resources into the account, if the account is configured to disallow deposit any of the resources then the transaction aborts.

Addendum

Scrypto Changes

Updated Proof creation methods

Method

Change

create_proof_of_amount

Creates a Proof of specified amount of the resource.
This used to be create_proof_by_amount.

create_proof_of_non_fungibles

Name change from create_proof_by_ids. This used to be create_proof_by_ids.

create_proof_all

A new method that creates a composite Proof of all the resource contained within the Bucket.
This used to be create_proof

Note that create_proof method has been removed.

Updated .authorize() interface for Bucket and Vault

.authorize() has been removed from Bucket and Vault and replaced with a new interface:

New Method

Description

authorize_with_all(fn)

Creates a composite Proof of all resources contained within the FungibleBucket or NonFungibleBucket.

authorize_with_amount(amount, fn)

Creates a specified number of Proof of the fungible resource contained within the FungibleVault or FungibleBucket.

authorize_with_non_fungibles(ids, fn)

Creates a specified Proof of non-fungibles contained within the NonFungibleVault or NonFungibleBucket.

Note that to access these methods, you must first convert the Vault or Bucket to their respective granular types using .as_fungible() or .as_non_fungible().

Replaced Proof Validation Mode

validate_proof() has been removed along with ProofValidationMode. It is now replaced with check(ResourceAddress).

pub fn authorized_method(&mut self, proof: Proof) {
    let checked_proof: CheckedProof = proof.check(self.badge.resource_address());

    let non_fungible_data = checked_proof.as_non_fungible().non_fungible().data();
}

Renamed ComponentAuthZone to LocalAuthZone

Renamed .resource_address method to .address for ResourceManager

Note that this has only been changed for the ResourceManager. Vault and Bucket types still have .resource_address method as it more accurately reflects that Vault and Bucket are associated with the address of their respective ResourceManager.

Removed Dynamic AccessRules

As a result of the update to the auth model, the ability to have dynamic AccessRules (i.e can’t read from struct state) is no longer supported. If you need this style of behaviors, you can either set the role’s mutability as its own component actor virtual badge or use a programmatic assert_auth requirement. Please refer to the Vesting scrypto example for a demonstration.

Added Runtime::Panic(message)

You can now create custom panic messages which will be visible to the gateway.

Removed borrow_resource_manager!, borrow_package!, and borrow_component!

  • Instead of using borrow_resource_manager! to turn a ResourceAddress to a ResourceManager. You may use:

let token_manager: Resource Manager = ResourceManager::from([resource_address]);

Updated External Blueprint Stubs

  • external_blueprint! is now extern_blueprint! respectively. Additionally, as a result of our improved type system, you can now use global_component!, resource_manager!, and package! macros for typed dependencies. Here’s an example below:

#[blueprint]
mod blueprint {
    extern_blueprint!(
        "package_rdx1pkgxxxxxxxxxfaucetxxxxxxxxx000034355863xxxxxxxxxfaucet",
        Faucet as MyFaucet {
            fn lock_fee(&self, amount: Decimal);
        }
    );

    const FAUCET: Global<MyFaucet> = global_component!(
        MyFaucet,
        "component_sim1cptxxxxxxxxxfaucetxxxxxxxxx000527798379xxxxxxxxxhkrefh"
    );

    struct Blueprint {}

    impl Blueprint {
        pub fn call_faucet_lock_fee() {
            let amount: Decimal = 10.into();
            FAUCET.lock_fee(amount);
        }
    }
}

The cross-blueprint-call example within the scrypto-examples repository provides several reference designs how to perform cross blueprint/component calls.

Added dump_manifest_to_file_system to scrypto-unit

String Transaction Manifest as an .rtm format can now be generated in test files.

#[test]
fn testing() {
    let private_key = Secp256k1PrivateKey::from_u64(1).unwrap();
    let account_component =
        ComponentAddress::virtual_account_from_public_key(&private_key.public_key());

    let manifest = ManifestBuilder::new()
            .withdraw_from_account(
                account_component,
                RADIX_TOKEN,
                dec!(10)
            )
            .deposit_batch(account_component)
            .build();

    // This generates the .rtm file of the Transaction Manifest.
    dump_manifest_to_file_system(
        &manifest,
        "./transaction-manifest",
        &NetworkDefinition::simulator()
    ).err();
}

Merged .updatable_metadata with .metadata from the ResourceBuilder

Added freeze_roles to the ResourceBuilder

You may now configure resources to be freezable by specifying the AccessRules of each FreezeRoles.

let my_token = ResourceBuilder::new_fungible()
    .metadata(metadata!(
        init {
            "name" => "My Token", locked;
            "symbol" => "TKN", locked;
        }
    ))
  .freeze_roles(freeze_roles!( (1)
      freezer => rule!(allow_all);
      freezer_updater => rule!(deny_all);
  .create_with_no_initial_supply();

Updated ResourceBuilder metadata interface

Specifying resource metadata within the ResourceBuilder now looks like this:

ResourceBuilder::new_fungible(OwnerRole::None)
  .divisibility(DIVISIBILITY_NONE)
  .metadata(metadata! {
    init {
      "key" => "value", locked;
    }
  })
  .mint_initial_supply(1);

Optionally, you can map AccessRule to each pre-defined resource metadata roles within the metadata! macro as well such as:

ResourceBuilder::new_fungible(OwnerRole::Fixed(rule!(require(owner_badge))))
  .divisibility(DIVISIBILITY_NONE)
  .metadata(metadata! {
    roles {
        metadata_setter => OWNER;
        metadata_setter_updater => rule!(deny_all);
        metadata_locker => rule!(require(RADIX_TOKEN);
        metadata_locker_updater => rule!(deny_all);
    },
    init {
      "key" => "value", locked;
    }
  })
  .mint_initial_supply(1);

Added metadata key and value limits

Metadata key is limited to 100 bytes. Metadata value is limited to 512 bytes.

Added a cap to royalty fees

Royalty fees are now capped to 150 XRD.

Added ability to specify royalty fees in XRD, USD, or Free

You can now specify royalty fees in XRD, USD, or Free.

Added enable_package_royalties! Macro

You can now specify package royalties in Scrypto. Example below:

#[blueprint]
mod radiswap {
    enable_package_royalties! {
        new => Xrd(5.into()),
        add_liquidity => Free,
        remove_liquidity => Usd(1.into()),
        swap => Xrd(2.into()),
    }
    ...
}

Added Radix Unique IDentifier (RUID)

RUID is a unique 32-byte identifier derived from transaction intent hash. This will result in change with NonFungibleLocalId to use RUID instead of UUID.

For example:

ResourceBuilder::new_ruid_non_fungible::<MyNfData>(OwnerRole::None)
    //..

Additionally, Runtime::generate_uuid has also been removed from Scrypto.

Added 4 API’s for querying fee reserve states

API

Description

Runtime::cost_unit_limit()

Returns the cost unit limit, set by transaction.

Runtime::cost_unit_price()

Returns the current cost unit price in XRD, defined by protocol.

Runtime::tip_percentage()

Returns the tip percentage, set by transaction.

Runtime::fee_balance()

Returns the remaining locked fee balance.

As a result, you can now calculate contingent fee reserves within your component.

Added support for Rust v1.70

You may now update Rust to v1.70.

Changed Package Definition File Extension from .schema to .rpd

You can find package definition files under ./target/wasm32-unknown-unknown/release. You will need them for publishing a package.

Increased resource mint limit to 2^160 subunits

// MAX_MINT_AMOUNT = 1461501637330902918203684832716.283019655932542976
MAX_MINT_AMOUNT: Decimal = Decimal(BnumI256::from(2).pow(160));

Transaction Manifest

There have been quite a bit of semantic changes with the transaction manifest under the hood and we’ll note those changes in this section. However, we will now also encourage developers to use the Rust ManifestBuilder and the TypeScript ManifestBuilder for constructing their manifests. Tooling on manifest building are still early, but using existing tools will provide a substantial improvement in developer experience than writing them by hand.

Added Support For Named Addresses and Enabled Global Address Allocation

You can now publish a package and call function in the same manifest.

See example:

ALLOCATE_GLOBAL_ADDRESS
    Address("${package_package_address}")
    "Package"
    AddressReservation("my_reservation")
    NamedAddress("my_package")
;
PUBLISH_PACKAGE_ADVANCED
    Some(AddressReservation("my_reservation"))
    Blob("${code_blob_hash}")
    Tuple(
        Tuple(
            Map<String, Tuple>()
        ),
        Map<String, Map>(),
        Map<String, Tuple>()
    )
    Map<String, Enum>()
    None
;
CALL_FUNCTION
    NamedAddress("my_package")
    "BlueprintName"
    "some_function"
    Decimal("1.0")
;

New Transaction Manifest Instructions

Instruction

Example

CREATE_ACCOUNT_ADVANCED

CREATE_ACCOUNT_ADVANCED
    Enum<OwnerRole::Updateable>(
        Enum<AccessRule::AllowAll>()
    );

ALLOCATE_GLOBAL_ADDRESS

ALLOCATE_GLOBAL_ADDRESS
    Address("package_address")
    "Package"
    AddressReservation("address_reservation")
    NamedAddress("component_address");

PUBLISH_PACKAGE_ADVANCED

PUBLISH_PACKAGE_ADVANCED
    Some(
        AddressReservation("address_reservation")
    )
    Blob("blobs")
    Tuple(
        Map<String, Tuple>()
    )
    Map<String, Enum>()
    None;

CREATE_IDENTITY_ADVANCED

CREATE_IDENTITY_ADVANCED
    None;

UPDATE_ROLE

UPDATE_ROLE
    Address("${resource_address}")
    Enum<0u8>()
    "hello"
    Enum<0u8>()
    Enum<0u8>();

Renamed Transaction Manifest instruction names

Transaction manifest instructions with _FROM_IDS or _BY_IDS has been replaced with NON_FUNGIBLES

Old Instruction

New Instruction

TAKE_FROM_WORKTOP_BY_AMOUNT

TAKE_FROM_WORKTOP

TAKE_FROM_WORKTOP_BY_IDS

TAKE_NON_FUNGIBLES_FROM_WORKTOP

TAKE_FROM_WORKTOP

TAKE_ALL_FROM_WORKTOP

ASSERT_WORKTOP_CONTAINS_BY_AMOUNT

ASSERT_WORKTOP_CONTAINS

ASSERT_WORKTOP_CONTAINS_BY_IDS

ASSERT_WORKTOP_CONTAINS_NON_FUNGIBLES

ASSERT_WORKTOP_CONTAINS

Removed

Changed argument ordering of Transaction Manifest instructions

ResourceAddress and NonFungibleLocalId now comes first for transaction manifest instructions which specifies resources.

  • TAKE_FROM_WORKTOP

  • TAKE_NON_FUNGIBLES_FROM_WORKTOP

  • CREATE_PROOF_FROM_BUCKET_OF_AMOUNT

  • CREATE_PROOF_FROM_BUCKET_OF_NON_FUNGIBLES

  • CREATE_PROOF_FROM_AUTH_ZONE_OF_AMOUNT

  • CREATE_PROOF_FROM_AUTH_ZONE_OF_NON_FUNGIBLES

  • ASSERT_WORKTOP_CONTAINS

  • ASSERT_WORKTOP_CONTAINS_NON_FUNGIBLES

Updated Proof creation instructions

Instruction

Change

CREATE_PROOF_FROM_BUCKET_OF_AMOUNT

New instruction

CREATE_PROOF_FROM_BUCKET_OF_NON_FUNGIBLES

New instruction

CREATE_PROOF_FROM_BUCKET_OF_ALL

New instruction

CREATE_PROOF_FROM_AUTH_ZONE_OF_AMOUNT

Renamed instruction.

CREATE_PROOF_FROM_AUTH_ZONE_OF_NON_FUNGIBLES

Renamed instruction.

CREATE_PROOF_FROM_AUTH_ZONE_OF_ALL

New instruction.

These Proof instructions have been removed: * CREATE_PROOF_FROM_BUCKET * CREATE_PROOF_FROM_AUTH_ZONE

Changed Enum specification

Now looks like Enum<3u8>() or Enum<Well::Known>() instead of Enum(3u8, …​) or Enum("Well:Known", …​).

Changed Map specification

Now looks like Map<X, Y>(x1 ⇒ y1, x2 = y2)