-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* added list exmaples and tests * list.md updated and formatted the code * documentation updated and replaced #external(v0) with #abi(embed_v0) * updated: list.md * updated: SUMMARY.md * list example moved to ch02 and defined interface * removed submodule: using_lists * added using_lists * git submodule removed * apply fmt --------- Co-authored-by: julio4 <jules.doumeche@gmail.com>
- Loading branch information
Showing
10 changed files
with
279 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,14 @@ | ||
# Code generated by scarb DO NOT EDIT. | ||
version = 1 | ||
|
||
[[package]] | ||
name = "alexandria_storage" | ||
version = "0.2.0" | ||
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=ae1d514#ae1d5149ff601a7ac5b39edc867d33ebd83d7f4f" | ||
|
||
[[package]] | ||
name = "cairo_cheatsheet" | ||
version = "0.1.0" | ||
dependencies = [ | ||
"alexandria_storage", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Code generated by scarb DO NOT EDIT. | ||
version = 1 | ||
|
||
[[package]] | ||
name = "alexandria_storage" | ||
version = "0.2.0" | ||
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=ae1d514#ae1d5149ff601a7ac5b39edc867d33ebd83d7f4f" | ||
|
||
[[package]] | ||
name = "using_lists" | ||
version = "0.1.0" | ||
dependencies = [ | ||
"alexandria_storage", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "using_lists" | ||
version = "0.1.0" | ||
|
||
# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html | ||
|
||
[dependencies] | ||
starknet = ">=2.3.0" | ||
alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev="ae1d514"} |
72 changes: 72 additions & 0 deletions
72
listings/ch02-advanced-concepts/using_lists/src/contract.cairo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
#[starknet::interface] | ||
trait IListExample<TContractState> { | ||
fn add_in_amount(ref self: TContractState, number: u128); | ||
fn add_in_task(ref self: TContractState, description: felt252, status: felt252); | ||
fn is_empty_list(self: @TContractState) -> bool; | ||
fn list_length(self: @TContractState) -> u32; | ||
fn get_from_index(self: @TContractState, index: u32) -> u128; | ||
fn set_from_index(ref self: TContractState, index: u32, number: u128); | ||
fn pop_front_list(ref self: TContractState); | ||
fn array_conversion(self: @TContractState) -> Array<u128>; | ||
} | ||
|
||
#[starknet::contract] | ||
mod ListExample { | ||
use alexandria_storage::list::{List, ListTrait}; | ||
|
||
#[storage] | ||
struct Storage { | ||
amount: List<u128>, | ||
tasks: List<Task> | ||
} | ||
|
||
#[derive(Copy, Drop, Serde, starknet::Store)] | ||
struct Task { | ||
description: felt252, | ||
status: felt252 | ||
} | ||
|
||
|
||
#[abi(embed_v0)] | ||
impl ListExample of super::IListExample<ContractState> { | ||
fn add_in_amount(ref self: ContractState, number: u128) { | ||
let mut current_amount_list = self.amount.read(); | ||
current_amount_list.append(number); | ||
} | ||
|
||
fn add_in_task(ref self: ContractState, description: felt252, status: felt252) { | ||
let new_task = Task { description: description, status: status }; | ||
let mut current_tasks_list = self.tasks.read(); | ||
current_tasks_list.append(new_task); | ||
} | ||
|
||
fn is_empty_list(self: @ContractState) -> bool { | ||
let mut current_amount_list = self.amount.read(); | ||
current_amount_list.is_empty() | ||
} | ||
|
||
fn list_length(self: @ContractState) -> u32 { | ||
let mut current_amount_list = self.amount.read(); | ||
current_amount_list.len() | ||
} | ||
|
||
fn get_from_index(self: @ContractState, index: u32) -> u128 { | ||
self.amount.read()[index] | ||
} | ||
|
||
fn set_from_index(ref self: ContractState, index: u32, number: u128) { | ||
let mut current_amount_list = self.amount.read(); | ||
current_amount_list.set(index, number); | ||
} | ||
|
||
fn pop_front_list(ref self: ContractState) { | ||
let mut current_amount_list = self.amount.read(); | ||
current_amount_list.pop_front(); | ||
} | ||
|
||
fn array_conversion(self: @ContractState) -> Array<u128> { | ||
let mut current_amount_list = self.amount.read(); | ||
current_amount_list.array() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
mod contract; | ||
|
||
#[cfg(test)] | ||
mod tests; |
102 changes: 102 additions & 0 deletions
102
listings/ch02-advanced-concepts/using_lists/src/tests.cairo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
use using_lists::contract::IListExample; | ||
use core::array::ArrayTrait; | ||
use using_lists::contract::ListExample; | ||
use using_lists::contract::ListExample::{ | ||
Task, amountContractMemberStateTrait, tasksContractMemberStateTrait | ||
}; | ||
|
||
fn STATE() -> ListExample::ContractState { | ||
ListExample::contract_state_for_testing() | ||
} | ||
|
||
#[test] | ||
#[available_gas(2000000)] | ||
fn test_add_in_amount() { | ||
let mut state = STATE(); | ||
state.add_in_amount(200); | ||
assert(state.amount.read()[0] == 200, 'should be 200'); | ||
} | ||
|
||
#[test] | ||
#[available_gas(2000000)] | ||
fn test_add_in_task() { | ||
let mut state = STATE(); | ||
state.add_in_task('test_description', 'test_status'); | ||
let current_task: Task = state.tasks.read()[0]; | ||
assert(current_task.description == 'test_description', 'should be test_description'); | ||
assert(current_task.status == 'test_status', 'should be test_status'); | ||
} | ||
|
||
#[test] | ||
#[available_gas(2000000)] | ||
fn test_is_empty_list() { | ||
let mut state = STATE(); | ||
|
||
let pre_addition = state.is_empty_list(); | ||
assert(pre_addition == true, 'should be true'); | ||
|
||
state.add_in_amount(200); | ||
let post_addition = state.is_empty_list(); | ||
assert(post_addition == false, 'should be false'); | ||
} | ||
|
||
#[test] | ||
#[available_gas(2000000)] | ||
fn test_list_length() { | ||
let mut state = STATE(); | ||
|
||
let pre_addition = state.list_length(); | ||
assert(pre_addition == 0, 'should be zero'); | ||
|
||
state.add_in_amount(200); | ||
let post_addition = state.list_length(); | ||
assert(post_addition == 1, 'should be 1'); | ||
} | ||
|
||
#[test] | ||
#[available_gas(2000000)] | ||
fn test_get_from_index() { | ||
let mut state = STATE(); | ||
state.add_in_amount(200); | ||
let output = state.get_from_index(0); | ||
assert(output == 200, 'should be 200'); | ||
} | ||
|
||
#[test] | ||
#[available_gas(2000000)] | ||
fn test_set_from_index() { | ||
let mut state = STATE(); | ||
state.add_in_amount(200); | ||
state.set_from_index(0, 400); | ||
assert(state.amount.read()[0] == 400, 'should be 400'); | ||
} | ||
|
||
#[test] | ||
#[available_gas(2000000)] | ||
fn test_pop_front_list() { | ||
let mut state = STATE(); | ||
|
||
state.add_in_amount(200); | ||
let pre_pop_front = state.list_length(); | ||
assert(pre_pop_front == 1, 'should be 1'); | ||
|
||
state.pop_front_list(); | ||
let post_pop_front = state.list_length(); | ||
assert(post_pop_front == 0, 'should be zero'); | ||
} | ||
|
||
#[test] | ||
#[available_gas(2000000)] | ||
fn test_array_conversion() { | ||
let mut ideal_array = ArrayTrait::<u128>::new(); | ||
ideal_array.append(200); | ||
ideal_array.append(400); | ||
|
||
let mut state = STATE(); | ||
|
||
state.add_in_amount(200); | ||
state.add_in_amount(400); | ||
let output: Array<u128> = state.array_conversion(); | ||
|
||
assert(output == ideal_array, 'should be equal'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# List | ||
|
||
By default, there is no list type supported in Cairo, but you can use Alexandria. You can refer to the [Alexandria documentation](https://github.com/keep-starknet-strange/alexandria/tree/main/src/storage) for more details. | ||
|
||
## What is `List`? | ||
|
||
An ordered sequence of values that can be used in Starknet storage: | ||
|
||
```rust | ||
#[storage] | ||
stuct Storage { | ||
amounts: List<u128> | ||
} | ||
``` | ||
|
||
### Interface | ||
|
||
```rust | ||
trait ListTrait<T> { | ||
fn len(self: @List<T>) -> u32; | ||
fn is_empty(self: @List<T>) -> bool; | ||
fn append(ref self: List<T>, value: T) -> u32; | ||
fn get(self: @List<T>, index: u32) -> Option<T>; | ||
fn set(ref self: List<T>, index: u32, value: T); | ||
fn pop_front(ref self: List<T>) -> Option<T>; | ||
fn array(self: @List<T>) -> Array<T>; | ||
} | ||
``` | ||
|
||
`List` also implements `IndexView` so you can use the familiar bracket notation to access its members: | ||
|
||
```rust | ||
let second = self.amounts.read()[1]; | ||
``` | ||
|
||
Note that unlike `get`, using this bracket notation panics when accessing an out of bounds index. | ||
|
||
### Support for custom types | ||
|
||
`List` supports most of the corelib types out of the box. If you want to store a your own custom type in a `List`, it has to implement the `Store` trait. You can have the compiler derive it for you using the `#[derive(starknet::Store)]` attribute. | ||
|
||
### Caveats | ||
|
||
There are two idiosyncacies you should be aware of when using `List` | ||
|
||
1. The `append` operation costs 2 storage writes - one for the value itself and another one for updating the List's length | ||
2. Due to a compiler limitation, it is not possible to use mutating operations with a single inline statement. For example, `self.amounts.read().append(42);` will not work. You have to do it in 2 steps: | ||
|
||
```rust | ||
let mut amounts = self.amounts.read(); | ||
amounts.append(42); | ||
``` | ||
|
||
### Dependencies | ||
|
||
Update your project dependencies by in the `Scarb.toml` file: | ||
```rust | ||
[dependencies] | ||
(...) | ||
alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git" } | ||
``` | ||
|
||
For example, let's use `List` to create a contract that tracks a list of amounts and tasks: | ||
|
||
```rust | ||
{{#include ../../../listings/ch02-advanced-concepts/using-lists/src/contract.cairo}} | ||
``` |