Run Your First Project

All done with installing the Scrypto toolchain? Great, let’s walk through the steps of running and interacting with some Scrypto code in your simulator environment, starting with a new project.

The basic process involves creating a blueprint package with some blueprint Scrypto code, deploying it to your local simulator (as if deploying it to the Radix network’s blueprint catalog), instantiating a Component from the blueprint, and interacting with the Component via a simulated transaction.

Creating a New Blueprint Package

First, we’ll create a new blueprint package.

scrypto new-package tutorial

This will scaffold a simple package which contains a single Hello blueprint. Open the new package directory in an IDE with Rust support.

The Scrypto development team mostly uses VS Code, which has a free Rust extension (rust-lang) that works well.

Open up src/lib.rs to see the source that we’ll be compiling and deploying to the simulator.

code tutorial/src/lib.rs

Hello provides a function which instantiates a new Component with a new supply of tokens, and a method which allows the caller to get one of those tokens from the instantiated Component.

Creating an Account

Before we can deploy, we first need to create an account on the simulator. On Radix, accounts are themselves components, but resim provides commands to make it convenient to create and use accounts in the simulator environment.

Most of commands offered by the resim tool act within the context of an account.

resim new-account

You should get a success message, and at the bottom of the output you should see the created address and private key:

Account component address: 020d3869346218a5e8deaaf2001216dc00fcacb79fb43e30ded79a
Public key: 044083a64afb4b630ce7683674a6cdcebc7007aef7cb08f10b2cd491b6ce24ca1204f88bd2a2068e27591f1c5cfbd4fddf9a51f7b2360d784ee1e8fbec8f7476a6
Private key: 7c9fa136d4413fa6173637e883b6998d32e1d675f88cddff9dcbcf331820f4b8
No configuration found on system. will use the above xref:system/accounts.adoc[account] as default.

Please do not use these credentials on the main network, as your private key will be stored in plain text.

If you do not see the line about setting your default account, then you already created an account previously. Either reset your entire simulator with the resim reset command and try again, or set this new account as your default account with the

resim set-default-account <ACCOUNT_COMPONENT_ADDRESS> \
<PRIVATE_KEY>

command.

Save the address of your new account Component to an environment variable to make your life easier later. For example:

export account=020d3869346218a5e8deaaf2001216dc00fcacb79fb43e30ded79a
export pubkey=044083a64afb4b630ce7683674a6cdcebc7007aef7cb08f10b2cd491b6ce24ca1204f88bd2a2068e27591f1c5cfbd4fddf9a51f7b2360d784ee1e8fbec8f7476a6
export privkey=7c9fa136d4413fa6173637e883b6998d32e1d675f88cddff9dcbcf331820f4b8

On Windows, do

# Using the PowerShell
$account="020d3869346218a5e8deaaf2001216dc00fcacb79fb43e30ded79a"
$pubkey="044083a64afb4b630ce7683674a6cdcebc7007aef7cb08f10b2cd491b6ce24ca1204f88bd2a2068e27591f1c5cfbd4fddf9a51f7b2360d784ee1e8fbec8f7476a6"
$privkey="7c9fa136d4413fa6173637e883b6998d32e1d675f88cddff9dcbcf331820f4b8"

Displaying the Account

You can look at the resources on your new account by running:

resim show $account

You will get something like this:

Component: 020d3869346218a5e8deaaf2001216dc00fcacb79fb43e30ded79a
Blueprint: { package_address: 010000000000000000000000000000000000000000000000000003, blueprint_name: "xref:system/accounts.adoc[Account]" }
Authorization
├─ "deposit_batch" => AllowAll
└─ "deposit" => AllowAll
State: Struct(LazyMap("bc417218214859fbbf019072394c50cc53d5419f4acd7a660dc7c880f0cce31a02040000"))
Lazy Map: 020d3869346218a5e8deaaf2001216dc00fcacb79fb43e30ded79a(bc417218214859fbbf019072394c50cc53d5419f4acd7a660dc7c880f0cce31a, 1026)
└─ ResourceAddress("030000000000000000000000000000000000000000000000000004") => Vault("bc417218214859fbbf019072394c50cc53d5419f4acd7a660dc7c880f0cce31a03040000")
Resources:
└─ { amount: 1000000, resource address: 030000000000000000000000000000000000000000000000000004, name: "Radix", symbol: "XRD" }

This reads as:

Caption Description

All RadixDLT accounts are Component and as such they have a ComponentAddress.

Blueprint

A set of Component belong to a blueprint. A set of Blueprints belong to a package. Packages have addresses, just like Component.

Authorization

All Component have a set of authorization rules. Here everyone has access to the deposit and the deposit_batch methods on the users account. This means that everyone can send you any token.

State

Component have a struct that contain all the data they can work on. This complete set of data stored by the Component is called the state. Account Component' state is stored in a single LazyMap where keys are ResourceAddresses (token addresses), and values are the Vaults that can store those Resources on the Account's behalf.

Here is the list of all the resoruces (tokens) the Account owns. There are Fungible and Non-Fungible resources on the Radix DLT.

Deploying the Package

Next, we’ll publish our package:

cd tutorial
resim publish .

You should see the published package's address in the output. Save that address in the $package environment variable.

export package=<package_address_returned_by_resim_publish>

Component Instantiation

Now that our package is deployed "on ledger" in the simulator, we will investigate the Hello Component.

// This is a function, and can be called directly on the blueprint once deployed
pub fn instantiate_hello() -> ComponentAddress {
  // Create a new token called "HelloToken," with a fixed supply of 1000, and put that supply into a bucket
  let my_bucket: xref:scrypto-lang/vaults-and-buckets.adoc[Bucket] = ResourceBuilder::new_fungible()
    .metadata("name", "HelloToken")
    .metadata("symbol", "HT")
    .initial_supply(1000);

  // Instantiate a Hello component, populating its vault with our supply of 1000 HelloToken
  Self {
    sample_vault: Vault::with_bucket(my_bucket)
  }
  .instantiate()
  .globalize()
}

instantiate_hello() is a method that creates a new Component and returns its address. In detail:

  1. It creates a new Bucket with a Fungible Token of an initial supply of 1000.

  2. It creates a new Vault and puts the Bucket into it.

  3. It calles the instantiate method on the Component that will create the Component without an Address,

  4. It calls the globalize method on the Component to make it globally accessible adding an Address to it.

Now we will instantiate a Hello Component by calling the new function on the Hello blueprint.

resim call-function $package Hello instantiate_hello

The output should look something like this:

Transaction Status: SUCCESS
Execution Time: 25 ms
Instructions:
├─ CallFunction { package_address: 01a1e4ab9ff224f540ac0cc15ca74cc0ee89c79992afbc60c6716b, blueprint_name: "Hello", function: "instantiate_hello", args: [] }
└─ CallMethodWithAllResources { component_address: 020d3869346218a5e8deaaf2001216dc00fcacb79fb43e30ded79a, method: "deposit_batch" }
Instruction Outputs:
├─ ComponentAddress("027dfeaa7617bcab867273dab69be7dfa26cd3694ccef26c75dfb8")
└─ ()
Logs: 0
New Entities: 2
└─ xref:scrypto-lang/blueprints-and-components.adoc[Component]: 027dfeaa7617bcab867273dab69be7dfa26cd3694ccef26c75dfb8
└─ Resource: 03315d52f961068c65758c554b76f08d48713a723b1636c5a5363e

This reads as:

Caption

Description

Transaction Status

Transaction was successful.

Execution Time

The transaction took 25 milliseconds to execute.

Instructions

The transaction executed contained one CallFunction instruction, that called instantiate_hello function which in turn issued a CallMethodWithAllResources instruction when the HelloToken was created.

Instruction Outputs

The CallFunction instruction returned the ComponentAddress of the new HelloToken Component and the CallMethodWithAllResources instruction returned the empty tuple.

Logs: There were no xref

scrypto-lang/logging.adoc[logs].

New Entities

There were two new entities created the Hello Component and the HelloToken Resource.

This will create two new entities with two different addresses:
  • a new HelloToken with a ResourceAddress,

  • and your fresh Hello Component having a ComponentAddress

Save this to the $component environment variable:

export component=<component_address_returned_by_resim_call_function>

On Radix, tokens and other assets are created as system-level resources, not blueprints, and the resources' definition contain the parameters for the supply of resource you have requested from the system.

You can investigate these new addresses, if you wish:

resim show <address>

Component Call

First let’s investigate the free_token method:

// This is a method, because it needs a reference to self.  Methods can only be called on components
pub fn free_token(&mut self) -> xref:scrypto-lang/vaults-and-buckets.adoc[Bucket] {
    info!("My balance is: {} HelloToken. Now giving away a token!", self.sample_vault.amount());
    // If the semi-colon is omitted on the last line, the last value seen is automatically returned
    // In this case, a bucket containing 1 HelloToken is returned
    self.sample_vault.take(1)
}

The following is done in the free_token method:

  1. It logs an info message with the current balance of the HelloToken.

  2. It takes a Bucket with one of the HelloToken from the Vault and returns it to the caller.

  3. The Vault now has one less HelloToken in it.

All Buckets must be returned to a Vault by the end of the transaction, otherwise the system will return an error.

Next, we’ll call the free_token method on our new Component.

resim call-method $component free_token

This requires a different resim command than the one we just used since we are now calling a method on an instantiated Component instead of the function of a blueprint.

The output should look something like this:

Transaction Status: SUCCESS
Execution Time: 18 ms
Instructions:
├─ CallMethod { component_address: 027dfeaa7617bcab867273dab69be7dfa26cd3694ccef26c75dfb8, method: "free_token", args: [] }
└─ CallMethodWithAllResources { component_address: 020d3869346218a5e8deaaf2001216dc00fcacb79fb43e30ded79a, method: "deposit_batch" }
Instruction Outputs:
├─ xref:scrypto-lang/vaults-and-buckets.adoc[Bucket](1024u32)
└─ ()
Logs: 1
└─ [INFO ] My balance is: 1000 HelloToken. Now giving away a token!
New Entities: 0

This reads as:

Caption Description

Transaction status

Transaction was successful.

Execution Time

The transaction took 18 milliseconds to execute.

Instructions

The transaction executed contained one CallMethod instruction, that called free_token method which in turn issued a CallMethodWithAllResources instruction when the HelloToken was created.

Instruction outputs

The CallMethod instruction returned a Bucket containing one HelloToken and the CallMethodWithAllResources instruction returned the empty tuple.

Logs

There was one log message. The message was: [INFO ] My balance is: 1000 HelloToken. Now giving away a token!

And now you have a shiny new HelloToken in your account, and your Hello Component has one less. You can verify this with some investigation using resim show on each Component.

You can create more accounts, and change which one you’re acting as, by running:

resim set-default-account <ACCOUNT_COMPONENT_ADDRESS> \
<PRIVATE_KEY>

Final Notes

If you make changes to the structs within your code, then unfortunately you will have to run through the entire publish-instantiate-call flow from scratch, saving the new addresses as they appear. If you only make implementation changes then it is possible to update your package with:

resim publish . --address $package

At any point you can instantly get a clean slate in the simulator by running:

resim reset

You almost certainly need to do this if you switch to working on a different project.

If you ever pull the latest from the Scrypto GitHub repo and see that the core Scrypto implementation has changed (i.e. stuff above the examples directory), you will usually need to re-run:

cd radixdlt-scrypto
cargo install --path ./simulator

in order to pick up the changes. And sometimes you will also need to run

resim reset

to clear the simulator state.

That concludes this most basic tutorial on using resim. More content to come!