Migrating from Scrypto v0.3.0 to v0.4.0

This article describes how you can migrate and port your existing Scrypto v0.3.0 code to v0.4.0. This article begins with a number of common changes which all users will need to make to their codebase to get it working with the new version. Then, this article moves into an FAQ-style article of tiny bits of information which you will need when migrating your codebase.

This migration guide assumes that you have already installed and updated your Scrypto toolchain to the current version. If you have not, then please follow the instructions here to update to the new version.

Throughout this article you will find that sample code is provided for v0.3.0 and v0.4.0 of Scrypto. These are provided to help the reader compare and contrast the changes between the two versions and to help make the new concepts clearer.

Updating your Cargo.toml file

This is the very first thing that you will need to do in order to get your existing packages to work with the new version of Scrypto.

When you create a new Scrypto package through scrypto new-package your-package-name a number of files are automatically generated for you. One of these files is the Cargo.toml file which references the dependencies to include in your package as well as their versions, meaning that you will find the Cargo.toml files inside the package directories.

To get your packages to work you will need to change the versions of the sbor, scrypto, and radix-engine dependencies to be v0.4.0 instead of v0.3.0 in the [dependencies] and [dev-dependencies] sections.

In addition to that, you need to add strip = "debuginfo" under the [profile.release] section. Below is a comparison of what a typical Cargo.toml file should look like for v0.4.0 as well as a comparison with v0.3.0 to make the differences clear.

  • Scrypto v0.4.0

  • Scrypto v0.3.0

[package]
name = "your-package-name"
version = "0.1.0"
edition = "2021"

[dependencies]
sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.4.0" } (1)
scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.4.0" } (1)

[dev-dependencies]
radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.4.0" } (1)

[profile.release]
opt-level = 's'     # Optimize for size.
lto = true          # Enable Link Time Optimization.
codegen-units = 1   # Reduce number of codegen units to increase optimizations.
panic = 'abort'     # Abort on panic.
strip = "debuginfo" # Strip debug info. (2)

[lib]
crate-type = ["cdylib", "lib"]
1 Ensure that the tag is v0.4.0 and not any other version.
2 Add this line to file to strip the debug info from the generated wasm code.
[package]
name = "your-package-name"
version = "0.1.0"
edition = "2021"

[dependencies]
sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.3.0" }
scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.3.0" }

[dev-dependencies]
radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.3.0" }

[profile.release]
opt-level = 's'     # Optimize for size.
lto = true          # Enable Link Time Optimization.
codegen-units = 1   # Reduce number of codegen units to increase optimizations.
panic = 'abort'     # Abort on panic.

[lib]
crate-type = ["cdylib", "lib"]

Renames

There are quite a number of renames which happened with v0.4.0 of Scrypto to a few of the data types. The following table includes a list of all of the renames in this version:

v0.3.0 Name

v0.4.0 Name

ResourceDef

ResourceManager

BucketRef

Proof

NonFungibleKey

NonFungibleId

Context

Runtime

The Address type has been removed in favour of three new types: PackageAddress, ComponentAddress, and ResourceAddress which (as the name suggests) are used to address packages, components, and resources respectively.

These name changes apply across the entirety of Scrypto, meaning that a method such as bucket.get_non_fungible_keys would now be called bucket.get_non_fungible_ids because of this name change to the NonFungibleKey.

The sections below include tables which highlight the difference between the method and function naming for a few of the data types. When a cell is left blank it means that this method does not exist for that version of code.

Buckets

The following table showcases the name changes to the methods on the Bucket type between v0.3.0 and v0.4.0.

v0.3.0 Name

v0.4.0 Name

bucket.burn_with_auth

bucket.burn

bucket.get_non_fungible_key

bucket.get_non_fungible_keys

bucket.non_fungible_ids

bucket.update_non_fungible_data

bucket.get_non_fungibles

bucket.non_fungibles

bucket.get_non_fungible_data

bucket.non_fungible

bucket.present

bucket.create_proof

bucket.resource_def

borrow_resource_manager!(bucket.resource_address())

Vault

The following table showcases the name changes to the methods on the Vault type between v0.3.0 and v0.4.0.

v0.3.0 Name

v0.4.0 Name

vault.authorize_with_auth

vault.authorize

vault.get_non_fungible_key

vault.get_non_fungible_keys

vault.non_fungible_ids

vault.get_non_fungibles

vault.non_fungibles

vault.get_non_fungible_data

vault.non_fungible

vault.take_with_auth

vault.take

vault.take_all_with_auth

vault.take_all

vault.create_proof

vault.create_proof_by_ids

vault.create_proof_by_amount

vault.take_non_fungible_with_auth

vault.take_non_fungible

vault.take_non_fungibles

vault.update_non_fungible_data

vault.resource_def

borrow_resource_manager!(vault.resource_address())

Component Instantiation

In v0.4.0, the process of instantiating a new component has been changed slightly in order to allow for the system-level authorization system to exist. The following are the main changes made to the component instantiation process:

  • Component instantiation now returns a ComponentAddress instead of a Component.

  • Components now require a call to .instantiate then to .globalize.

  • After instantiation and before globalization, components allow for a call to .add_access_check to specify the access rules on methods.

The following code is a snippet of how components may be instantiated and created in v0.4.0:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

use scrypto::prelude::*;

blueprint! {
    struct Sample {}

    impl Sample {
        pub fn new() -> ComponentAddress { (1)
            return Self{}
                .instantiate()
                .globalize(); (2)
        }
    }
}
1 Instantiation functions now return a ComponentAddress and not a Component.
2 A call to .globalize is now required.
use scrypto::prelude::*;

blueprint! {
    struct Sample {}

    impl Sample {
        pub fn new() -> Component {
            return Self{}
                .instantiate()
        }
    }
}

Authorization System

The authorization system is the one area that has seen the most changes between the v0.3.0 and v0.4.0. V0.4.0 introduces the concept of system-level auth which essentially means that authenticated methods may be called if their required badge(s) are found in the Auth Zone.

In v0.3.0 the transaction worktop used to hold resources, buckets, and badges (in the form of BucketRef). With v0.4.0 comes a new section in the transaction worktop called the Auth Zone which is primarily used to hold Proofs (formerly called BucketRef). When a method/function is configured such that it requires a certain resource for authorization, the system automatically verifies that the transaction has these resources in the Auth Zone before allowing this method/function to be called.

The following are the key changes made to the authorization system between v0.3.0 and v0.4.0:

  • Added a new system-level authorization system to components and resources.

  • Removed the #[auth(…​)] macro (while still allowing for similar behavior).

Let’s first begin taking a look at how the system-level auth may be used before jumping into how the v0.3.0-like system of authorization can be implemented in v0.4.0. Firstly, the system-level auth works very well when all that we care about is the presence of some resource(s). As an example, Lets say that you have some method which you would like to make sure that only callers with an admin_badge may call the method. This can be easily done in v0.4.0. The following is some sample code to demonstrate how this may be done:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

use scrypto::prelude::*;

blueprint! {
    struct Sample {}

    impl Sample {
        pub fn new() -> ComponentAddress {
            // Setting up the auth
            let admin_badge: ResourceAddress = ...;
            let auth: AccessRules = AccessRules::new()
                .method("authenticated_method", rule!(require(admin_badge))) (1)
                .default(rule!(allow_all)); (2)

            return Self{}
                .instantiate()
                .add_access_check(auth)
                .globalize();
        }

        pub fn authenticated_method(&self) {
            // Some code goes here.
        }
    }
}
1 Specifying that only the admin_badge may call the authenticated_method.
2 Specifying that the system should allow all access to methods which we did not specify an access rule for.
use scrypto::prelude::*;

blueprint! {
    struct Sample {
        admin_badge: Address
    }

    impl Sample {
        pub fn new() -> ComponentAddress {
            let admin_badge: ResourceAddress = ...;
            return Self{
                admin_badge
            }
            .instantiate();
        }

        #[auth(admin_badge)]
        pub fn authenticated_method(&self) {
            // Some code goes here.
        }
    }
}

This new system-level auth allows for things which had not been easily possible in v0.3.0. As an example, let’s say that we wish to restrict access to the authenticated_method such that it may only be called if a supervisor_badge and an admin_badge are present. Alternatively, it the superadmin_badge alone is present then the method may be called. This can be easily done in v0.4.0 of Scrypto but requires a lot of work to setup in v0.3.0:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

use scrypto::prelude::*;

blueprint! {
    struct Sample {}

    impl Sample {
        pub fn new() -> ComponentAddress {
            // Setting up the auth
            let admin_badge: ResourceAddress = ...;
            let supervisor_badge: ResourceAddress = ...;
            let superadmin_badge: ResourceAddress = ...;
            let auth: AccessRules = AccessRules::new()
                .method("authenticated_method", rule!( (require(admin_badge)) && require(supervisor_badge))) || require(superadmin_badge)) ) (1)
                .default(rule!(allow_all)); (2)

            return Self{}
                .instantiate()
                .add_access_check(auth)
                .globalize();
        }

        pub fn authenticated_method(&self) {
            // Some code goes here.
        }
    }
}
use scrypto::prelude::*;

blueprint! {
    struct Sample {
        admin_badge: Address,
        supervisor_badge: Address,
        superadmin_badge: Address
    }

    impl Sample {
        pub fn new() -> Component {
            let admin_badge: Address = ...;
            let supervisor_badge: Address = ...;
            let superadmin_badge: Address = ...;
            return Self{
                admin_badge
            }
            .instantiate();
        }

        pub fn authenticated_method(
            &self,
            badges: Vec<BucketRef>
        ) {
            let is_admin_present: bool = badges.iter().any(|&x| x.resource_address() == self.admin_badge && x.amount() >= dec!("1"));
            let is_supervisor_present: bool = badges.iter().any(|&x| x.resource_address() == self.supervisor_badge && x.amount() >= dec!("1"));
            let is_superadmin_present: bool = badges.iter().any(|&x| x.resource_address() == self.superadmin_badge && x.amount() >= dec!("1"));

            assert!((is_admin_present && is_supervisor_present) || is_superadmin_present, "Unauthorized!");
            // Some code goes here.
        }
    }
}

The previous example makes it abundantly clear how powerful this new system of auth is. It requires less code, and allows for rules to be defined in a very human readable format.

There are certain cases where its important to use the v0.3.0-like auth system. As we’ve previously mentioned, the Auth-Zone-based authorization system is a perfect fit for when your applications only care about the presence of certain badge(s). However, when your application is required to read information from the badge then passing a Proof by intent is what you need here. The following is an example of how this may be done in this version of Scrypto:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

use scrypto::prelude::*;

blueprint! {
    struct Sample {}

    impl Sample {
        pub fn new() -> ComponentAddress {
            return Self{}.instantiate().globalize();
        }

        pub fn authenticated_method(&self, auth: Proof) {
            assert_eq!(auth.resource_address() == admin_badge, "Invalid badge passed to method.");
            assert!(auth.amount() >= dec!("1"), "Invalid badge amount passed.");
            // Some code goes here.
        }
    }
}
use scrypto::prelude::*;

blueprint! {
    struct Sample {}

    impl Sample {
        pub fn new() -> Component {
            return Self{}.instantiate();
        }

        #[auth(admin_badge)]
        pub fn authenticated_method(&self) {
            // Some code goes here.
        }
    }
}

To conclude this, there is a single question which you need to ask yourself to determine which authorization system fits your needs best when porting your code. Does this method/function require just the presence of a badge or does it also require reading the data on the badge?

  • If the presence of certain badge(s) is the only thing that you care about then the auth-zone-based authorization system should be a good fit for your needs.

  • If the data of certain badge(s) is what you need then passing Proofs by intent to a function/method should be a good fit for your needs.

Resources

Creating Resources

There has been two main changes made with regard to how resources are created in v0.4.0. Everything about how resources are created is identical except for the following:

  • Setting the divisibility of fungible resources is now done through a call to .divisibility when building the resource.

  • Flags as well as mutable flags have been removed in favor of a new system of defining the resource behavior.

  • The initial supply of a resource is now specified through a call to .initial_supply() instead of .initial_supply_fungible and initial_supply_non_fungible.

In v0.3.0, the divisibility of a fungible resources was specified as an argument when calling the ResourceBuilder::new_fungible function. However with v0.4.0, to specify the divisibility you need to add an additional chained method call to .divisibility to specify it. As an example:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let my_tokens: Bucket = ResourceBuilder::new_fungible()
    .metadata("name", "New Token")
    .metadata("symbol", "NT")
    .divisibility(7)
    .initial_supply(10);
let my_tokens: Bucket = ResourceBuilder::new_fungible(7)
    .metadata("name", "New Token")
    .metadata("symbol", "NT")
    .initial_supply_fungible(10);
Calling .divisibility when creating a new resource is optional. If it is not called, then the resource divisibility is set to the default value of 18.

In v0.3.0 the behavior of resources was controlled through flags such as MINTABLE, BURNABLE and so on. V0.4.0 removes the system of resource flags in favor of a more powerful system of specifying the behavior of resources. As an example, let’s say that we would like to create a new token which may only be minted and burned by some admin badge, the following is an example of how that may be done in v0.4.0:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let my_tokens: Bucket = ResourceBuilder::new_fungible()
    .metadata("name", "New Token")
    .metadata("symbol", "NT")
    .divisibility(7)
    .mintable(rule!(require(admin_badge.resource_address())), LOCKED)
    .burnable(rule!(require(admin_badge.resource_address())), LOCKED)
    .initial_supply(10);
let my_tokens: Bucket = ResourceBuilder::new_fungible(7)
    .metadata("name", "New Token")
    .metadata("symbol", "NT")
    .flags(MINTABLE | BURNABLE)
    .badge(admin_badge.resource_def(), MAY_MINT | MAY_BURN)
    .initial_supply_fungible(10);

The above example is the equivalent of specifying the flags (not mutable flags) on a resource in order to specify how this resource behaves or what kind of actions are allowed for this resource. In this example, this resource is mintable and will remain mintable forever as we’ve specified the mutability of mintable to be LOCKED, meaning that it wont change at any point in the future.

If we wish to create a resource which is mintable at the current moment of time, but can be made non-mintable in the future (essentially mimicking the mutable flags behavior from v0.3.0), we can do it by changing the LOCKED argument to MUTABLE and specifying the badge that is allowed to change the resource from mintable to non-mintable and vice-versa, as an example:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let my_tokens: Bucket = ResourceBuilder::new_fungible()
    .metadata("name", "New Token")
    .metadata("symbol", "NT")
    .divisibility(7)
    .mintable(
        rule!(require(admin_badge.resource_address())), (1)
        MUTABLE(rule!(require(admin_badge.resource_address()))) (2)
    )
    .initial_supply(10);
1 Specifies that this resource may be minted with the admin_badge.
2 Specifies that the admin_badge may update the mintable rule in the future.
let my_tokens: Bucket = ResourceBuilder::new_fungible(7)
    .metadata("name", "New Token")
    .metadata("symbol", "NT")
    .flags(MINTABLE)
    .badge(
        admon_badge.resource_def(),
        MAY_MANAGE_RESOURCE_FLAGS
    )
    .badge(admin_badge.resource_def(), MAY_MINT)
    .initial_supply_fungible(10);

Just like in v0.3.0, multiple badges can have the authorization to mint this resource. As an example:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let my_tokens: Bucket = ResourceBuilder::new_fungible()
    .metadata("name", "New Token")
    .metadata("symbol", "NT")
    .divisibility(7)
    .mintable(
        rule!( require(admin_badge.resource_address()) || require(supervisor_badge.resource_address()) ), (1)
        MUTABLE(rule!(require(admin_badge.resource_address()))) (2)
    )
    .initial_supply(10);
1 Specifies that this resource may be minted with the admin_badge or the supervisor_badge.
2 Specifies that the admin_badge may update the mintable rule in the future.
let my_tokens: Bucket = ResourceBuilder::new_fungible(7)
    .metadata("name", "New Token")
    .metadata("symbol", "NT")
    .flags(MINTABLE)
    .badge(
        admon_badge.resource_def(),
        MAY_MANAGE_RESOURCE_FLAGS
    )
    .badge(
        supervisor_badge.resource_def(),
        MAY_MANAGE_RESOURCE_FLAGS
    )
    .badge(admin_badge.resource_def(), MAY_MINT)
    .initial_supply_fungible(10);

The two examples above only showcase how the minting behavior of a resource may be set. Other resource behavior can be controlled through similar methods defined on the ResourceBuilder that may be called in the same way and that accept the same arguments.

The set of methods which could be used on the resource builder in order to change the behavior of a resource, their v0.3.0 equivalent flags, and default behavior is:

v0.4.0 Method Name

Equivalent v0.3.0 Flag

Behavior When Not Specified

.mintable

MINTABLE

When not specified, the resource is not mintable.

.burnable

BURNABLE

When not specified, the resource is not burnable.

.restrict_deposit

When not specified, the resource may be deposited into any vault.

.restrict_withdraw

RESTRICTED_TRANSFER

When not specified, the resource may be withdrawn from any vault.

.updateable_metadata

SHARED_METADATA_MUTABLE

When not specified, the shared resource metadata may not be updated.

.updateable_non_fungible_data

INDIVIDUAL_METADATA_MUTABLE

When not specified, the individual resource metadata may not be updated.

Managing Resources

Everything that had been possible to do through a ResourceDef in v0.3.0 is now possible to do through a ResourceManager. This is a direct rename of the same underlying concept with a few changes made to it.

With the v0.3.0 system of flags, resources with mutable flags could have their flags changed at any point of time thus changing their properties and behavior. This can be done in v0.4.0 through the appropriate methods on the ResourceManager (formerly known as ResourceDef).

As an example, let’s say that there is some mintable token which we wish to make non-mintable. We can do this by calling the appropriate methods on the ResourceManager of the resource as such:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let admin_badge: Bucket = ... // Some bucket with your admin badge
let my_tokens: Bucket = ... // Some bucket with your tokens (to get the resource address)
let my_resource_manager: &ResourceManager = borrow_resource_manager!(my_tokens.resource_address());
self.admin_badge.authorize(|| {
    my_resource_manager.set_mintable(rule!(deny_all));
})
let admin_badge: Bucket = ... // Some bucket with your admin badge
let my_tokens: Bucket = ... // Some bucket with your tokens (to get the resource address)
let my_resource_def: ResourceDef = ResourceDef::from(my_tokens.resource_address());
my_resource_def.disable_flags(MINTABLE, admin_badge.present());

As you can see from the previous example, the way to obtain the ResourceManager (formerly known as ResourceDef) for a given resource address, is by using the borrow_resource_manager! macro which returns a reference to the ResourceManager of a given ResourceAddress.

You can see from the two previous examples that when we wanted to make the resource mintable, we called the .mintable method on the ResourceBuilder while creating the resource. When wanting to change or modify whether the resource is mintable or not, we called the .set_mintable method on the ResourceManager of the resource. A full table of the behavior altering methods on the ResourceBuilder as well as their setters on the ResourceManager is shown below:

ResourceBuilder Method

ResourceManager Setter Method

.mintable

.set_mintable

.burnable

.set_burnable

.restrict_deposit

.set_depositable

.restrict_withdraw

.set_withdrawable

.updateable_metadata

.set_updateable_metadata

.updateable_non_fungible_data

.set_updateable_non_fungible_data

A resource’s ResourceManager also allows for tokens to be minted, burned, have their metadata modified, and so on. Here is an example of how tokens may be minted in v0.4.0:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let admin_badge: Bucket = ... // Some bucket with your admin badge
let my_tokens: Bucket = ... // Some bucket with your tokens (to get the resource address)
let my_resource_manager: &ResourceManager = borrow_resource_manager!(my_tokens.resource_address());
self.admin_badge.authorize(|| {
    my_resource_manager.mint(10);
})
let admin_badge: Bucket = ... // Some bucket with your admin badge
let my_tokens: Bucket = ... // Some bucket with your tokens (to get the resource address)
let my_resource_def: ResourceDef = ResourceDef::from(my_tokens.resource_address());
my_resource_def.mint(10, admin_badge.present());

Transaction Manifests

Transaction manifests had a number of substantial changes made to them with v0.4.0 and the introduction of the system-based authorization system. In v0.3.0 the transaction worktop used to hold resources, buckets, and badges (in the form of BucketRef). With v0.4.0 comes a new section in the transaction worktop called the Auth Zone which is primarily used to hold Proofs (formerly called BucketRefs).

The introduction of this new section of the transaction worktop introduced the need for more transaction manifest instructions and for revisions on the existing instructions. Overall, the changes made to transaction manifests are as follows:

  • Introduced the Auth Zone which is a new section in the transaction worktop which holds Proofs to later use them for authorization.

  • Added, removed, and renamed some instructions.

  • Some data types have been renamed similar to their Scrypto counterparts.

The introduction of the Auth Zone has made transactions require a fewer number of lines. Recall that in v0.3.0 when needing to withdraw multiple resources from an account, you would need to clone the signers' virtual badges as many times as withdrawals from the accounts. This changes with v0.4.0 where the signers' virtual badges are put in the Auth Zone as soon as the transaction begins. This means that you can now withdraw multiple resources from your account without the need to clone the virtual badge multiple times which makes the manifest much smaller. As an example:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

CALL_METHOD
    ComponentAddress("account component address")
    "withdraw_by_amount" Decimal("5.0")
    ResourceAddress("resource address"); (1)
CALL_METHOD
    ComponentAddress("account component address")
    "withdraw_by_amount"
    Decimal("5.0")
    ResourceAddress("resource address");
1 There is no longer a need to specify your virtual badge when performing withdrawals. The auth for this method is automatically handled through the Auth Zone.
CLONE_BUCKET_REF
    BucketRef(1u32)
    BucketRef("withdrawal_badge_1");
CLONE_BUCKET_REF
    BucketRef(1u32)
    BucketRef("withdrawal_badge_2");
CALL_METHOD
    Address("account component address")
    "withdraw"
    Decimal("5.0")
    Address("resource address")
    BucketRef("withdrawal_badge_1");
CALL_METHOD
    Address("account component address")
    "withdraw"
    Decimal("5.0")
    Address("resource address")
    BucketRef("withdrawal_badge_2");

One thing that is very relevant to transactions manifests which you might’ve noticed from the previous example is that when withdrawing some funds from the account, v0.4.0 uses the withdraw_by_amount method whereas v0.3.0 uses the withdraw method. This is because the v0.4.0 Account blueprint has had a number of renames and added methods. The following is a table of the relevant Account component methods which are relevant in the context of transaction manifests:

Name

Description

account.withdraw

This method withdraws the entirety of a given resource from the account.

account.withdraw_by_amount

This method withdraws some amount of a given resource from the account.

account.withdraw_by_ids

This method withdraws resources with given non-fungible ids of a given resource from the account.

account.create_proof

This method creates a proof with the entirety of a given resource from the account.

account.create_proof_by_amount

This method creates a proof with some amount of a given resource from the account.

account.create_proof_by_ids

This method creates a proof with resources with given non-fungible ids of a given resource from the account.

It is important to note that all Buckets returned from functions/methods are deposited into the transaction worktop whereas all returned Proofs are deposited into the Auth Zone.

Name Changes

The following are all of the name changes that have come to transaction manifests both to instructions and data types:

v0.3.0 Name

v0.4.0 Name

BucketRef

Proof

NonFungibleKey

NonFungibleId

TAKE_FROM_WORK_TOP

TAKE_FROM_WORK_TOP_BY_AMOUNT

TAKE_ALL_FROM_WORKTOP

TAKE_FROM_WORK_TOP

TAKE_NON_FUNGIBLES_FROM_WORKTOP

TAKE_FROM_WORK_TOP_BY_IDS

ASSERT_WORKTOP_CONTAINS

ASSERT_WORKTOP_CONTAINS_BY_AMOUNT

The Address type has also been split into three main types: PackageAddress, ComponentAddress, and ResourceAddress which (as the name suggests) are used to address packages, components, and resources respectively. This means that as of v0.4.0, Address is no longer an accepted type.

Testing

There has been a few changes made to unit tests in v0.4.0. These changes are as follows:

  • When creating a new account through executor.new_account(), a new keypair and component address are generated for you.

  • Code required to publish a package has changed.

  • The TransactionBuilder now does not require a reference to the executor.

  • Transactions now require to be signed before they can be executed.

  • The resim style of creating buckets in tests is no longer valid and has been replaced with a more programmatic way of creating buckets and proofs.

Creating a New Account

The way in which new accounts are created in unit tests has been changed slightly. Previously, accounts were created by first creating a new public key and then creating a new account component from this public key. This is no longer the case as of v0.4.0 where executor.new_account() now generates a new key-pair and their corresponding component address.

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let (public_key, private_key, account_component_address) = executor.new_account();
let public_key = executor.new_public_key();
let account = executor.new_account(public_key);

Publishing a Package

The way in which packages are published in unit tests has had slight changes made to it. The include_code!("package-name") macro has been replaced with the compile_package!() macro which compiles the package code and returns it as a slice of bytes. Aside from this macro change, the method used on the executor to publish packages is still the same.

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let package: PackageAddress = executor.publish_package(compile_package!()).unwrap();
let package: Address = executor.publish_package(include_code!("package-name")).unwrap();

Building Transactions

Building transactions has hade a few changes made to it in this version. First of all, the transaction builder no longer requires a reference to the executor to run. Secondly, transactions now need to be signed before being executed. Thirdly, call-function no longer requires an account to be specified. Lastly, the resim style of specifying buckets is no longer valid and has been changed with a more programmatic approach.

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let token_creation_tx: SignedTransaction = TransactionBuilder::new() (1)
    .new_token_fixed(token_information, dec!("10000000"))
    .call_method_with_all_resources(account_component_address, "deposit_batch")
    .build(executor.get_nonce([public_key])) (2)
    .sign([&private_key]); (3)
1 There is no longer a need to provide a reference to the executor.
2 The public keys of the signers is now used to get the nonce used in the transaction from the intended signers.
3 Transactions now need to be signed with the private keys of the signers.
let token_creation_tx: Transaction = TransactionBuilder::new(&executor)
    .new_token_fixed(token_information, dec!("10000000"))
    .call_method_with_all_resources(account, "deposit_batch")
    .build(vec![public_key])
    .unwrap();

The resim style of specifying buckets is no longer valid and has been changed with a more programmatic approach. Meaning that an argument of 10,030000000000000000000000000000000000000000000000000004 is no longer a valid argument in scrypto unit tests. Instead, funds should be withdrawn from the payer’s account, and a bucket should be created from the withdrawn funds. Let’s say that we would like to call some function called new on some blueprint called Foo with 10 XRD. We can do that in v0.4.0 unit tests by:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let amount: Decimal = dec!("10");
let resource_address: ResourceAddress = RADIX_TOKEN;

let transaction: SignedTransaction = TransactionBuilder::new()
    .withdraw_from_account_by_amount(amount, resource_address, account_component_address)
    .take_from_worktop(resource_address, |builder, bucket_id| {
        builder.call_function(
            package,
            "Foo",
            "new",
            args![scrypto::resource::Bucket(bucket_id)], (1)
        )
    })
    .call_method_with_all_resources(account_component_address, "deposit_batch")
    .build(executor.get_nonce([public_key]))
    .sign([&private_key]);
1 Creating a bucket from the bucket_id containing the 10 XRD.
let amount: Decimal = dec!("10");
let resource_address: ResourceAddress = RADIX_TOKEN;

let transaction: Transaction = TransactionBuilder::new(&executor)
    .call_function(
        package,
        "Foo",
        "new",
        vec![format!("{}, {}", amount, resource_address)],
        Some(account_component_address)
    )
    .call_method_with_all_resources(account, "deposit_batch")
    .build(vec![public_key])
    .unwrap();

Running Transactions

Transactions may now be run through the validate_and_execute method on the executor instead of the run method. This method validates the signatures on the transaction before executing it and returning a receipt.

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let transaction: SignedTransaction = ... // Some transaction.
let receipt: Receipt = executor.validate_and_execute(&transaction).unwrap();
let transaction: Transaction = ... // Some transaction.
let receipt: Receipt = executor.run(receipt).unwrap();

Getting Newly Created Entities

A lot of transactions result in the creation of new packages, components, or resources. Let’s say that some transaction has created a number of new components for us. In v0.3.0, the address of the first newly created component would be obtained by receipt.component(0).unwrap(). However, in v0.4.0, the address of the first newly created component is obtained by receipt.new_component_addresses[0]. This is the same for newly created packages as well as newly created resources.

Miscellaneous

A number of miscellaneous changes which should be kept in mind when migrating your codebase to v0.4.0

  • The BigDecimal data type has been removed. Consider using the Decimal type.

  • Multi-signature transactions are now possible by specifying the --signing-keys option on the resim run command.

FAQ

How can I get the ResourceManager (formerly known as ResourceDef) for a ResourceAddress?

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let resource_address: ResourceAddress = RADIX_TOKEN;
let resource_manager: &ResourceManager = borrow_resource_manager!(resource_address);
let resource_address: Address = RADIX_TOKEN;
let resource_manager: ResourceDef = ResourceDef::new(resource_address);

How can I get a Component from it’s ComponentAddress (formerly known as Address)?

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let component_address: ComponentAddress = ...; // The address of some component
let component: &Component = borrow_component!(component_address);
let component_address: Address = ...; // The address of some component
let component: Component = Component::from(component_address);

How can I get a call a method on a Component given it’s ComponentAddress (formerly known as Address)?

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let component_address: ComponentAddress = ...; // The address of some component
let component: &Component = borrow_component!(component_address);
component.call::<()>("deposit", vec![scrypto_encode(&bucket)]);
let component_address: Address = ...; // The address of some component
let component: Component = Component::from(component_address);
component.call::<()>("deposit", vec![scrypto_encode(&bucket)]);

How can I convert a Component to its underlying component state?

  • Scrypto v0.4.0

  • Scrypto v0.3.0

let component_address: ComponentAddress = ...; // The address of some component
let my_component: MyBlueprint = component_address.into(); (1)
1 Replace MyBlueprint with the name of your blueprint
let component_address: Address = ...; // The address of some component
let component: Component = Component::from(component_address);
let my_component: MyBlueprint = component.into(); (1)
1 Replace MyBlueprint with the name of your blueprint

The #[auth(…​)] macro no longer works. What can I do about that?

We recommend that you first learn about the new authorization system introduced with v0.4.0 of Scrypto. If you’re adamant about using the v0.3.0-like system of performing auth, then follow the example below:

  • Scrypto v0.4.0

  • Scrypto v0.3.0

pub fn my_authenticated_method(&self, auth: Proof) {
    assert_eq!(auth.resource_address() == admin_badge, "Invalid badge passed to method.");
    assert!(auth.amount() >= dec!("1"), "Invalid badge amount passed.");

    // Your code goes here.
}
#[auth(admin_badge)]
pub fn my_authenticated_method(&self) {
    // Your code goes here.
}