Skip to main content

storage

Attribute Macro storage 

Source
#[storage]
Expand description

Storage macros and traits for the key-value store.

§#[storage] / #[derive(Storage)]

The storage attribute macro is shorthand for #[derive(Storage)].

Deriving Storage generates From<Keypath> for the struct, assigning a sub-keypath to each field.

All fields must be SDK storage types. Plain data must be wrapped in Value<T> to make the persistence model explicit.

§Storage types

TypeDescription
Value<T>Single XDR-serializable value
BalanceNon-negative, zero-defaulting i128 amount (0..=i128::MAX) with checked and lazy increase/decrease; the type to reach for first for balances, allowances, and supply
NNInti128 counter whose signed increments merge across concurrent transactions; the floor is enforced at commit (see its docs for the set/inc/strict_inc rules)
CapacityConstraintBlock-wide linear limit that concurrent transactions consume from
BytestringVariable-length byte blob
RCQ<T>Retrocausal queue: inserts commute, consumes are sequential
Map<K, V>Key-addressed collection (entries not enumerable)
Vector<S>Index-addressed, sparse-capable sequence
Array<S, N>Fixed-length sequence of N slots; no stored length
Node<T>Nested storage struct used inside collections

§Common patterns

§Single values

Use Value<T> for ordinary stored fields.

Value::set() writes immediately to the storage delta for the current frame.

Value::load_mut() returns an auto-flushing edit guard: the updated value is written back when the guard is dropped.

§Maps

Map<K, V> stores independently addressed entries. Entries may be absent; at() and at_mut() return storage-slot guards, not references into an in-memory collection.

§Vectors

Vector<S> tracks an in-bounds extent with Vector::len(). Individual slots may still be empty if their backing storage has been deleted, so vectors are sparse-capable.

Preferred append path:

Low-level append path:

§Fixed-length arrays

Array<S, N> addresses element i at the slot Map<u32, S> would use for key i, but knows its length at compile time: at()/at_mut() are bounds-checked without a storage read, and the array can be iterated and deleted. Every element may be unset. Element keys are derived on access, so the handle costs the same whatever N is.

§Nested structs in collections

Collections store storage types, not plain structs. For a nested storage struct inside a Map, Vector or Array, wrap it in Node<T>:

ⓘ
#[derive(Storage)]
struct Inner {
    amount: Value<u64>,
}

#[derive(Storage)]
struct State {
    inners: Vector<Node<Inner>>,
}

§Deletable trait

Deletable marks storage types whose data can be fully deleted. It provides three methods:

  • delete(&mut self): delete using the keypath’s own locking semantics
  • delete_exclusive(&mut self): delete with an exclusive lock
  • delete_internal(&mut self, exclusive: bool): low-level propagation hook used by derived impls

§Which types are Deletable?

TypeDeletable?Notes
NNIntYes
BalanceYes
CapacityConstraintYes
Value<T>Yes
BytestringYes
RCQ<T>YesDeletion cost scales with the queue’s size
Node<T>When T: Deletable
Vector<S>When S: Deletable
Array<S, N>When S: DeletableDeletes all N elements
Map<K, V>NoCannot enumerate entries

§#[derive(Deletable)]

Opt-in derive macro. Generates a Deletable impl that calls delete_internal() on every field.

If any field’s type does not implement Deletable, the derive produces a compile error at the struct definition, similar to deriving Clone for a struct containing a non-Clone field.

ⓘ
#[storage]
#[derive(Deletable)]
struct Counters {
    a: NNInt,
    b: Value<u64>,
}
// Counters::delete() will delete both `a` and `b`.

Structs containing Map fields cannot derive Deletable:

ⓘ
#[storage]
struct Registry {
    entries: Map<Address, Value<u64>>,
    total: NNInt,
}
// #[derive(Deletable)] on Registry would fail:
// Map<_, _> does not implement Deletable.

Usage examples are in the top-level documentation. Mark a struct as contract storage: generates From<Keypath> so each field is bound to a storage slot derived from the struct’s keypath.

Works on named-field structs (fields must themselves be storage types like NNInt, Map, Value, or nested storage structs) and on unit structs (struct Token;) for contracts that keep all state behind raw storage keys.

Accepts an optional domain = "..." argument to override the default domain (the struct’s bare identifier name).