Custom Types
Storing Custom Types
In Starknet contracts, storing custom types in contract storage requires implementing the Store
trait. While native types (like felt252
, u128
, etc.) can be stored directly, custom types need this additional step to generate the necessary implementation on how to handle their storage.
To make a custom type storable:
-
Derive the
starknet::Store
trait for your struct -
Add any other necessary traits like
Drop
,Serde
, andCopy
-
Define your storage variables using the custom type
Here’s an example showing how to store a custom Person
struct:
#[starknet::interface]
trait IStoringCustomType<TContractState> {
fn set_person(ref self: TContractState, person: Person);
fn set_name(ref self: TContractState, name: felt252);
}
// Deriving the starknet::Store trait
// allows us to store the `Person` struct in the contract's storage.
#[derive(Drop, Serde, Copy, starknet::Store)]
struct Person {
age: u8,
name: felt252,
}
#[starknet::contract]
mod StoringCustomType {
use starknet::storage::StoragePointerWriteAccess;
use super::Person;
use super::IStoringCustomType;
#[storage]
struct Storage {
person: Person,
}
#[abi(embed_v0)]
impl StoringCustomType of IStoringCustomType<ContractState> {
fn set_person(ref self: ContractState, person: Person) {
self.person.write(person);
}
fn set_name(ref self: ContractState, name: felt252) {
self.person.name.write(name);
}
}
}
For more complex types, you might need to implement the |
Accessing Struct Members
When you derive the Store
trait, Cairo automatically generates the necessary storage pointers for each struct member. This allows you to access and modify individual fields of your stored struct directly:
fn set_name(ref self: ContractState, name: felt252) {
self.person.name.write(name);
}
Custom Types in Entrypoints
When using custom types in Starknet contract entrypoints, you need to handle serialization and deserialization of data. This is because:
-
Input parameters are sent to entrypoints as arrays of
felt252
-
Return values must be converted back to arrays of
felt252
-
Custom types need to be converted between these formats automatically
Using the Serde Trait
The Serde
trait provides the necessary serialization and deserialization capabilities for your custom types. For most simple types, you can derive this trait automatically:
#[starknet::interface]
trait ISerdeCustomType<TContractState> {
fn person_input(ref self: TContractState, person: Person);
fn person_output(self: @TContractState) -> Person;
}
// Deriving the `Serde` trait allows us to use
// the `Person` type as an entrypoint parameter and as a return value
#[derive(Drop, Serde)]
struct Person {
age: u8,
name: felt252,
}
#[starknet::contract]
mod SerdeCustomType {
use super::Person;
use super::ISerdeCustomType;
#[storage]
struct Storage {}
#[abi(embed_v0)]
impl SerdeCustomType of ISerdeCustomType<ContractState> {
fn person_input(ref self: ContractState, person: Person) {}
fn person_output(self: @ContractState) -> Person {
Person { age: 10, name: 'Joe' }
}
}
}
For some complex types, you might need to implement the The |