RADIX WikiRADIX Wiki

Introduction

Radix enforces a strict invariant: every resource must exist inside a container at all times. There are two container types: Vaults (permanent, on-ledger storage) and Buckets (temporary, transaction-scoped carriers). The Radix Engine guarantees that resources cannot be duplicated, accidentally destroyed, or left in limbo – if your transaction ends with an undeposited bucket, it fails. This "physical resource" model eliminates entire categories of bugs common in other smart contract platforms.

Vault Patterns

Single-Vault Component

The simplest pattern: a component holds one vault to store a single resource type. A token sale component, for example, stores tokens in a vault and returns XRD payment to the buyer's account.

struct TokenSale {
    token_vault: Vault,    // holds tokens for sale
    xrd_vault: Vault,      // collects XRD payments
    price: Decimal,
}

Multi-Vault Component (HashMap)

When a component needs to manage arbitrary resource types (e.g. a DEX liquidity pool or a wallet-like component), use a HashMap<ResourceAddress, Vault>. When a new resource type is deposited, create a new vault; when an existing type arrives, deposit into the matching vault. Compiled and called under Scrypto 1.4.0 on 29 September 2026, two deposits of XRD land in one vault.

struct MultiVault {
    vaults: HashMap<ResourceAddress, Vault>,
}
 
impl MultiVault {
    pub fn deposit(&mut self, bucket: Bucket) {
        let addr = bucket.resource_address();
        self.vaults
            .entry(addr)
            .or_insert_with(|| Vault::new(addr))
            .put(bucket);
    }
}

A HashMap sits inside the component's state, so every method call reads the whole map. For a collection that grows without limit, a KeyValueStore<ResourceAddress, Vault> (source) stores each entry separately and loads only the entries a call touches.

Vault Take & Put

Withdraw resources from a vault with .take(amount) (returns a Bucket) or .take_all(). Deposit with .put(bucket). Withdrawing specific non-fungibles needs a NonFungibleVault: .take_non_fungible(&id) and .take_non_fungibles(&ids), where ids is an IndexSet<NonFungibleLocalId>, are defined on ScryptoNonFungibleVault and not on a plain Vault. A component that stores a generic Vault reaches them through .as_non_fungible():

let nft: NonFungibleBucket = self.vault.as_non_fungible().take_non_fungible(&id);

Bucket Patterns

Bucket Passing (Move Semantics)

Buckets use Rust's ownership model – passing a bucket to a function moves it. The caller no longer has access. This prevents double-spending at the language level.

A component cannot withdraw from a user's account, so the caller that creates the bucket is a transaction manifest. This one buys from the TokenSale above, and committed in resim under Scrypto 1.4.0:

CALL_METHOD Address("${account}") "lock_fee" Decimal("10");
CALL_METHOD Address("${account}") "withdraw" Address("${xrd}") Decimal("100");
TAKE_FROM_WORKTOP Address("${xrd}") Decimal("100") Bucket("payment");
CALL_METHOD Address("${token_sale}") "buy" Bucket("payment");
CALL_METHOD Address("${account}") "deposit_batch" Expression("ENTIRE_WORKTOP");

Bucket Splitting

Use .take(amount) on a bucket to split it into two buckets – the original retains the remainder. This is how you implement partial fills, fee extraction, or distributing resources across multiple destinations within a single transaction.

The Worktop

In transaction manifests, returned resources land on the worktop – a temporary staging area. Use TAKE_FROM_WORKTOP to create named buckets from worktop resources, then pass them to subsequent method calls. Leftover resources fail a transaction in two different places, both tested in resim on 29 September 2026. A named bucket that no later instruction uses is rejected before execution, with DanglingBucket, and no fee is charged. Resources left on the worktop get through validation and fail when the transaction ends, as a committed failure with DropNonEmptyBucket, and the fee is paid. The last instruction in the manifest above, deposit_batch with ENTIRE_WORKTOP, is there to empty it.

Next Steps

HydrateLast updated 6d agov1.5.012 revisionsVerified Sep 29, 2026