Just like other programming languages, enums (enumerations) are used in cairo to define variables that can only hold a set of predefined variants (= enum options), enhancing code readability and safety. They facilitate strong type checking and are ideal for organizing related options and supporting structured logic through pattern matching for example, which is also described in the next chapter.

In cairo, enum variants can hold different data types (the unit type, structs, other enums, tuples, default core library types, arrays, dictionaries, ...), as shown in the code snippet below. Furthermore, as a quick reminder, enums are expressions, meaning they can return values.

#[derive(Drop, Serde, Copy, starknet::Store)]
struct Position {
    x: u32,
    y: u32,

#[derive(Drop, Serde, Copy, starknet::Store)]
enum UserCommand {

#[derive(Drop, Serde, Copy, starknet::Store)]
enum Action {
    Move: Position,
    SendMessage: felt252,
    ChangeAvatarColor: (u8, u8, u8),
    ProfileState: UserCommand

Enums can be declared both inside and outside a contract. If declared outside, they need to be imported inside using the use keyword, just like other imports.

  1. Storing enums in contract

    • It is possible to store enums in the contract storage. But unlike most of the core library types which implement the Store trait, enums are custom types and therefore do not automatically implement the Store trait. The enum as well as all of its variants have to explicitly implement the Store trait in order for it to be stored inside a contract storage.

    • If all of its variants implement the Store trait, implementing the Store trait on the enum is as simple as deriving it, using #[derive(starknet::Store)] (as shown in example above). If not, the Store trait has to be manually implemented -- see an example of manually implementing the Store trait for a complex type in chapter Storing Arrays.

  2. Enums as parameters and return values to entrypoints

    • It is possible to pass enums to contract entrypoints as parameters, as well as return them from entrypoints. For that purpose, the enum needs to be serializable and dropable, hence the derivation of traits Serde and Drop in the above code snippet.

Here is an example of a contract illustrating the above statements :

trait IEnumContract<TContractState> {
    fn register_action(ref self: TContractState, action: Action);
    fn generate_default_actions_list(self: @TContractState) -> Array<Action>;

mod EnumContract {
    use core::clone::Clone;
    use core::traits::Into;
    use super::IEnumContract;
    use super::{Action, Position, UserCommand};

    struct Storage {
        most_recent_action: Action,

    impl IEnumContractImpl of IEnumContract<ContractState> {
        fn register_action(ref self: ContractState, action: Action) {
            // quick note: match takes ownership of variable (but enum Action implements Copy trait)
            match action {
                Action::Quit => { println!("Quit"); },
                Action::Move(value) => { println!("Move with x: {} and y: {}", value.x, value.y); },
                Action::SendMessage(msg) => { println!("Write with message: {}", msg); },
                    r, g, b
                )) => { println!("Change color to r: {}, g: {}, b: {}", r, g, b); },
                Action::ProfileState(state) => {
                    let profile_state = match state {
                        UserCommand::Login => 1,
                        UserCommand::UpdateProfile => 2,
                        UserCommand::Logout => 3,
                    println!("profile_state: {}", profile_state);


        fn generate_default_actions_list(self: @ContractState) -> Array<Action> {
            let actions = array![
                Action::Move(Position { x: 1, y: 2 }),
                Action::SendMessage('here is my message'),
                Action::ChangeAvatarColor((1, 2, 3)),

Last change: 2024-06-09, commit: 3fbfb60