StorageNode

A trait that given a storage path of a struct, generates the storage node of this struct. The storage node is a struct that is used to structure the storage of a struct, while taking into account this structure when computing the address of the struct members in the storage. The trigger for creating a storage node is the #[starknet::storage_node] attribute. Storage nodes are used in order to structure the storage of a struct, while not enforcing the struct to be sequential in the storage. This is useful for structs that contains phantom types such as Map and Vec. As a result, structs attributed with #[starknet::storage_node] are also considered to be phantom types, although not explicitly annotated as such. Structs which do not contain phantom types, can still be declared a storage node, and it will make them a phantom type. However, it may be preferable to simply make this struct storable (i.e. #[derive(Store)]') instead. This will still allow accessing individual members of the struct (see SubPointers`), but will not make the struct a phantom type. For example, given the following struct:

[starknet::storage_node]
struct MyStruct {
   a: felt252,
   b: Map<felt252, felt52>,
}

The following storage node struct and impl will be generated:

struct MyStructStorageNode {
    a: PendingStoragePath<felt252>,
    b: PendingStoragePath<Map<felt252, felt52>>,
}

impl MyStructStorageNodeImpl of StorageNode<MyStruct> {
   fn storage_node(self: StoragePath<MyStruct>) -> MyStructStorageNode {
        MyStructStorageNode {
           a: PendingStoragePathTrait::new(@self, selector!("a")),
           b: PendingStoragePathTrait::new(@self, selector!("b")),
        }
   }
}

For a type T that implement StorageNode (e.g. MyStruct in the example above), Deref<StoragePath<T>> is implemented as simply calling storage_node, and thus exposing the members of the storage node (a and b in the example above). For example, given the following storage:

[storage]
struct Storage {
    my_struct: MyStruct,
    a: felt52,
}

We can access the members of the storage node as follows:

fn use_storage(self: @ContractState) { let a_value = self.a.read(); let inner_a_value = self.my_struct.a.read(); let b_value = self.my_struct.b.entry(42).read(); }


If a member is annotated with `#[flat]`, the storage node will be flattened, and the
member name (i.e. `my_struct`) will not affect the address of the storage object.
In the storage example above, it will look like:

#storage struct Storage { #flat my_struct: MyStruct, a: felt52, } In this case, the storage node will be flattened, and both self.a and self.my_struct.a will point to the same address. This behavior is rarely intended, and thus #[flat] should be used with caution. Notice that the members of the storage node are PendingStoragePath instances, which are used to lazily get the updated storage path of the struct members, in this way only members that are accessed are actually evaluated.

Fully qualified path: core::starknet::storage::storage_node::StorageNode

pub trait StorageNode<T>

Trait functions

storage_node

Fully qualified path: core::starknet::storage::storage_node::StorageNode::storage_node

fn storage_node(self: StoragePath<T>) -> Self::NodeType

Trait types

NodeType

Fully qualified path: core::starknet::storage::storage_node::StorageNode::NodeType

type NodeType;