Skip to content

Commit

Permalink
feat(enums): Enums in contract (#212)
Browse files Browse the repository at this point in the history
* feat(enums): Enums in contract

* fix: small fmt

---------

Co-authored-by: julio4 <[email protected]>
  • Loading branch information
hudem1 and julio4 authored Jun 4, 2024
1 parent 8720e01 commit 78f3b63
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 0 deletions.
83 changes: 83 additions & 0 deletions listings/getting-started/cairo_cheatsheet/src/enum_example.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// ANCHOR: enums
#[derive(Drop, Serde, Copy, starknet::Store)]
struct Position {
x: u32,
y: u32,
}

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

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

// ANCHOR: enum_contract
#[starknet::interface]
trait IEnumContract<TContractState> {
fn register_action(ref self: TContractState, action: Action);
fn generate_default_actions_list(self: @TContractState) -> Array<Action>;
}

#[starknet::contract]
mod EnumContract {
use core::clone::Clone;
use core::traits::Into;
use super::IEnumContract;
use super::{Action, Position, UserCommand};

#[storage]
struct Storage {
most_recent_action: Action,
}

#[abi(embed_v0)]
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); },
Action::ChangeAvatarColor((
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);
}
};

self.most_recent_action.write(action);
}

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

actions
}
}
}
// ANCHOR_END: enum_contract


1 change: 1 addition & 0 deletions listings/getting-started/cairo_cheatsheet/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod mapping_example;
mod felt_example;
mod loop_example;
mod while_example;
mod enum_example;
mod match_example;
mod struct_example;
mod type_casting_example;
Expand Down
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Summary
- [while](./ch00/cairo_cheatsheet/while.md)
- [if let](./ch00/cairo_cheatsheet/if_let.md)
- [while let](./ch00/cairo_cheatsheet/while_let.md)
- [Enums](./ch00/cairo_cheatsheet/enums.md)
- [Match](./ch00/cairo_cheatsheet/match.md)
- [Tuples](./ch00/cairo_cheatsheet/tuples.md)
- [Struct](./ch00/cairo_cheatsheet/struct.md)
Expand Down
27 changes: 27 additions & 0 deletions src/ch00/cairo_cheatsheet/enums.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Enums

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.

```rust
{{#include ../../../listings/getting-started/cairo_cheatsheet/src/enum_example.cairo:enums}}
```

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](https://starknet-by-example.voyager.online/ch02/storing_arrays.html).

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 :

```rust
{{#include ../../../listings/getting-started/cairo_cheatsheet/src/enum_example.cairo:enum_contract}}
```

0 comments on commit 78f3b63

Please sign in to comment.