Vaults and Buckets

Because Radix and Scrypto are all about safe resource handling, we introduced the concept of resource containers. There are two types of resource containers: Bucket and Vault.

  • Bucket - Buckets are temporary containers and are used to move resources during a transaction; therefore, only exist in the duration of the transaction.

  • Vault - Vaults are permanent containers and at the end of each transaction, all resources must be stored in a Vault.

Buckets are transient resource containers which are used to move resources from one Vault to another. As such, buckets are dropped at the end of the transaction and the resource held by the Bucket must be stored in a permanent resource container such as a Vault.

Both buckets and vaults have special methods built into them which are there to facilitate resource movements. For example, you can call a .take(amount) method on a Vault to withdraw the resource from the Vault and place it into a Bucket. You can then call the .put(amount) method on the Bucket to transfer the resource contained within the Bucket to another Vault.

This is a concept understood at the platform level which allows the system to behave in a predictable manner when moving and storing resources. We’ll explore and explain what this means and how these concepts are applied when working with Scrypto.

Buckets

Whenever you are moving resources around, for example sending tokens to someone, you will put those tokens in a bucket and send that bucket to the destination. This allows you to group a certain amount of tokens together. It also allows the system to makes sure that while resources are moving, they cannot end in undesirable or unpredictable states. Below is a table of methods available to work with buckets and you can use these methods to manage asset movements within your blueprint.

Bucket Methods

Method

Description

.new(ResourceAddress)

Creates a new Bucket that accepts the resource specified by the ResourceAddress.

.drop_empty()

.burn()

Burns (or destroys) the resource contained within the Bucket.

`.create_proof()

Creates a single Proof of the resource and automatically pushes it to the authzone.

.create_proof_of_amount(amount)

.create_proof_of_all()

.resource_manager()

Retrieves the ResourceManager of resource contained within the Bucket.

.take(amount)

Take a quantity of tokens and return a new Bucket containing them.

.put(bucket)

Take a Bucket and put its content into the one on which this method is called.

.take_non_fungibles(BTreeSet<NonFungibleLocalId>)

Take multiple non-fungible tokens from the Bucket. This returns a new Bucket with the specified NFTs.

.take_non_fungible(NonFungibleId)

Takes a specific NFT from the Bucket and returns a new Bucket that contains it.

Note: this method panics if there are more than one NFT in the Bucket

.amount()

Returns the amount of tokens stored in the Bucket.

.resource_address()

Returns the address of the resource stored in the Bucket.

.is_empty()

Returns true if the Bucket is empty, false otherwise.

.drop_empty()

If the Bucket is empty, the Bucket is dropped. Otherwise, an error will be returned.

.non_fungibles()

This returns a vector containing the data of each NFT present in the Bucket.

.non_fungible()

Returns the data of the NFT that the Bucket contains.

Note: this method panics if there is more than one NFT in the Bucket.

.authorize(function)

Authorizes an action by putting the badges present in the Bucket on the AuthZone before running the specified function.

Note: more information about this here.

Vaults

Buckets are used to store resources while they are moving during a transaction. On the other hand, vaults are used to store resources for longer-term in-between transactions. The distinction between these two containers allows the system to make sure no resources are ever lost if, for example, someone is withdrawing tokens from their account but forgets to insert them into another vault.

The most straightforward way to illustrate the concept of vaults is with the account components. Each account component contains a vault for each resource type it owns, as seen in its state definition:

struct Account {
  vaults: KeyValueStore<ResourceAddress, Vault>
}

When an account receives a new token (e.g. through a call to one of its deposit methods), it checks if it already instantiated a vault of this particular resource type. If a vault for the received resource type does not exist, one is created and the received tokens are stored in it.

You can visualize the vaults and resources present on an account component (or any component) by using the resim show [component_address] command as shown bellow.

> resim new-account
> resim new-token-fixed --symbol BTC --name Bitcoin 21000000
> resim show [account_address]

Component: account_sim1q0esun0yw3y6h4glaea4fds5kzd3j0hayqxpec589m3s0kjz2g
Access Rules
State: Tuple(KeyValueStore("b825ef8e0e15fe71d85b7e3f4adb4c0c8bb9e9902fdb52ff4d7c73219c7d286403040000"))
Key Value Store: AccountComponent[03f30e4de47449abd51fee7b54b614b09b193efd200c1ce2872ee3][...]
├─ ResourceAddress("resource_sim1qzkcyv5dwq3r6kawy6pxpvcythx8rh8ntum6ws62p95sqjjpwr") => Vault("b825ef8e0e15fe71d85b7e3f4adb4c0c8bb9e9902fdb52ff4d7c73219c7d286405040000")
└─ ResourceAddress("resource_sim1qrgs0ge3nm2sh6fc09rtfgzcx4jfvk6uk77t9sqdu66qrs6z50") => Vault("93a0a362a5aa37e847e5b9a94343061ee6738f19ad81f5ce04b4558cd6fbd2f705040000")
Resources:
├─ { amount: 1000, resource address: resource_sim1qzkcyv5dwq3r6kawy6pxpvcythx8rh8ntum6ws62p95sqjjpwr, name: "Radix", symbol: "XRD" }
└─ { amount: 21000000, resource address: resource_sim1qrgs0ge3nm2sh6fc09rtfgzcx4jfvk6uk77t9sqdu66qrs6z50, name: "Bitcoin", symbol: "BTC" }

In the Key Value Store section, you can see the mapping between the resource addresses and their corresponding Vault. Resim then displays the resource list in a user-friendly format in the Resources section.

Creating Vaults

In Scrypto, there are two main ways for creating a Vault:

Function

Description

Vault::new(ResourceAddress)

This creates a new empty Vault that will be used to stored resources of the specified ResourceAddress.

Vault::with_bucket(Bucket)

This is a shortcut for creating a new Vault and inserting a Bucket of tokens inside. The ResourceAddress is inferred from the passed Bucket.

Here is a simple example:

use scrypto::prelude::*;

#[blueprint]
mod my_module {
    struct MyBlueprint {
        my_vault: Vault
    }

    impl MyBlueprint {
        pub fn instantiate(tokens: Bucket) -> Global<MyBlueprint> {
            Self {
                // Create a Vault from the provided bucket
                // and store in on the component state
                my_vault: Vault::with_bucket(tokens)
            }.instantiate().globalize()
        }
    }
}

Just like buckets have to be deposited into a vault by the end of a transaction, Vaults must be stored on a component state by the end of a transaction.

Vault Methods

Method

Description

.put(bucket)

.amount()

.resource_address()

.take(amount)

Returns a Bucket containing the specified quantity of the resources present in the Vault.

.take_non_fungible(NonFungibleId)

Returns a Bucket with a NFT of the specified ID taken from the vault.

.take_all()

Returns a Bucket containing all the resources present in the Vault.

.lock_fee(amount)

Lock fee to pay for the transaction.

Note: this panics if the vault contains a resource different than XRD. More information on fees here.

.authorize(function)

Authorizes an action by putting the badges present in the vault on the AuthZone before running the specified function.

Note: more information about this here.