---
title: "6. Vault and Resource Patterns"
url: "https://radix-wiki.miow.me/developers/scrypto/06-vault-patterns"
updated: 2026-10-03
description: "Radix enforces a strict invariant: every resource must exist inside a container at all times. There are two container types: Vaults (permanent, on-ledger…"
---

# 6. Vault and Resource Patterns

## Introduction

Radix enforces a strict invariant: every [resource](/contents/tech/core-concepts/asset-oriented-programming) must exist inside a container at all times. There are two container types: **[Vaults](https://docs.radixdlt.com/docs/buckets-and-vaults)** (permanent, on-ledger storage) and **[Buckets](https://docs.radixdlt.com/docs/resources)** (temporary, transaction-scoped carriers). The [Radix Engine](/contents/tech/core-protocols/radix-engine) guarantees that [resources](/contents/tech/core-concepts/resources) cannot be duplicated, accidentally destroyed, or left in limbo – if your transaction ends with an undeposited [bucket](/contents/tech/core-concepts/buckets-proofs-and-vaults), 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](/contents/tech/core-concepts/components) holds one vault to store a single resource type. A token sale component, for example, stores tokens in a vault and returns [XRD](/contents/tech/core-protocols/xrd-token) 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`. 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,
}

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`](https://docs.radixdlt.com/docs/data-types/) ([source](https://github.com/radixdlt/radixdlt-scrypto/blob/v1.4.0/scrypto/src/component/kv_store.rs)) 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`, are defined on [`ScryptoNonFungibleVault`](https://github.com/radixdlt/radixdlt-scrypto/blob/v1.4.0/scrypto/src/resource/vault.rs) 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](https://docs.radixdlt.com/docs/resources) 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](/contents/tech/core-protocols/transaction-manifests), returned resources land on the **[worktop](/contents/tech/core-concepts/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

- [Multi-Component Architecture](/developers/scrypto/07-multi-component-architecture) – split a growing dApp across components that call each other

## External Links

- [Vaults and Buckets – Official Docs](https://docs.radixdlt.com/docs/buckets-and-vaults)
- [Resources – Official Docs](https://docs.radixdlt.com/docs/resources)
- [Permissioned and Regulated Assets](/developers/scrypto/09-permissioned-and-regulated-assets) – freeze, recall and withdrawal rules on resources, with worked examples
