Buckets and Vaults
  • 08 Oct 2024
  • 6 Minutes to read
  • Dark
    Light
  • PDF

Buckets and Vaults

  • Dark
    Light
  • PDF

Article summary

Because Scrypto is about safe resource handling, we introduce the concept of resource containers. There are two types of resource containers: Buckets and Vaults.

  • Bucket - A bucket is a temporary container used to move resources during a transaction; therefore, it only exist in the duration of the transaction.

  • Vault - A vault is a permanent container 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 vault or burned.

pub fn vault_to_vault(&mut self) {
  let bucket: Bucket = self.vault.take(dec!("1"));
  self.other_vault.put(bucket);
}

Fungible/NonFungible

Buckets and vaults may be further categorized to be Fungible or NonFungible adding four more types of resource containers:

  • FungibleBucket - A bucket containing an amount of some fungible resource.

  • NonFungibleBucket - A bucket containing an amount of some non-fungible resource.

  • FungibleVault - A vault containing an amount of some fungible resource.

  • NonFungibleVault - A vault containing an amount of some non-fungible resource.

These more specific types allows one to call more specialized methods for the given resource type:

pub fn display_non_fungibles(bucket: Bucket) -> Bucket {
  let non_fungible_bucket: NonFungibleBucket = bucket.as_non_fungible();
  println!("{:?}", non_fungible_bucket.non_fungibles());
  non_fungible_bucket.into()
}

Note that the above will panic if the passed in bucket is actually of fungible type.

Buckets

Buckets and it’s Fungible/NonFungible variants are the main mechanism through which assets move. Below is a table of methods available to work with buckets.

Bucket Methods

Method

Description

.burn()

Burns (or destroys) the resource contained within the bucket

.create_proof_of_all()

Creates a Proof of all the resources in the bucket

.resource_address()

Returns the address of the resource stored in the bucket

.resource_manager()

Returns the ResourceManager of the resource contained within the bucket

.amount()

Returns the amount of tokens stored in the bucket

.is_empty()

Returns true if the Bucket is empty, false otherwise.

.put(bucket)

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

.take(amount)

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

.take_advanced(amount, withdraw_strategy)

Take a quantity of tokens with a certain withdraw strategy and return a new Bucket containing them.

.drop_empty()

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

.as_fungible()

Convert the bucket into a FungibleBucket.

Note: This method panics if the underlying resource is not a fungible resource.

.as_non_fungible()

Convert the bucket into a NonFungibleBucket.

Note: This method panics if the underlying resource is not a non fungible resource.

.authorize_all(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.

FungibleBucket Methods

A FungibleBucket inherits all of the Bucket methods with the following additional methods:

Method

Description

.create_proof_of_amount(amount)

Creates a Proof of a certain amount of the resources in the bucket.

.authorize_with_amount(amount, function)

Authorizes an action by putting a proof of certain amount present in the Bucket on the AuthZone before running the specified function.

NonFungibleBucket Methods

A NonFungibleBucket inherits all of the Bucket methods with the following additional methods:

Method

Description

.non_fungibles()

Returns a vector containing the data of each NFT present in the bucket.

.take_non_fungibles(IndexSet<NonFungibleLocalId>)

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

.non_fungible()

Returns the data of the NFT that the bucket contains.

Note: this method panics if the bucket does not contain exactly one NFT

.take_non_fungible(NonFungibleId)

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

Note: this method panics if the bucket does not contain exactly one NFT

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 below.

> 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)

Deposits a Bucket into this vault

.resource_address()

Returns the address of the resource stored in the vault

.resource_manager()

Returns the ResourceManager of the resource contained within the vault

.amount()

Returns the amount of tokens stored in the vault

.is_empty()

Returns true if the vault is empty, false otherwise.

.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

.authorize(function)

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

FungibleVault Methods

A FungibleVault inherits all of the Vault methods with the following additional methods:

Method

Description

.lock_fee(amount)

Lock fee to pay for the transaction.

Note: this panics if the vault contains a resource different than XRD.

.lock_contingent_fee(amount)

Lock fee to pay for the transaction, contingent on this transaction committing successfully

Note: this panics if the vault contains a resource different than XRD.

.create_proof_of_amount(amount)

Creates a Proof of a certain amount of resources in the vault

.authorize_with_amount(amount)

Authorizes an action by putting a proof of certain amount present in the vault on the AuthZone before running the specified function.

NonFungibleVault Methods

A NonFungibleVault inherits all of the Vault methods with the following additional methods:

Method

Description

.non_fungible_local_ids(limit)

Returns a index-set containing the id of NFTs present in the vault. The maximum number of NFTs returned is defined by limit

.contains_non_fungible(NonFungibleLocalId)

Returns true if the vault contains a given NonFungible, false otherwise

.non_fungibles()

Returns the ResourceManager of the resource contained within the vault

.take_non_fungibles(IndexSet<NonFungibleLocalId>)

Takes a set of NFTs from the vault and returns a Bucket with those NFTs.

.burn_non_fungibles(IndexSet<NonFungibleLocalId>)

Burns a set of NFTs from the vault

.non_fungible_local_id()

Returns the non fungible local id of the vault

Note: this method panics if the bucket does not contain exactly one NFT

.non_fungible()

Returns the data of the NFT that the vault contains

Note: this method panics if the bucket does not contain exactly one NFT

.take_non_fungible(NonFungibleId)

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

.create_proof_of_non_fungibles(IndexSet<NonFungibleLocalId>)

Creates a Proof of a set of NFTs from the vault

.authorize_with_non_fungibles(IndexSet<NonFungibleLocalId>)

Authorizes an action by putting a proof of a set of NonFungibles present in the vault on the AuthZone before running the specified function.


Was this article helpful?