);
From 57c35bd10ab421bcf63e3459544c59f2f810b857 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Fri, 3 Jan 2025 11:02:31 +0100
Subject: [PATCH 12/44] feat: migrate storage to snforge
---
listings/getting-started/storage/Scarb.toml | 3 +-
.../storage/src/contract.cairo | 35 +++++++++----------
.../storage/src/minimal_contract.cairo | 11 +++---
3 files changed, 22 insertions(+), 27 deletions(-)
diff --git a/listings/getting-started/storage/Scarb.toml b/listings/getting-started/storage/Scarb.toml
index 4bacb1e5..7d483c5a 100644
--- a/listings/getting-started/storage/Scarb.toml
+++ b/listings/getting-started/storage/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/storage/src/contract.cairo b/listings/getting-started/storage/src/contract.cairo
index 4ae5443c..dc7a436a 100644
--- a/listings/getting-started/storage/src/contract.cairo
+++ b/listings/getting-started/storage/src/contract.cairo
@@ -1,34 +1,31 @@
// [!region contract]
#[starknet::contract]
-pub mod Contract {
+mod Contract {
#[storage]
struct Storage {
- pub a: u128,
- pub b: u8,
- pub c: u256,
+ a: u128,
+ b: u8,
+ c: u256,
}
}
// [!endregion contract]
#[cfg(test)]
mod test {
- use super::Contract;
- use starknet::syscalls::deploy_syscall;
- use starknet::storage::StoragePointerReadAccess;
-
- #[test]
- fn test_can_deploy() {
- let (_contract_address, _) = deploy_syscall(
- Contract::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
- }
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare, load};
#[test]
fn test_storage_members() {
- let state = @Contract::contract_state_for_testing();
- assert_eq!(state.a.read(), 0_u128);
- assert_eq!(state.b.read(), 0_u8);
- assert_eq!(state.c.read(), 0_u256);
+ let contract = declare("Contract").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
+
+ let mut loaded = load(contract_address, selector!("a"), 1).span();
+ assert_eq!(Serde::deserialize(ref loaded).unwrap(), 0);
+
+ let mut loaded = load(contract_address, selector!("b"), 1).span();
+ assert_eq!(Serde::deserialize(ref loaded).unwrap(), 0);
+
+ let mut loaded = load(contract_address, selector!("c"), 2).span();
+ assert_eq!(Serde::deserialize(ref loaded).unwrap(), 0);
}
}
diff --git a/listings/getting-started/storage/src/minimal_contract.cairo b/listings/getting-started/storage/src/minimal_contract.cairo
index ef8ae142..eea58c6a 100644
--- a/listings/getting-started/storage/src/minimal_contract.cairo
+++ b/listings/getting-started/storage/src/minimal_contract.cairo
@@ -1,6 +1,6 @@
// [!region contract]
#[starknet::contract]
-pub mod Contract {
+mod Contract {
#[storage]
struct Storage {}
}
@@ -9,15 +9,12 @@ pub mod Contract {
// [!region tests]
#[cfg(test)]
mod test {
- use super::Contract;
- use starknet::syscalls::deploy_syscall;
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
#[test]
fn test_can_deploy() {
- let (_contract_address, _) = deploy_syscall(
- Contract::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
+ let contract = declare("Contract").unwrap().contract_class();
+ let (_contract_address, _) = contract.deploy(@array![]).unwrap();
// Not much to test
}
}
From b0cbfffe62bc0c9ecb7a89506b928d8bd3b96cc5 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Fri, 3 Jan 2025 11:45:51 +0100
Subject: [PATCH 13/44] feat: rework storage
---
pages/getting-started/basics/storage.md | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/pages/getting-started/basics/storage.md b/pages/getting-started/basics/storage.md
index 16210f31..d046ee6a 100644
--- a/pages/getting-started/basics/storage.md
+++ b/pages/getting-started/basics/storage.md
@@ -1,23 +1,27 @@
# Storage
-Here's the most minimal contract you can write in Cairo:
+## Basic Contract Structure
+
+Every Starknet contract must be defined as a module with the `#[starknet::contract]` attribute. Here's the simplest possible Cairo contract:
```cairo
// [!include ~/listings/getting-started/storage/src/minimal_contract.cairo:contract]
```
-Storage is a `struct` annotated with `#[storage]`. Every contract must have one and only one storage.
-It's a key-value store, where each key will be mapped to a storage address of the contract's storage space.
+## Contract Storage Basics
+
+Storage in Cairo contracts is implemented as a key-value store using a struct marked with the `#[storage]` attribute. Every contract must have exactly one storage definition, which serves as the contract's persistent state on the blockchain and is kept between contract executions.
+
+### Storage Variables
-You can define [storage variables](/getting-started/basics/variables#storage-variables) in your contract, and then use them to store and retrieve data.
+You can define [Storage Variables](/getting-started/basics/variables#storage-variables) to store and retrieve data in your contract:
```cairo
// [!include ~/listings/getting-started/storage/src/contract.cairo:contract]
```
:::note
-Actually these two contracts have the same underlying Sierra program.
-The Sierra code is generated only for storage variables that are actually accessed in the contract's functions. Declaring but never using a storage variable doesn't affect the compiled contract size/gas costs.
+💡 **Optimization Tip**: Both contracts above generate identical Sierra code. The compiler only generates code for storage variables that are actually used in contract functions. Declaring unused storage variables has no impact on contract size or gas costs.
:::
-You can also read about [storing custom types](/getting-started/basics/storing-custom-types).
+For more complex data structures, see [Storing Custom Types](/getting-started/basics/storing-custom-types).
From 23c161b182c55fa0d0b41b2af19311557791d979 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Fri, 3 Jan 2025 12:03:31 +0100
Subject: [PATCH 14/44] feat: storage space section
---
pages/getting-started/basics/storage.md | 30 +++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/pages/getting-started/basics/storage.md b/pages/getting-started/basics/storage.md
index d046ee6a..9388dd43 100644
--- a/pages/getting-started/basics/storage.md
+++ b/pages/getting-started/basics/storage.md
@@ -25,3 +25,33 @@ You can define [Storage Variables](/getting-started/basics/variables#storage-var
:::
For more complex data structures, see [Storing Custom Types](/getting-started/basics/storing-custom-types).
+
+### Storage Space (advanced)
+
+The contract's storage space consists of $2^{251}$ *storage slots*, where each slot:
+
+- Can store a single `felt252` value
+- Is initialized to 0
+- Has a unique address that can be accessed using `selector!("variable_name")` for primitive types
+
+In our previous contract example:
+
+- Variable `a` (u128):
+ - Address: `selector!("a")`
+ - Uses first 128 bits of the slot
+ - Leaves 124 bits unused
+- Variable `b` (u8):
+ - Address: `selector!("b")`
+ - Uses first 8 bits of the slot
+ - Leaves 244 bits unused
+- Variable `c` (u256)
+ - An u256 cannot fit in a single slot
+ - Base address: `selector!("c")`
+ - Uses two consecutive slots:
+ - First slot: lower 128 bits at `selector!("c")`
+ - Second slot: upper 128 bits at `selector!("c") + 1`
+ - Leaves 248 bits unused
+
+:::note
+💡 **Storage Optimization**: Notice how many bits are left unused in each slot? This can make storage operations expensive. To optimize storage usage, you can pack multiple variables together. Learn more in [Storage Optimisation](/advanced-concepts/optimisations/store_using_packing).
+:::
From 9e74dd025fd5fdec4de63d5b91a3938b7f6974e7 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Fri, 3 Jan 2025 12:07:21 +0100
Subject: [PATCH 15/44] fix: syscall typo
---
pages/getting-started/basics/syscalls.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pages/getting-started/basics/syscalls.md b/pages/getting-started/basics/syscalls.md
index 64ce911e..bacccda4 100644
--- a/pages/getting-started/basics/syscalls.md
+++ b/pages/getting-started/basics/syscalls.md
@@ -4,7 +4,7 @@ At the protocol level, the Starknet Operating System (OS) is the program that ma
Some of the OS functionalities are exposed to smart contracts through the use of syscalls (system calls). Syscalls can be used to get information about the state of the Starknet network, to interact with/deploy contracts, emit events, send messages, and perform other low-level operations.
-Syscalls return a `SyscallResult` which is either `Success` of `Failure`, allowing the contract to handle errors.
+Syscalls return a `SyscallResult` which is either `Success` or `Failure`, allowing the contract to handle errors.
Here's the available syscalls:
From 4ad870ec22aadfc1d59ab871fa9149f7794f5835 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Sun, 12 Jan 2025 09:17:45 +0100
Subject: [PATCH 16/44] feat: migrate constructor to snforge
---
Scarb.lock | 6 +++
.../getting-started/constructor/Scarb.toml | 3 +-
.../constructor/src/constructor.cairo | 52 +++++++++----------
3 files changed, 34 insertions(+), 27 deletions(-)
diff --git a/Scarb.lock b/Scarb.lock
index f401f76d..4f571cb7 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -64,6 +64,9 @@ dependencies = [
[[package]]
name = "constructor"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "counter"
@@ -300,6 +303,9 @@ dependencies = [
[[package]]
name = "storage"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "store_using_packing"
diff --git a/listings/getting-started/constructor/Scarb.toml b/listings/getting-started/constructor/Scarb.toml
index db71d0e6..239e6080 100644
--- a/listings/getting-started/constructor/Scarb.toml
+++ b/listings/getting-started/constructor/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/constructor/src/constructor.cairo b/listings/getting-started/constructor/src/constructor.cairo
index c384b00a..4234c81c 100644
--- a/listings/getting-started/constructor/src/constructor.cairo
+++ b/listings/getting-started/constructor/src/constructor.cairo
@@ -1,19 +1,23 @@
// [!region contract]
#[starknet::contract]
-pub mod ExampleConstructor {
- use starknet::ContractAddress;
- use starknet::storage::{Map, StorageMapWriteAccess};
+mod ConstructorContract {
+ // This trait is necessary to be able to write to a specific storage variable
+ use starknet::storage::StoragePointerWriteAccess;
#[storage]
struct Storage {
- pub names: Map::,
+ a: u128,
+ b: u8,
+ c: u256,
}
// The constructor is decorated with a `#[constructor]` attribute.
// It is not inside an `impl` block.
#[constructor]
- fn constructor(ref self: ContractState, name: felt252, address: ContractAddress) {
- self.names.write(address, name);
+ fn constructor(ref self: ContractState, a: u128, b: u8, c: u256) {
+ self.a.write(a);
+ self.b.write(b);
+ self.c.write(c);
}
}
// [!endregion contract]
@@ -21,29 +25,25 @@ pub mod ExampleConstructor {
// [!region tests]
#[cfg(test)]
mod tests {
- use super::ExampleConstructor;
- use starknet::{ContractAddress, syscalls::deploy_syscall};
- use starknet::{contract_address_const, testing::{set_contract_address}};
- use starknet::storage::StorageMapReadAccess;
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare, load};
#[test]
fn should_deploy_with_constructor_init_value() {
- let name: felt252 = 'bob';
- let address: ContractAddress = contract_address_const::<'caller'>();
-
- let (contract_address, _) = deploy_syscall(
- ExampleConstructor::TEST_CLASS_HASH.try_into().unwrap(),
- 0,
- array![name, address.into()].span(),
- false,
- )
- .unwrap();
-
- let state = @ExampleConstructor::contract_state_for_testing();
- set_contract_address(contract_address);
-
- let name = state.names.read(address);
- assert_eq!(name, 'bob');
+ let contract = declare("ConstructorContract").unwrap().contract_class();
+ let mut constructor_calldata: Array = array![];
+ 1_u128.serialize(ref constructor_calldata); // a
+ 2_u8.serialize(ref constructor_calldata); // b
+ 3_u256.serialize(ref constructor_calldata); // c
+ let (contract_address, _) = contract.deploy(@constructor_calldata).unwrap();
+
+ let mut loaded = load(contract_address, selector!("a"), 1).span();
+ assert_eq!(Serde::deserialize(ref loaded).unwrap(), 1);
+
+ let mut loaded = load(contract_address, selector!("b"), 1).span();
+ assert_eq!(Serde::deserialize(ref loaded).unwrap(), 2);
+
+ let mut loaded = load(contract_address, selector!("c"), 2).span();
+ assert_eq!(Serde::deserialize(ref loaded).unwrap(), 3);
}
}
// [!endregion tests]
From 1dd88c788983426034ecfe3e094918d55307cde5 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Sun, 12 Jan 2025 10:23:35 +0100
Subject: [PATCH 17/44] feat: rework constructor
---
pages/getting-started/basics/constructor.md | 30 +++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/pages/getting-started/basics/constructor.md b/pages/getting-started/basics/constructor.md
index 13806020..d32b5a9c 100644
--- a/pages/getting-started/basics/constructor.md
+++ b/pages/getting-started/basics/constructor.md
@@ -1,9 +1,35 @@
# Constructor
-Constructors are a special type of function that runs only once when deploying a contract, and can be used to initialize the state of the contract. Your contract must not have more than one constructor, and that constructor function must be annotated with the `#[constructor]` attribute. Also, a good practice consists in naming that function `constructor`.
+A constructor is a special function that initializes a contract's state during deployment. It has several key characteristics:
-Here's a simple example that demonstrates how to initialize the state of a contract on deployment by defining logic inside a constructor.
+- Runs exactly once when the contract is deployed
+- Must be annotated with `#[constructor]`
+- Up to one constructor per contract
+- Function is conventionally named `constructor`
+
+Here's an example that shows how to initialize storage variables during contract deployment:
```cairo
// [!include ~/listings/getting-started/constructor/src/constructor.cairo:contract]
```
+
+In this example:
+
+- The constructor takes three parameters: `a`, `b`, and `c`
+- Each parameter corresponds to a storage variable of the same name, but you can specify any argument variable name
+- The values are written to storage using the `write()` method. You need to import the `StoragePointerWriteAccess` trait to be able to write to a specific storage pointer
+
+:::note
+**Best Practice**
+
+Constructors are ideal for:
+
+- Setting initial contract state
+- Storing deployment-time parameters
+- Initializing access control (e.g., setting an owner)
+
+:::
+
+:::warning
+**Constructor values cannot be changed after deployment unless you specifically implement functions to modify them.**
+:::
From 313f75e4c70cde1a31a2f98f822560e1e9ee5a2b Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Mon, 13 Jan 2025 13:43:55 +0100
Subject: [PATCH 18/44] feat: improve storage layout example
---
pages/getting-started/basics/storage.md | 39 ++++++++++++++++---------
1 file changed, 26 insertions(+), 13 deletions(-)
diff --git a/pages/getting-started/basics/storage.md b/pages/getting-started/basics/storage.md
index 9388dd43..d93b7d55 100644
--- a/pages/getting-started/basics/storage.md
+++ b/pages/getting-started/basics/storage.md
@@ -21,7 +21,9 @@ You can define [Storage Variables](/getting-started/basics/variables#storage-var
```
:::note
-💡 **Optimization Tip**: Both contracts above generate identical Sierra code. The compiler only generates code for storage variables that are actually used in contract functions. Declaring unused storage variables has no impact on contract size or gas costs.
+**Optimization Tip**
+
+Both contracts above generate identical Sierra code. The compiler only generates code for storage variables that are actually used in contract functions. Declaring unused storage variables has no impact on contract size or gas costs.
:::
For more complex data structures, see [Storing Custom Types](/getting-started/basics/storing-custom-types).
@@ -32,26 +34,37 @@ The contract's storage space consists of $2^{251}$ *storage slots*, where each s
- Can store a single `felt252` value
- Is initialized to 0
-- Has a unique address that can be accessed using `selector!("variable_name")` for primitive types
+
+### Storage Pointers
+
+Storage variables are stored in storage slots using Starknet's memory model abstraction called **Storage Pointers**. A storage pointer is a tuple `(base_address, offset)` where:
+
+- `base_address` is the address of the first slot where the variable is stored
+- `offset` is the distance from the base address where the variable is stored
+
+To get the base address of a storage variable, you can use the `selector!` macro to derive it from the variable name: for example, `selector!("variable_name")`.
+
+### Storage Layout Example
In our previous contract example:
- Variable `a` (u128):
- - Address: `selector!("a")`
- - Uses first 128 bits of the slot
+ - Base address: `selector!("a")`
+ - Uses lowest 128 bits of the slot at offset 0
- Leaves 124 bits unused
- Variable `b` (u8):
- - Address: `selector!("b")`
- - Uses first 8 bits of the slot
+ - Base address: `selector!("b")`
+ - Uses lowest 8 bits of the slot at offset 0
- Leaves 244 bits unused
-- Variable `c` (u256)
- - An u256 cannot fit in a single slot
+- Variable `c` (u256):
- Base address: `selector!("c")`
- - Uses two consecutive slots:
- - First slot: lower 128 bits at `selector!("c")`
- - Second slot: upper 128 bits at `selector!("c") + 1`
- - Leaves 248 bits unused
+ - Too large for a single slot, uses two consecutive slots:
+ - First slot: lower 128 bits at offset 0
+ - Second slot: lower 128 bits at offset 1
+ - Leaves 248 bits unused, 124 in each slot
:::note
-💡 **Storage Optimization**: Notice how many bits are left unused in each slot? This can make storage operations expensive. To optimize storage usage, you can pack multiple variables together. Learn more in [Storage Optimisation](/advanced-concepts/optimisations/store_using_packing).
+**Storage Optimization**
+
+Notice how many bits are left unused in each slot? This can make storage operations expensive. To optimize storage usage, you can pack multiple variables together. Learn more in [Storage Optimisation](/advanced-concepts/optimisations/store_using_packing).
:::
From 65db4cb7f0fd707a4395108da483bb9589a1fc16 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 14 Jan 2025 15:12:31 +0100
Subject: [PATCH 19/44] feat: migrate variables to snforge
---
listings/getting-started/variables/Scarb.toml | 3 +-
.../variables/src/global_variables.cairo | 33 +++----------
.../variables/src/local_variables.cairo | 49 ++++++-------------
.../variables/src/storage_variables.cairo | 34 +++++--------
4 files changed, 36 insertions(+), 83 deletions(-)
diff --git a/listings/getting-started/variables/Scarb.toml b/listings/getting-started/variables/Scarb.toml
index 59fc6451..9509bac1 100644
--- a/listings/getting-started/variables/Scarb.toml
+++ b/listings/getting-started/variables/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/variables/src/global_variables.cairo b/listings/getting-started/variables/src/global_variables.cairo
index 01e7af48..c2ff2d07 100644
--- a/listings/getting-started/variables/src/global_variables.cairo
+++ b/listings/getting-started/variables/src/global_variables.cairo
@@ -1,39 +1,20 @@
-#[starknet::interface]
-pub trait IGlobalExample {
- fn foo(ref self: TContractState);
-}
-
// [!region contract]
#[starknet::contract]
-pub mod GlobalExample {
+pub mod GlobalVariablesContract {
// import the required functions from the starknet core library
use starknet::get_caller_address;
#[storage]
struct Storage {}
- #[abi(embed_v0)]
- impl GlobalExampleImpl of super::IGlobalExample {
- fn foo(ref self: ContractState) {
- // Call the get_caller_address function to get the sender address
- let _caller = get_caller_address();
- // ...
- }
+ pub fn foo(ref self: ContractState) {
+ // Call the get_caller_address function to get the sender address
+ let _caller = get_caller_address();
+ // ...
}
}
// [!endregion contract]
-#[cfg(test)]
-mod test {
- use super::GlobalExample;
- use starknet::syscalls::deploy_syscall;
+// Not much to test
+
- #[test]
- fn test_can_deploy() {
- let (_contract_address, _) = deploy_syscall(
- GlobalExample::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
- // Not much to test
- }
-}
diff --git a/listings/getting-started/variables/src/local_variables.cairo b/listings/getting-started/variables/src/local_variables.cairo
index b6d62839..040ba0a2 100644
--- a/listings/getting-started/variables/src/local_variables.cairo
+++ b/listings/getting-started/variables/src/local_variables.cairo
@@ -1,51 +1,34 @@
-#[starknet::interface]
-pub trait ILocalVariablesExample {
- fn do_something(self: @TContractState, value: u32) -> u32;
-}
-
// [!region contract]
#[starknet::contract]
-pub mod LocalVariablesExample {
+mod LocalVariablesContract {
#[storage]
struct Storage {}
- #[abi(embed_v0)]
- impl LocalVariablesExample of super::ILocalVariablesExample {
- fn do_something(self: @ContractState, value: u32) -> u32 {
- // This variable is local to the current block.
- // It can't be accessed once it goes out of scope.
- let increment = 10;
+ pub fn do_something(self: @ContractState, value: u32) -> u32 {
+ // This variable is local to the current block.
+ // It can't be accessed once it goes out of scope.
+ let increment = 10;
- {
- // The scope of a code block allows for local variable declaration
- // We can access variables defined in higher scopes.
- let sum = value + increment;
- sum
- }
- // We can't access the variable `sum` here, as it's out of scope.
+ {
+ // The scope of a code block allows for local variable declaration
+ // We can access variables defined in higher scopes.
+ let sum = value + increment;
+ sum
}
+ // We can't access the variable `sum` here, as it's out of scope.
}
}
// [!endregion contract]
#[cfg(test)]
mod test {
- use super::{
- LocalVariablesExample, ILocalVariablesExampleDispatcher,
- ILocalVariablesExampleDispatcherTrait,
- };
- use starknet::syscalls::deploy_syscall;
+ use super::LocalVariablesContract;
+ use super::LocalVariablesContract::do_something;
#[test]
- fn test_can_deploy_and_do_something() {
- let (contract_address, _) = deploy_syscall(
- LocalVariablesExample::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
-
- let contract = ILocalVariablesExampleDispatcher { contract_address };
+ fn test_can_do_something() {
+ let mut state = LocalVariablesContract::contract_state_for_testing();
let value = 10;
- let res = contract.do_something(value);
- assert_eq!(res, value + 10);
+ assert_eq!(do_something(@state, value), value + 10);
}
}
diff --git a/listings/getting-started/variables/src/storage_variables.cairo b/listings/getting-started/variables/src/storage_variables.cairo
index 3369445e..342a7f00 100644
--- a/listings/getting-started/variables/src/storage_variables.cairo
+++ b/listings/getting-started/variables/src/storage_variables.cairo
@@ -1,25 +1,26 @@
+// [!region contract]
#[starknet::interface]
-pub trait IStorageVariableExample {
+trait IStorageVariable {
fn set(ref self: TContractState, value: u32);
fn get(self: @TContractState) -> u32;
}
-// [!region contract]
#[starknet::contract]
-pub mod StorageVariablesExample {
+mod StorageVariablesContract {
// You need to import these storage functions to read and write to storage variables
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
+ use super::IStorageVariable;
// All storage variables are contained in a struct called Storage
// annotated with the `#[storage]` attribute
#[storage]
struct Storage {
// Storage variable holding a number
- pub value: u32,
+ value: u32,
}
#[abi(embed_v0)]
- impl StorageVariablesExample of super::IStorageVariableExample {
+ impl StorageVariables of IStorageVariable {
// Write to storage variables by sending a transaction
// that calls an external function
fn set(ref self: ContractState, value: u32) {
@@ -36,31 +37,18 @@ pub mod StorageVariablesExample {
#[cfg(test)]
mod test {
- use super::{
- StorageVariablesExample, IStorageVariableExampleDispatcher,
- IStorageVariableExampleDispatcherTrait,
- };
- use starknet::syscalls::deploy_syscall;
- use starknet::testing::set_contract_address;
- use starknet::storage::StoragePointerReadAccess;
+ use super::{IStorageVariableDispatcher, IStorageVariableDispatcherTrait};
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
#[test]
fn test_can_deploy_and_mutate_storage() {
- let (contract_address, _) = deploy_syscall(
- StorageVariablesExample::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
-
- let contract = IStorageVariableExampleDispatcher { contract_address };
+ let contract = declare("StorageVariablesContract").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
+ let contract = IStorageVariableDispatcher { contract_address };
let initial_value = 10;
contract.set(initial_value);
assert_eq!(contract.get(), initial_value);
-
- // With contract state directly
- let state = @StorageVariablesExample::contract_state_for_testing();
- set_contract_address(contract_address);
- assert_eq!(state.value.read(), initial_value);
}
}
From b33bbfd05ec9a51300ffb8d02e4d76beeded2b44 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 14 Jan 2025 15:41:11 +0100
Subject: [PATCH 20/44] feat: rework variables
---
Scarb.lock | 3 ++
pages/getting-started/basics/variables.md | 63 +++++++++++++++--------
routes.ts | 8 +--
3 files changed, 48 insertions(+), 26 deletions(-)
diff --git a/Scarb.lock b/Scarb.lock
index 4f571cb7..feccab33 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -339,6 +339,9 @@ version = "0.1.0"
[[package]]
name = "variables"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "visibility"
diff --git a/pages/getting-started/basics/variables.md b/pages/getting-started/basics/variables.md
index ead4d571..725e44e1 100644
--- a/pages/getting-started/basics/variables.md
+++ b/pages/getting-started/basics/variables.md
@@ -1,24 +1,32 @@
# Variables
-There are 3 types of variables in Cairo contracts:
-
-- Local
- - declared inside a function
- - not stored on the blockchain
-- Storage
- - declared in the [Storage](/getting-started/basics/storage) of a contract
- - can be accessed from one execution to another
-- Global
- - provides information about the blockchain
- - accessed anywhere, even within library functions
+Cairo contracts support three types of variables, each serving a different purpose:
+
+1. **Local Variables**
+ - Temporary variables within functions
+ - Exist only during function execution
+ - Not stored on the blockchain
+
+2. **Storage Variables**
+ - Defined in the contract's [Storage](/getting-started/basics/storage)
+ - Persist between contract executions
+ - Stored on the blockchain
+
+3. **Global Variables**
+ - Provide blockchain context and information
+ - Accessible anywhere in the contract
+ - Read-only system variables
## Local Variables
-Local variables are used and accessed within the scope of a specific function or block of code. They are temporary and exist only for the duration of that particular function or block execution.
+Local variables are temporary variables that exist only within their defined scope (a function or code block). Key characteristics:
-Local variables are stored in memory and are not stored on the blockchain. This means they cannot be accessed from one execution to another. Local variables are useful for storing temporary data that is relevant only within a specific context. They also make the code more readable by giving names to intermediate values.
+- Stored in memory, not on the blockchain
+- Used for intermediate calculations and temporary data
+- Available only during function execution
+- Help improve code readability by naming values
-Here's a simple example of a contract with only local variables:
+Here's an example demonstrating local variable scope:
```cairo
// [!include ~/listings/getting-started/variables/src/local_variables.cairo:contract]
@@ -26,25 +34,36 @@ Here's a simple example of a contract with only local variables:
## Storage Variables
-Storage variables are persistent data stored on the blockchain. They can be accessed from one execution to another, allowing the contract to remember and update information over time. See [Storage](/getting-started/basics/storage).
-
-To write or update a storage variable, you need to interact with the contract through an external entrypoint by sending a transaction.
+Storage variables provide persistent state for your contract on the blockchain. They have these properties:
-On the other hand, you can read state variables for free, without any transaction, simply by interacting with a node.
+- Persist between contract executions
+- Can be read for free (no transaction needed)
+- Require a transaction to write to them
+- Must be defined in the contract's Storage struct
-Here's a simple example of a contract with one storage variable:
+Here's an example showing storage variable usage:
```cairo
// [!include ~/listings/getting-started/variables/src/storage_variables.cairo:contract]
```
+:::note
+**Storage Access**
+
+- Reading: Free operation, no transaction needed
+- Writing: Requires a transaction and costs gas
+
+:::
+
## Global Variables
-Global variables are predefined variables that provide information about the blockchain and the current execution environment. They can be accessed at any time and from anywhere!
+Global variables provide access to blockchain context and system information. In Starknet:
-In Starknet, you can access global variables by using specific functions from the Starknet core library.
+- Accessed through core library functions
+- Available anywhere in the contract
+- Provide critical blockchain context (e.g., caller address, block info)
-For example, the `get_caller_address` function returns the address of the caller of the current transaction, and the `get_contract_address` function returns the address of the current contract.
+Example using global variables:
```cairo
// [!include ~/listings/getting-started/variables/src/global_variables.cairo:contract]
diff --git a/routes.ts b/routes.ts
index 0faacdaf..6f7702ce 100644
--- a/routes.ts
+++ b/routes.ts
@@ -18,14 +18,14 @@ const config: Sidebar = [
text: "Storage",
link: "/getting-started/basics/storage",
},
- {
- text: "Constructor",
- link: "/getting-started/basics/constructor",
- },
{
text: "Variables",
link: "/getting-started/basics/variables",
},
+ {
+ text: "Constructor",
+ link: "/getting-started/basics/constructor",
+ },
{
text: "Visibility and Mutability",
link: "/getting-started/basics/visibility-mutability",
From 9455abf3d2908372034bafe44aab96e84cafcff1 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 14 Jan 2025 15:59:14 +0100
Subject: [PATCH 21/44] fix: simplify local variable
---
.../getting-started/variables/src/local_variables.cairo | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/listings/getting-started/variables/src/local_variables.cairo b/listings/getting-started/variables/src/local_variables.cairo
index 040ba0a2..43bcf8c0 100644
--- a/listings/getting-started/variables/src/local_variables.cairo
+++ b/listings/getting-started/variables/src/local_variables.cairo
@@ -4,7 +4,7 @@ mod LocalVariablesContract {
#[storage]
struct Storage {}
- pub fn do_something(self: @ContractState, value: u32) -> u32 {
+ pub fn do_something(value: u32) -> u32 {
// This variable is local to the current block.
// It can't be accessed once it goes out of scope.
let increment = 10;
@@ -22,13 +22,11 @@ mod LocalVariablesContract {
#[cfg(test)]
mod test {
- use super::LocalVariablesContract;
use super::LocalVariablesContract::do_something;
#[test]
fn test_can_do_something() {
- let mut state = LocalVariablesContract::contract_state_for_testing();
let value = 10;
- assert_eq!(do_something(@state, value), value + 10);
+ assert_eq!(do_something(value), value + 10);
}
}
From 6c419f518c098a78f832afd83de90d0b6b9cf186 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Thu, 16 Jan 2025 10:34:46 +0100
Subject: [PATCH 22/44] feat: migrate visibility to snforge
---
Scarb.lock | 3 +
.../getting-started/visibility/Scarb.toml | 3 +-
.../visibility/src/visibility.cairo | 117 +++++++++---------
3 files changed, 62 insertions(+), 61 deletions(-)
diff --git a/Scarb.lock b/Scarb.lock
index feccab33..de4d40ad 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -346,6 +346,9 @@ dependencies = [
[[package]]
name = "visibility"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "write_to_any_slot"
diff --git a/listings/getting-started/visibility/Scarb.toml b/listings/getting-started/visibility/Scarb.toml
index ec43ebaa..d313c951 100644
--- a/listings/getting-started/visibility/Scarb.toml
+++ b/listings/getting-started/visibility/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/visibility/src/visibility.cairo b/listings/getting-started/visibility/src/visibility.cairo
index 6143566c..a872346f 100644
--- a/listings/getting-started/visibility/src/visibility.cairo
+++ b/listings/getting-started/visibility/src/visibility.cairo
@@ -1,103 +1,100 @@
+// [!region contract]
+// This trait defines the public interface of our contract
+// All functions declared here will be accessible externally
#[starknet::interface]
-pub trait IExampleContract {
+trait ContractInterface {
fn set(ref self: TContractState, value: u32);
fn get(self: @TContractState) -> u32;
}
-// [!region contract]
#[starknet::contract]
-pub mod ExampleContract {
+mod Contract {
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
+ use super::ContractInterface;
#[storage]
- struct Storage {
+ pub struct Storage {
pub value: u32,
}
- // The `#[abi(embed_v0)]` attribute indicates that all
- // the functions in this implementation can be called externally.
- // Omitting this attribute would make all the functions internal.
+ // External Functions Implementation
+ // The `#[abi(embed_v0)]` attribute makes these functions callable from outside the contract
+ // This is where we implement our public interface defined in ContractInterface
#[abi(embed_v0)]
- impl ExampleContract of super::IExampleContract {
- // The `set` function can be called externally
- // because it is written inside an implementation marked as `#[abi(embed_v0)]`.
- // It can modify the contract's state as it is passed as a reference.
+ pub impl ContractImpl of ContractInterface {
+ // External function that can modify state
+ // - Takes `ref self` to allow state modifications
+ // - Calls internal `increment` function to demonstrate internal function usage
fn set(ref self: ContractState, value: u32) {
- self.value.write(value);
+ self.value.write(increment(value));
}
- // The `get` function can be called externally
- // because it is written inside an implementation marked as `#[abi(embed_v0)]`.
- // However, it can't modify the contract's state, as it is passed as a snapshot
- // -> It's only a "view" function.
+ // External view function (cannot modify state)
+ // - Takes `@self` (snapshot) to prevent state modifications
+ // - Demonstrates calling an internal function (_read_value)
fn get(self: @ContractState) -> u32 {
- // We can call an internal function from any functions within the contract
- PrivateFunctionsTrait::_read_value(self)
+ self._read_value()
}
}
- // The lack of the `#[abi(embed_v0)]` attribute indicates that all the functions in
- // this implementation can only be called internally.
- // We name the trait `PrivateFunctionsTrait` to indicate that it is an
- // internal trait allowing us to call internal functions.
+ // Internal Functions Implementation
+ // These functions can only be called from within the contract
+ // The #[generate_trait] attribute creates a trait for these internal functions
#[generate_trait]
- pub impl PrivateFunctions of PrivateFunctionsTrait {
- // The `_read_value` function is outside the implementation that is
- // marked as `#[abi(embed_v0)]`, so it's an _internal_ function
- // and can only be called from within the contract.
- // However, it can't modify the contract's state, as it is passed
- // as a snapshot: it is only a "view" function.
+ pub impl Internal of InternalTrait {
+ // Internal view function
+ // - Takes `@self` as it only needs to read state
+ // - Can only be called by other functions within the contract
fn _read_value(self: @ContractState) -> u32 {
self.value.read()
}
}
+
+ // Pure Internal Function
+ // - Doesn't access contract state
+ // - Defined directly in the contract body
+ // - Considered good practice to keep pure functions outside impl blocks
+ // It's also possible to use ContractState here, but it's not recommended
+ // as it'll require to pass the state as a parameter
+ pub fn increment(value: u32) -> u32 {
+ value + 1
+ }
}
// [!endregion contract]
#[cfg(test)]
mod test {
- use super::{ExampleContract, IExampleContractDispatcher, IExampleContractDispatcherTrait};
- use starknet::syscalls::deploy_syscall;
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
-
- // These imports will allow us to directly access and set the contract state:
- // - for `PrivateFunctionsTrait` internal functions access
- // implementation need to be public to be able to access it
- use super::ExampleContract::PrivateFunctionsTrait;
- // to set the contract address for the state
- // and also be able to use the dispatcher on the same contract
- use starknet::testing::set_contract_address;
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
+ use super::{ContractInterfaceDispatcher, ContractInterfaceDispatcherTrait};
+ use super::Contract;
+ use super::Contract::{InternalTrait, increment};
#[test]
- fn can_call_set_and_get() {
- let (contract_address, _) = deploy_syscall(
- ExampleContract::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
+ fn test_external_functions() {
+ // Deploy the contract
+ let contract = declare("Contract").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
- // You can interact with the external entrypoints of the contract using the dispatcher.
- let contract = IExampleContractDispatcher { contract_address };
- // But for internal functions, you need to use the contract state.
- let mut state = ExampleContract::contract_state_for_testing();
- set_contract_address(contract_address);
+ // Create contract interface for external calls
+ let contract = ContractInterfaceDispatcher { contract_address };
- // The contract dispatcher and state refer to the same contract.
- assert_eq!(contract.get(), state.value.read());
-
- // We can set from the dispatcher
+ // Test external function that modifies state
contract.set(42);
- assert_eq!(contract.get(), state.value.read());
- assert_eq!(42, state.value.read());
- assert_eq!(42, contract.get());
+ assert_eq!(43, contract.get()); // Value is incremented
+ }
+
+ #[test]
+ fn test_internal_functions() {
+ // Create contract state for internal function access
+ let mut state = Contract::contract_state_for_testing();
- // Or directly from the state for more complex operations
+ // Test direct state modification
state.value.write(24);
- assert_eq!(contract.get(), state.value.read());
assert_eq!(24, state.value.read());
- assert_eq!(24, contract.get());
- // We can also access internal functions from the state
+ // Test internal function access
assert_eq!(state._read_value(), state.value.read());
- assert_eq!(state._read_value(), contract.get());
+ assert_eq!(25, increment(24));
}
}
From bb3e7e0569acb87308d0d1fa2c309ca8fcab471a Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Thu, 16 Jan 2025 13:12:26 +0100
Subject: [PATCH 23/44] feat: rework visibility
---
.../basics/visibility-mutability.md | 79 +++++++++++++++----
routes.ts | 8 +-
2 files changed, 68 insertions(+), 19 deletions(-)
diff --git a/pages/getting-started/basics/visibility-mutability.md b/pages/getting-started/basics/visibility-mutability.md
index 3d4a57f9..aad460e2 100644
--- a/pages/getting-started/basics/visibility-mutability.md
+++ b/pages/getting-started/basics/visibility-mutability.md
@@ -1,29 +1,78 @@
-# Visibility and Mutability
+# Interfaces, Visibility and Mutability
-## Visibility
+## Function Visibility
-There are two types of functions in Starknet contracts:
+In Starknet contracts, functions can have two types of visibility:
-- Functions that are accessible externally and can be called by anyone.
-- Functions that are only accessible internally and can only be called by other functions in the contract.
-
-These functions are also typically divided into two different implementation blocks. The first `impl` block for externally accessible functions is explicitly annotated with an `#[abi(embed_v0)]` attribute. This indicates that all the functions inside this block can be called either as a transaction or as a view function. The second `impl` block for internally accessible functions is not annotated with any attribute, which means that all the functions inside this block are private by default.
+- **External Functions**: Can be called by anyone, including other contracts and users
+- **Internal Functions**: Can only be called by other functions within the same contract
## State Mutability
-Regardless of whether a function is internal or external, it can either modify the contract's state or not. When we declare functions that interact with storage variables inside a smart contract,
-we need to explicitly state that we are accessing the `ContractState` by adding it as the first parameter of the function. This can be done in two different ways:
+Every function in a contract can either modify or just read the contract's state. This behavior is determined by how we pass the `ContractState` parameter:
+
+- **State-Modifying Functions**: Use `ref self: ContractState`
+ - Can read and write to storage
+ - Require a transaction to execute
+ - Cost gas to run
+
+- **View Functions**: Use `self: @ContractState`
+ - Can only read from storage
+ - Can be called directly through an RPC node
+ - Free to call (no transaction needed)
+
+:::note
+Internal functions follow the same state mutability rules as external functions. The only difference is who can call them.
+:::
+
+## Implementation
+
+### External Functions
+
+For external functions (both state-modifying and view), you need:
+
+1. **Interface Definition**
+ - Defined with `#[starknet::interface]` attribute
+ - Lists all functions that can be called externally
+ - Functions can be called as transactions or view calls
+ - Part of the contract's public API
-- If we want our function to be able to mutate the state of the contract, we pass it by reference like this: `ref self: ContractState`
-- If we want our function to be read-only and not mutate the state of the contract, we pass it by snapshot like this: `self: @ContractState`
+2. **Interface Implementation**
+ - Uses `#[abi(embed_v0)]` attribute
+ - Becomes part of the contract's ABI (Application Binary Interface)
+ - ABI defines how to interact with the contract from outside
+ - Must implement all functions defined in the interface
-Read-only functions, also called view functions, can be directly called without making a transaction. You can interact with them directly through an RPC node to read the contract's state, and they're free to call!
-External functions, that modify the contract's state, on the other hand, can only be called by making a transaction.
+### Internal Functions
-Internal functions can't be called externally, but the same principle applies regarding state mutability.
+For internal functions, there are two options:
-Let's take a look at a simple example contract to see these in action:
+1. **Implementation Block**
+ - Can use `#[generate_trait]` attribute
+ - Recommended for functions that need `ContractState` access
+ - Sometimes prefixed with `_` to indicate internal use
+
+2. **Direct Contract Body**
+ - Functions defined directly in the contract
+ - Recommended for pure functions
+ - Useful for helper functions and calculations
+
+## Example
+
+Here's a complete example demonstrating these concepts:
```cairo
// [!include ~/listings/getting-started/visibility/src/visibility.cairo:contract]
```
+
+:::note
+**Multiple Implementations**
+
+Cairo contracts can implement multiple interfaces and have multiple internal implementation blocks. This is not only possible but recommended because it:
+
+- Keeps each implementation block focused on a single responsibility
+- Makes the code more maintainable and easier to test
+- Simplifies the implementation of standard interfaces
+- Allows for better organization of related functionality
+
+:::
diff --git a/routes.ts b/routes.ts
index 6f7702ce..70b18183 100644
--- a/routes.ts
+++ b/routes.ts
@@ -18,6 +18,10 @@ const config: Sidebar = [
text: "Storage",
link: "/getting-started/basics/storage",
},
+ {
+ text: "Interfaces, Visibility and Mutability",
+ link: "/getting-started/basics/visibility-mutability",
+ },
{
text: "Variables",
link: "/getting-started/basics/variables",
@@ -26,10 +30,6 @@ const config: Sidebar = [
text: "Constructor",
link: "/getting-started/basics/constructor",
},
- {
- text: "Visibility and Mutability",
- link: "/getting-started/basics/visibility-mutability",
- },
{
text: "Counter Example",
link: "/getting-started/basics/counter",
From 4632562a7a2a6da4fe8cb50add2b19f0aded7ca5 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Mon, 20 Jan 2025 15:52:13 +0100
Subject: [PATCH 24/44] feat: migrate counter to snforge
---
Scarb.lock | 3 +++
listings/getting-started/counter/Scarb.toml | 3 ++-
.../getting-started/counter/src/counter.cairo | 17 +++++++----------
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/Scarb.lock b/Scarb.lock
index de4d40ad..6b3fd50b 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -71,6 +71,9 @@ dependencies = [
[[package]]
name = "counter"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "crowdfunding"
diff --git a/listings/getting-started/counter/Scarb.toml b/listings/getting-started/counter/Scarb.toml
index 3979e167..fa07006f 100644
--- a/listings/getting-started/counter/Scarb.toml
+++ b/listings/getting-started/counter/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/counter/src/counter.cairo b/listings/getting-started/counter/src/counter.cairo
index 593d2342..bf205d3c 100644
--- a/listings/getting-started/counter/src/counter.cairo
+++ b/listings/getting-started/counter/src/counter.cairo
@@ -13,7 +13,7 @@ pub mod SimpleCounter {
#[storage]
struct Storage {
// Counter variable
- pub counter: u128,
+ counter: u128,
}
#[constructor]
@@ -45,17 +45,14 @@ pub mod SimpleCounter {
#[cfg(test)]
mod test {
- use super::{SimpleCounter, ISimpleCounterDispatcher, ISimpleCounterDispatcherTrait};
- use starknet::syscalls::deploy_syscall;
+ use super::{ISimpleCounterDispatcher, ISimpleCounterDispatcherTrait};
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
fn deploy(init_value: u128) -> ISimpleCounterDispatcher {
- let (contract_address, _) = deploy_syscall(
- SimpleCounter::TEST_CLASS_HASH.try_into().unwrap(),
- 0,
- array![init_value.into()].span(),
- false,
- )
- .unwrap();
+ let contract = declare("SimpleCounter").unwrap().contract_class();
+ let mut constructor_calldata = array![];
+ init_value.serialize(ref constructor_calldata);
+ let (contract_address, _) = contract.deploy(@constructor_calldata).unwrap();
ISimpleCounterDispatcher { contract_address }
}
From 4d474364d1df837d516abb869e619a9f71363616 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 21 Jan 2025 17:33:49 +0100
Subject: [PATCH 25/44] fix: remove pub visibility for counter contract
---
listings/getting-started/counter/src/counter.cairo | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/listings/getting-started/counter/src/counter.cairo b/listings/getting-started/counter/src/counter.cairo
index bf205d3c..ed538eb1 100644
--- a/listings/getting-started/counter/src/counter.cairo
+++ b/listings/getting-started/counter/src/counter.cairo
@@ -1,5 +1,5 @@
#[starknet::interface]
-pub trait ISimpleCounter {
+trait ISimpleCounter {
fn get_current_count(self: @TContractState) -> u128;
fn increment(ref self: TContractState);
fn decrement(ref self: TContractState);
@@ -7,7 +7,7 @@ pub trait ISimpleCounter {
// [!region contract]
#[starknet::contract]
-pub mod SimpleCounter {
+mod SimpleCounter {
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
#[storage]
From b8120c9af4c1cd02dd7e828edf59225e94ef07a8 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 21 Jan 2025 17:34:12 +0100
Subject: [PATCH 26/44] feat: migrate mappings to snforge
---
Scarb.lock | 3 ++
listings/getting-started/mappings/Scarb.toml | 3 +-
.../mappings/src/mappings.cairo | 33 +++++++++++--------
3 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/Scarb.lock b/Scarb.lock
index 6b3fd50b..57afc518 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -127,6 +127,9 @@ version = "0.1.0"
[[package]]
name = "mappings"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "merkle_tree"
diff --git a/listings/getting-started/mappings/Scarb.toml b/listings/getting-started/mappings/Scarb.toml
index b5a30f8a..51b8de05 100644
--- a/listings/getting-started/mappings/Scarb.toml
+++ b/listings/getting-started/mappings/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/mappings/src/mappings.cairo b/listings/getting-started/mappings/src/mappings.cairo
index 7a4aa932..cf94d12f 100644
--- a/listings/getting-started/mappings/src/mappings.cairo
+++ b/listings/getting-started/mappings/src/mappings.cairo
@@ -1,24 +1,25 @@
use starknet::ContractAddress;
#[starknet::interface]
-pub trait IMapContract {
+trait IMapContract {
fn set(ref self: TContractState, key: ContractAddress, value: felt252);
fn get(self: @TContractState, key: ContractAddress) -> felt252;
}
// [!region contract]
#[starknet::contract]
-pub mod MapContract {
+mod MapContract {
+ use super::IMapContract;
use starknet::ContractAddress;
use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess};
#[storage]
struct Storage {
- map: Map::,
+ map: Map,
}
#[abi(embed_v0)]
- impl MapContractImpl of super::IMapContract {
+ impl MapContractImpl of IMapContract {
fn set(ref self: ContractState, key: ContractAddress, value: felt252) {
self.map.write(key, value);
}
@@ -32,23 +33,27 @@ pub mod MapContract {
#[cfg(test)]
mod test {
- use super::{MapContract, IMapContractDispatcher, IMapContractDispatcherTrait};
- use starknet::syscalls::deploy_syscall;
+ use super::{IMapContractDispatcher, IMapContractDispatcherTrait};
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
+ use starknet::contract_address_const;
+
+ fn deploy() -> IMapContractDispatcher {
+ let contract = declare("MapContract").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
+ IMapContractDispatcher { contract_address }
+ }
#[test]
fn test_deploy_and_set_get() {
- let (contract_address, _) = deploy_syscall(
- MapContract::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
- let mut contract = IMapContractDispatcher { contract_address };
+ let contract = deploy();
// Write to map.
+ let key = contract_address_const::<'key'>();
let value: felt252 = 1;
- contract.set(key: contract_address, value: value);
+ contract.set(key, value);
// Read from map.
- let read_value = contract.get(contract_address);
- assert(read_value == 1, 'wrong value read');
+ let read_value = contract.get(key);
+ assert_eq!(read_value, value);
}
}
From 4d9e92884fb01ce67f2d4949b4d4ccc26e3fed03 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 21 Jan 2025 17:34:46 +0100
Subject: [PATCH 27/44] feat: rework mappings
---
pages/getting-started/basics/mappings.md | 34 +++++++++++++++++++-----
routes.ts | 8 +++---
2 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/pages/getting-started/basics/mappings.md b/pages/getting-started/basics/mappings.md
index c9754e97..e3d48f35 100644
--- a/pages/getting-started/basics/mappings.md
+++ b/pages/getting-started/basics/mappings.md
@@ -1,15 +1,37 @@
# Mappings
-Maps are a key-value data structure used to store data within a smart contract. In Cairo they are implemented using the `Map` type. It's important to note that the `Map` type can only be used inside the `Storage` struct of a contract and that it can't be used elsewhere.
+Maps are a fundamental key-value data structure in Cairo smart contracts that allow you to store and retrieve values using unique keys. The `Map` type in `starknet::storage` is specifically designed for contract storage for this purpose.
-Here we demonstrate how to use the `Map` type within a Cairo contract, to map between a key of type `ContractAddress` and value of type `felt252`. The key-value types are specified within angular brackets <>. We write to the map by calling the `write()` method, passing in both the key and value. Similarly, we can read the value associated with a given key by calling the `read()` method and passing in the relevant key.
+Here's a simple example that demonstrates how to use a `Map`:
-Some additional notes:
+```cairo
+// [!include ~/listings/getting-started/mappings/src/mappings.cairo:contract]
+```
+
+Let's break down the key components:
+
+- **Declaration**: Maps are declared using `Map` syntax
+- **Storage**: Maps must be declared inside the contract's `Storage` struct
+ - You need to import the `StorageMapReadAccess` and `StorageMapWriteAccess` traits from `starknet::storage`
+- **Operations**:
+ - `write(key, value)`: Stores a value for a given key
+ - `read(key)`: Retrieves the value associated with a key
+- Maps automatically initialize all values to zero
+- Keys and values must be of valid storage types, see [Storing Custom Types](/getting-started/basics/storing-custom-types)
-- More complex key-value mappings are possible, for example we could use `Map::<(ContractAddress, ContractAddress), felt252>` to create an allowance on an ERC20 token contract.
+### Composite Keys
-- In mappings, the address of the value at key $k_1,...,k_n$ is $\text{h}(...\text{h}(\text{h}(\text{sn\_keccak}(name),k_1),k_2),...,k_n)$ where $\text{h}$ is the Pedersen hash and the final value is taken $(\bmod {2^{251}} - 256~)$. You can learn more about the contract storage layout in the [Starknet Documentation](https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/contract-storage/#storage_variables).
+For more complex scenarios, you can use composite keys by combining multiple values:
```cairo
-// [!include ~/listings/getting-started/mappings/src/mappings.cairo:contract]
+// Example: ERC20 allowance mapping
+Map<(ContractAddress, ContractAddress), felt252> // (owner, spender) -> amount
```
+
+### Storage Layout (advanced)
+
+Under the hood, Cairo maps use a deterministic storage layout:
+
+- Each key-value pair is stored at a unique address calculated using Pedersen hashes
+- The address formula is: $\text{h}(...\text{h}(\text{h}(\text{sn\_keccak}(name),k_1),k_2),...,k_n)$ mod $2^{251} - 256$
+- Learn more in the [Starknet Documentation](https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/contract-storage/#storage_variables)
diff --git a/routes.ts b/routes.ts
index 70b18183..80e6a99d 100644
--- a/routes.ts
+++ b/routes.ts
@@ -34,10 +34,6 @@ const config: Sidebar = [
text: "Counter Example",
link: "/getting-started/basics/counter",
},
- {
- text: "Mappings",
- link: "/getting-started/basics/mappings",
- },
{
text: "Errors",
link: "/getting-started/basics/errors",
@@ -58,6 +54,10 @@ const config: Sidebar = [
text: "Storing Custom Types",
link: "/getting-started/basics/storing-custom-types",
},
+ {
+ text: "Mappings",
+ link: "/getting-started/basics/mappings",
+ },
{
text: "Custom types in entrypoints",
link: "/getting-started/basics/custom-types-in-entrypoints",
From e1908dab035bdfeedee4d7ef759adc578b768081 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 21 Jan 2025 18:10:21 +0100
Subject: [PATCH 28/44] remove ByteArray/String chapter
---
listings/getting-started/bytearray/.gitignore | 1 -
listings/getting-started/bytearray/Scarb.lock | 6 --
listings/getting-started/bytearray/Scarb.toml | 15 -----
.../bytearray/src/bytearray.cairo | 55 -------------------
.../getting-started/bytearray/src/lib.cairo | 1 -
.../basics/bytearrays-strings.md | 45 ---------------
routes.ts | 4 --
7 files changed, 127 deletions(-)
delete mode 100644 listings/getting-started/bytearray/.gitignore
delete mode 100644 listings/getting-started/bytearray/Scarb.lock
delete mode 100644 listings/getting-started/bytearray/Scarb.toml
delete mode 100644 listings/getting-started/bytearray/src/bytearray.cairo
delete mode 100644 listings/getting-started/bytearray/src/lib.cairo
delete mode 100644 pages/getting-started/basics/bytearrays-strings.md
diff --git a/listings/getting-started/bytearray/.gitignore b/listings/getting-started/bytearray/.gitignore
deleted file mode 100644
index 1de56593..00000000
--- a/listings/getting-started/bytearray/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-target
\ No newline at end of file
diff --git a/listings/getting-started/bytearray/Scarb.lock b/listings/getting-started/bytearray/Scarb.lock
deleted file mode 100644
index 60e3a935..00000000
--- a/listings/getting-started/bytearray/Scarb.lock
+++ /dev/null
@@ -1,6 +0,0 @@
-# Code generated by scarb DO NOT EDIT.
-version = 1
-
-[[package]]
-name = "constructor"
-version.workspace = true
diff --git a/listings/getting-started/bytearray/Scarb.toml b/listings/getting-started/bytearray/Scarb.toml
deleted file mode 100644
index 0ed5aa06..00000000
--- a/listings/getting-started/bytearray/Scarb.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "bytearray"
-version.workspace = true
-edition.workspace = true
-
-[dependencies]
-starknet.workspace = true
-
-[dev-dependencies]
-cairo_test.workspace = true
-
-[scripts]
-test.workspace = true
-
-[[target.starknet-contract]]
diff --git a/listings/getting-started/bytearray/src/bytearray.cairo b/listings/getting-started/bytearray/src/bytearray.cairo
deleted file mode 100644
index 03911b70..00000000
--- a/listings/getting-started/bytearray/src/bytearray.cairo
+++ /dev/null
@@ -1,55 +0,0 @@
-#[starknet::interface]
-pub trait IMessage {
- fn append(ref self: TContractState, str: ByteArray);
- fn prepend(ref self: TContractState, str: ByteArray);
-}
-
-// [!region contract]
-#[starknet::contract]
-pub mod MessageContract {
- use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
-
- #[storage]
- struct Storage {
- pub message: ByteArray,
- }
-
- #[constructor]
- fn constructor(ref self: ContractState) {
- self.message.write("World!");
- }
-
- #[abi(embed_v0)]
- impl MessageContract of super::IMessage {
- fn append(ref self: ContractState, str: ByteArray) {
- self.message.write(self.message.read() + str);
- }
-
- fn prepend(ref self: ContractState, str: ByteArray) {
- self.message.write(str + self.message.read());
- }
- }
-}
-// [!endregion contract]
-
-#[cfg(test)]
-mod tests {
- use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
- use bytearray::bytearray::{MessageContract, IMessage};
-
- #[test]
- #[available_gas(2000000000)]
- fn message_contract_tests() {
- let mut state = MessageContract::contract_state_for_testing();
- state.message.write("World!");
-
- let message = state.message.read();
- assert(message == "World!", 'wrong message');
-
- state.append(" Good day, sir!");
- assert(state.message.read() == "World! Good day, sir!", 'wrong message (append)');
-
- state.prepend("Hello, ");
- assert(state.message.read() == "Hello, World! Good day, sir!", 'wrong message (prepend)');
- }
-}
diff --git a/listings/getting-started/bytearray/src/lib.cairo b/listings/getting-started/bytearray/src/lib.cairo
deleted file mode 100644
index f5e70c2b..00000000
--- a/listings/getting-started/bytearray/src/lib.cairo
+++ /dev/null
@@ -1 +0,0 @@
-mod bytearray;
diff --git a/pages/getting-started/basics/bytearrays-strings.md b/pages/getting-started/basics/bytearrays-strings.md
deleted file mode 100644
index 9ea7db86..00000000
--- a/pages/getting-started/basics/bytearrays-strings.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Strings and ByteArrays
-
-In Cairo, there's no native type for strings. Instead, you can use a single `felt252` to store a short string or a `ByteArray` for strings of arbitrary length.
-
-## Short strings
-
-Each character is encoded on 8 bits following the ASCII standard, so it's possible to store up to 31 characters in a single `felt252`.
-
-Short strings are declared with single quotes, like this: `'Hello, World!'`.
-See the [Felt](/cairo_cheatsheet/felt) section for more information about short strings with the `felt252` type.
-
-:::note
-Notice that any short string only use up to 31 bytes, so it's possible to represent any short string with `bytes31`.
-:::
-
-## ByteArray (Long strings)
-
-The `ByteArray` struct is used to store strings of arbitrary length. It contains a field `data` of type `Array` to store a sequence of short strings.
-
-ByteArrays are declared with double quotes, like this: `"Hello, World!"`.
-
-They can be stored in the contract's storage and passed as arguments to entrypoints.
-
-```cairo
-// [!include ~/listings/getting-started/bytearray/src/bytearray.cairo:contract]
-```
-
-### Operations
-
-ByteArrays also provide a set of operations that facilitate the manipulation of strings.
-Here are the available operations on an instance of `ByteArray`:
-
-- `append(mut other: @ByteArray)` - Append another ByteArray to the current one.
-- `append_word(word: felt252, len: usize)` - Append a short string to the ByteArray. You **need to ensure** that `len` is at most 31 and that `word` can be converted to a `bytes31` with maximum `len` bytes/characters.
-- `append_byte(byte: felt252)` - Append a single byte/character to the end of the ByteArray.
-- `len() -> usize` - Get the length of the ByteArray.
-- `at(index: usize) -> Option` - Access the character at the given index.
-- `rev() -> ByteArray` - Return a new ByteArray with the characters of the original one in reverse order.
-- `append_word_rev(word: felt252, len: usize)` - Append a short string to the ByteArray in reverse order. You **need to ensure** again that `len` is at most 31 and that `word` can be converted to a `bytes31` with maximum `len` bytes/characters.
-
-Additionally, there are some operations that can be called as static functions:
-
-- `concat(left: @ByteArray, right: @ByteArray)` - Concatenate two ByteArrays.
-
-Concatenation of `ByteArray` (with `append(mut other: @ByteArray)`) can also be done with the `+` and `+=` operators directly, and access to a specific index can be done with the `[]` operator (with the maximum index being `len() - 1`).
diff --git a/routes.ts b/routes.ts
index 80e6a99d..2f0b226b 100644
--- a/routes.ts
+++ b/routes.ts
@@ -46,10 +46,6 @@ const config: Sidebar = [
text: "Syscalls",
link: "/getting-started/basics/syscalls",
},
- {
- text: "Strings and ByteArrays",
- link: "/getting-started/basics/bytearrays-strings",
- },
{
text: "Storing Custom Types",
link: "/getting-started/basics/storing-custom-types",
From e05dc64b0802d864cb2f85023715633a2673d097 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 21 Jan 2025 18:10:46 +0100
Subject: [PATCH 29/44] feat: migrate errors to snforge
---
Scarb.lock | 3 +
listings/getting-started/errors/Scarb.toml | 3 +-
.../errors/src/custom_errors.cairo | 32 +++++-----
.../errors/src/simple_errors.cairo | 59 ++++++++++++-------
.../errors/src/vault_errors.cairo | 28 ++++-----
5 files changed, 70 insertions(+), 55 deletions(-)
diff --git a/Scarb.lock b/Scarb.lock
index 57afc518..536cdf05 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -99,6 +99,9 @@ version = "0.1.0"
[[package]]
name = "errors"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "events"
diff --git a/listings/getting-started/errors/Scarb.toml b/listings/getting-started/errors/Scarb.toml
index cc12bd89..c84833f8 100644
--- a/listings/getting-started/errors/Scarb.toml
+++ b/listings/getting-started/errors/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/errors/src/custom_errors.cairo b/listings/getting-started/errors/src/custom_errors.cairo
index b287514f..e75f5533 100644
--- a/listings/getting-started/errors/src/custom_errors.cairo
+++ b/listings/getting-started/errors/src/custom_errors.cairo
@@ -1,24 +1,24 @@
#[starknet::interface]
-pub trait ICustomErrorsExample {
+trait ICustomErrors {
fn test_assert(self: @TContractState, i: u256);
fn test_panic(self: @TContractState, i: u256);
}
// [!region contract]
-pub mod Errors {
+mod Errors {
pub const NOT_POSITIVE: felt252 = 'must be greater than 0';
pub const NOT_NULL: felt252 = 'must not be null';
}
#[starknet::contract]
-pub mod CustomErrorsExample {
- use super::Errors;
+mod CustomErrorsContract {
+ use super::{Errors, ICustomErrors};
#[storage]
struct Storage {}
#[abi(embed_v0)]
- impl CustomErrorsExample of super::ICustomErrorsExample {
+ impl CustomErrorsContract of ICustomErrors {
fn test_assert(self: @ContractState, i: u256) {
assert(i > 0, Errors::NOT_POSITIVE);
}
@@ -34,28 +34,24 @@ pub mod CustomErrorsExample {
#[cfg(test)]
mod test {
- use super::{
- CustomErrorsExample, ICustomErrorsExampleDispatcher, ICustomErrorsExampleDispatcherTrait,
- };
- use starknet::syscalls::deploy_syscall;
-
- fn deploy() -> ICustomErrorsExampleDispatcher {
- let (contract_address, _) = deploy_syscall(
- CustomErrorsExample::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
- ICustomErrorsExampleDispatcher { contract_address }
+ use super::{ICustomErrorsDispatcher, ICustomErrorsDispatcherTrait};
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
+
+ fn deploy() -> ICustomErrorsDispatcher {
+ let contract = declare("CustomErrorsContract").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
+ ICustomErrorsDispatcher { contract_address }
}
#[test]
- #[should_panic(expected: ('must not be null', 'ENTRYPOINT_FAILED'))]
+ #[should_panic(expected: 'must not be null')]
fn should_panic() {
let contract = deploy();
contract.test_panic(0);
}
#[test]
- #[should_panic(expected: ('must be greater than 0', 'ENTRYPOINT_FAILED'))]
+ #[should_panic(expected: 'must be greater than 0')]
fn should_assert() {
let contract = deploy();
contract.test_assert(0);
diff --git a/listings/getting-started/errors/src/simple_errors.cairo b/listings/getting-started/errors/src/simple_errors.cairo
index 1477e1c8..bae0f5b4 100644
--- a/listings/getting-started/errors/src/simple_errors.cairo
+++ b/listings/getting-started/errors/src/simple_errors.cairo
@@ -1,28 +1,35 @@
#[starknet::interface]
-pub trait IErrorsExample {
+trait IErrors {
fn test_assert(self: @TContractState, i: u256);
fn test_panic(self: @TContractState, i: u256);
}
// [!region contract]
#[starknet::contract]
-pub mod ErrorsExample {
+mod ErrorsContract {
+ use super::IErrors;
+
#[storage]
struct Storage {}
#[abi(embed_v0)]
- impl ErrorsExample of super::IErrorsExample {
+ impl ErrorsContract of IErrors {
+ // Assert used to validate a condition
+ // and abort execution if the condition is not met
fn test_assert(self: @ContractState, i: u256) {
- // Assert used to validate a condition
- // and abort execution if the condition is not met
assert(i > 0, 'i must be greater than 0');
+ let x = 10;
+ assert!(i > x, "i must be greater than {}", x);
}
+ // Panic used to abort execution directly
fn test_panic(self: @ContractState, i: u256) {
if (i == 0) {
- // Panic used to abort execution directly
core::panic_with_felt252('i must not be 0');
}
+ if (i < 10) {
+ panic!("i: {} must be greater than 10", i);
+ }
}
}
}
@@ -30,28 +37,40 @@ pub mod ErrorsExample {
#[cfg(test)]
mod test {
- use super::{ErrorsExample, IErrorsExampleDispatcher, IErrorsExampleDispatcherTrait};
- use starknet::syscalls::deploy_syscall;
-
- fn deploy() -> IErrorsExampleDispatcher {
- let (contract_address, _) = deploy_syscall(
- ErrorsExample::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
- IErrorsExampleDispatcher { contract_address }
+ use super::{IErrorsDispatcher, IErrorsDispatcherTrait};
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
+
+ fn deploy() -> IErrorsDispatcher {
+ let contract = declare("ErrorsContract").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
+ IErrorsDispatcher { contract_address }
}
#[test]
- #[should_panic(expected: ('i must not be 0', 'ENTRYPOINT_FAILED'))]
- fn should_panic() {
+ #[should_panic(expected: 'i must be greater than 0')]
+ fn should_assert_with_felt() {
+ let contract = deploy();
+ contract.test_assert(0);
+ }
+
+ #[test]
+ #[should_panic(expected: "i must be greater than 10")]
+ fn should_assert_with_byte_array() {
+ let contract = deploy();
+ contract.test_assert(5);
+ }
+
+ #[test]
+ #[should_panic(expected: 'i must not be 0')]
+ fn should_panic_with_felt() {
let contract = deploy();
contract.test_panic(0);
}
#[test]
- #[should_panic(expected: ('i must be greater than 0', 'ENTRYPOINT_FAILED'))]
- fn should_assert() {
+ #[should_panic(expected: "i: 5 must be greater than 10")]
+ fn should_panic_with_byte_array() {
let contract = deploy();
- contract.test_assert(0);
+ contract.test_panic(5);
}
}
diff --git a/listings/getting-started/errors/src/vault_errors.cairo b/listings/getting-started/errors/src/vault_errors.cairo
index 4da8a4e8..c9db40e3 100644
--- a/listings/getting-started/errors/src/vault_errors.cairo
+++ b/listings/getting-started/errors/src/vault_errors.cairo
@@ -1,19 +1,19 @@
#[starknet::interface]
-pub trait IVaultErrorsExample {
+trait IVaultErrors {
fn deposit(ref self: TContractState, amount: u256);
fn withdraw(ref self: TContractState, amount: u256);
}
// [!region contract]
-pub mod VaultErrors {
+mod VaultErrors {
pub const INSUFFICIENT_BALANCE: felt252 = 'insufficient_balance';
// you can define more errors here
}
#[starknet::contract]
-pub mod VaultErrorsExample {
+mod VaultErrorsContract {
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
- use super::VaultErrors;
+ use super::{VaultErrors, IVaultErrors};
#[storage]
struct Storage {
@@ -21,7 +21,7 @@ pub mod VaultErrorsExample {
}
#[abi(embed_v0)]
- impl VaultErrorsExample of super::IVaultErrorsExample {
+ impl VaultErrorsContract of IVaultErrors {
fn deposit(ref self: ContractState, amount: u256) {
let mut balance = self.balance.read();
balance = balance + amount;
@@ -48,17 +48,13 @@ pub mod VaultErrorsExample {
#[cfg(test)]
mod test {
- use super::{
- VaultErrorsExample, IVaultErrorsExampleDispatcher, IVaultErrorsExampleDispatcherTrait,
- };
- use starknet::syscalls::deploy_syscall;
+ use super::{IVaultErrorsDispatcher, IVaultErrorsDispatcherTrait};
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
- fn deploy() -> IVaultErrorsExampleDispatcher {
- let (contract_address, _) = deploy_syscall(
- VaultErrorsExample::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
- IVaultErrorsExampleDispatcher { contract_address }
+ fn deploy() -> IVaultErrorsDispatcher {
+ let contract = declare("VaultErrorsContract").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
+ IVaultErrorsDispatcher { contract_address }
}
#[test]
@@ -69,7 +65,7 @@ mod test {
}
#[test]
- #[should_panic(expected: ('insufficient_balance', 'ENTRYPOINT_FAILED'))]
+ #[should_panic(expected: 'insufficient_balance')]
fn should_panic_on_insufficient_balance() {
let mut contract = deploy();
contract.deposit(10);
From 41cdc06de0f3e912518007643aff32a27da61a4e Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 21 Jan 2025 18:11:11 +0100
Subject: [PATCH 30/44] feat: rework errors
---
pages/getting-started/basics/errors.md | 57 ++++++++++++++++++--------
1 file changed, 41 insertions(+), 16 deletions(-)
diff --git a/pages/getting-started/basics/errors.md b/pages/getting-started/basics/errors.md
index 600985e9..0c0190c5 100644
--- a/pages/getting-started/basics/errors.md
+++ b/pages/getting-started/basics/errors.md
@@ -1,38 +1,63 @@
-# Errors
+# Error Handling in Cairo
-Errors can be used to handle validation and other conditions that may occur during the execution of a smart contract.
-If an error is thrown during the execution of a smart contract call, the execution is stopped and any changes made during the transaction are reverted.
+Cairo provides robust error handling mechanisms for smart contracts. When an error occurs during contract execution, the transaction is immediately reverted and all state changes are undone.
-To throw an error, use the `assert` or `panic` functions:
+## Basic Error Functions
-- `assert` is used to validate conditions.
- If the check fails, an error is thrown along with a specified value, often a message.
- It's similar to the `require` statement in Solidity.
+Cairo offers two main functions for error handling:
-- `panic` immediately halts the execution with the given error value.
- It should be used for complex condition checks and for internal errors. It's similar to the `revert` statement in Solidity.
- You can use `panic_with_felt252` to directly pass a `felt252` as the error value.
+### 1. `assert`
-The `assert_eq!`, `assert_ne!`, `assert_lt!`, `assert_le!`, `assert_gt!` and `assert_ge!` macros can be used as an `assert` shorthand to compare two values, but **only** in tests. In contracts, you should only use the `assert` function.
+- Used for condition validation (similar to Solidity's `require`)
+- Stops execution if the condition is false
+- Supports two formats:
-Here's a simple example that demonstrates the use of these functions:
+ ```cairo
+ assert(condition, 'error message'); // Basic assertion
+ assert!(condition, "formatted error: {}", x); // Formatted string error
+ ```
+
+### 2. `panic`
+
+- Used for immediate execution halt (similar to Solidity's `revert`)
+- Best for complex conditions or internal errors
+- Supports multiple formats:
+
+ ```cairo
+ panic_with_felt252('error message'); // Basic panic
+ panic!("formatted error: value={}", value); // Formatted string error
+ ```
+
+:::warning
+While Cairo provides assertion macros like `assert_eq!` and `assert_ne!`, these are **only for testing**. In contract code, always use the standard `assert` function.
+:::
+
+## Simple Example
+
+Here's a basic example demonstrating both error handling approaches:
```cairo
// [!include ~/listings/getting-started/errors/src/simple_errors.cairo:contract]
```
-## Custom errors
+## Custom Error Codes
-You can make error handling easier by defining your error codes in a specific module.
+For better organization and consistency, you can define error messages in a dedicated module:
```cairo
// [!include ~/listings/getting-started/errors/src/custom_errors.cairo:contract]
```
-## Vault example
+## Real-World Example: Vault Contract
-Here's another example that demonstrates the use of errors in a more complex contract:
+Here's a practical example showing error handling in a vault contract that manages deposits and withdrawals:
```cairo
// [!include ~/listings/getting-started/errors/src/vault_errors.cairo:contract]
```
+
+In this example:
+
+1. Custom errors are defined in a separate module
+2. The `withdraw` function demonstrates both `assert` and `panic` approaches
+3. Balance checks protect against underflow conditions
From 93845ffb69c9f89c8cd11f2d2b1da94e52e62316 Mon Sep 17 00:00:00 2001
From: Julio <30329843+julio4@users.noreply.github.com>
Date: Tue, 21 Jan 2025 18:20:51 +0100
Subject: [PATCH 31/44] Update README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index bf09c9e4..ee29065f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# Starknet by Example
+Dev preview at: https://starknet-by-example-dev.voyager.online/
+
## Description
Starknet by Example is a collection of examples of how to use the [Cairo](https://github.com/starkware-libs/cairo) programming language to create smart contracts on Starknet.
From 93ee3fb3356e3a84a2aa47b3c02beb032c6b3e18 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Wed, 22 Jan 2025 13:32:43 +0100
Subject: [PATCH 32/44] feat: migrate events to snforge
---
Scarb.lock | 7 +-
listings/getting-started/events/Scarb.toml | 3 +-
.../getting-started/events/src/counter.cairo | 110 ++++++++++--------
3 files changed, 64 insertions(+), 56 deletions(-)
diff --git a/Scarb.lock b/Scarb.lock
index 536cdf05..00726fac 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -18,10 +18,6 @@ dependencies = [
"snforge_std",
]
-[[package]]
-name = "bytearray"
-version = "0.1.0"
-
[[package]]
name = "cairo_cheatsheet"
version = "0.1.0"
@@ -106,6 +102,9 @@ dependencies = [
[[package]]
name = "events"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "factory"
diff --git a/listings/getting-started/events/Scarb.toml b/listings/getting-started/events/Scarb.toml
index 35c9f343..3cd2e428 100644
--- a/listings/getting-started/events/Scarb.toml
+++ b/listings/getting-started/events/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/events/src/counter.cairo b/listings/getting-started/events/src/counter.cairo
index 612c81e5..e391961b 100644
--- a/listings/getting-started/events/src/counter.cairo
+++ b/listings/getting-started/events/src/counter.cairo
@@ -1,18 +1,37 @@
+// [!region contract]
#[starknet::interface]
-pub trait IEventCounter {
+trait IEventCounter {
fn increment(ref self: TContractState, amount: u128);
}
-// [!region contract]
+mod Events {
+ // Events must derive the `starknet::Event` trait
+ #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)]
+ pub struct CounterIncreased {
+ pub amount: u128,
+ }
+
+ #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)]
+ pub struct UserIncreaseCounter {
+ // The `#[key]` attribute indicates that this event will be indexed.
+ // You can also use `#[flat]` for nested structs.
+ #[key]
+ pub user: starknet::ContractAddress,
+ pub new_value: u128,
+ }
+}
+
#[starknet::contract]
-pub mod EventCounter {
- use starknet::{get_caller_address, ContractAddress};
+mod EventCounter {
+ use super::IEventCounter;
+ use super::Events::*;
+ use starknet::get_caller_address;
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
#[storage]
struct Storage {
// Counter value
- pub counter: u128,
+ counter: u128,
}
#[event]
@@ -24,24 +43,8 @@ pub mod EventCounter {
UserIncreaseCounter: UserIncreaseCounter,
}
- // By deriving the `starknet::Event` trait, we indicate to the compiler that
- // this struct will be used when emitting events.
- #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)]
- pub struct CounterIncreased {
- pub amount: u128,
- }
-
- #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)]
- pub struct UserIncreaseCounter {
- // The `#[key]` attribute indicates that this event will be indexed.
- // You can also use `#[flat]` for nested structs.
- #[key]
- pub user: ContractAddress,
- pub new_value: u128,
- }
-
#[abi(embed_v0)]
- impl EventCounter of super::IEventCounter {
+ impl EventCounter of IEventCounter {
fn increment(ref self: ContractState, amount: u128) {
self.counter.write(self.counter.read() + amount);
// Emit event
@@ -64,44 +67,49 @@ pub mod EventCounter {
#[cfg(test)]
mod tests {
use super::{
- EventCounter, EventCounter::{Event, CounterIncreased, UserIncreaseCounter},
- IEventCounterDispatcherTrait, IEventCounterDispatcher,
+ EventCounter::{Event, CounterIncreased, UserIncreaseCounter}, IEventCounterDispatcherTrait,
+ IEventCounterDispatcher,
};
- use starknet::{contract_address_const, syscalls::deploy_syscall};
- use starknet::testing::set_contract_address;
- use starknet::storage::StoragePointerReadAccess;
+ use starknet::contract_address_const;
+
+ use snforge_std::{
+ EventSpyAssertionsTrait, spy_events, start_cheat_caller_address, stop_cheat_caller_address,
+ ContractClassTrait, DeclareResultTrait, declare,
+ };
+
+ fn deploy() -> IEventCounterDispatcher {
+ let contract = declare("EventCounter").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
+ IEventCounterDispatcher { contract_address }
+ }
#[test]
fn test_increment_events() {
- let (contract_address, _) = deploy_syscall(
- EventCounter::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
- let mut contract = IEventCounterDispatcher { contract_address };
- let state = @EventCounter::contract_state_for_testing();
-
+ let mut contract = deploy();
let amount = 10;
let caller = contract_address_const::<'caller'>();
- // fake caller
- set_contract_address(caller);
+ // [!region test_events]
+ let mut spy = spy_events();
+ start_cheat_caller_address(contract.contract_address, caller);
contract.increment(amount);
- // set back to the contract for reading state
- set_contract_address(contract_address);
- assert_eq!(state.counter.read(), amount);
+ stop_cheat_caller_address(contract.contract_address);
- // Notice the order: the first event emitted is the first to be popped.
- // [!region test_events]
- assert_eq!(
- starknet::testing::pop_log(contract_address),
- Option::Some(Event::CounterIncreased(CounterIncreased { amount })),
- );
+ spy
+ .assert_emitted(
+ @array![
+ (
+ contract.contract_address,
+ Event::CounterIncreased(CounterIncreased { amount }),
+ ),
+ (
+ contract.contract_address,
+ Event::UserIncreaseCounter(
+ UserIncreaseCounter { user: caller, new_value: amount },
+ ),
+ ),
+ ],
+ );
// [!endregion test_events]
- assert_eq!(
- starknet::testing::pop_log(contract_address),
- Option::Some(
- Event::UserIncreaseCounter(UserIncreaseCounter { user: caller, new_value: amount }),
- ),
- );
}
}
From a4fa6b4492627ff372bc711b078ebb6b5b18d97d Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Thu, 23 Jan 2025 13:52:22 +0100
Subject: [PATCH 33/44] feat: rework events
---
pages/getting-started/basics/events.md | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/pages/getting-started/basics/events.md b/pages/getting-started/basics/events.md
index 4dabcd7a..06b12399 100644
--- a/pages/getting-started/basics/events.md
+++ b/pages/getting-started/basics/events.md
@@ -1,10 +1,26 @@
# Events
-Events are a way to emit data from a contract. All events must be defined in the `Event` enum, which must be annotated with the `#[event]` attribute.
-An event is defined as a struct that derives the `starknet::Event` trait. The fields of that struct correspond to the data that will be emitted. An event can be indexed for easy and fast access when querying the data at a later time by adding a `#[key]` attribute to a field member.
+Events in Cairo smart contracts allow you to emit and record data on the Starknet blockchain. They are essential for tracking important state changes and providing transparency to users and other contracts. Events are also useful when building interfaces, to be notified about important state changes.
-Here's a simple example of a contract that emits an event each time a counter is incremented by the `increment` function:
+To use events in your contract:
+
+1. Create event structs that derive the `starknet::Event` trait
+2. Define an `Event` enum in the contract, annotated with `#[event]`, where each variant is linked to an event struct
+3. Emit events with the `emit` function
+
+You can make events searchable by adding the `#[key]` attribute to specific fields, which indexes them for efficient querying later.
+
+Events variant names and structs are recommended to be named consistently, even if it create some redundancy when emitting events.
+
+Here's a practical example of a contract that emits events when incrementing a counter:
```cairo
// [!include ~/listings/getting-started/events/src/counter.cairo:contract]
```
+
+:::note
+
+For better code organization, especially in larger contracts, you can define event structs outside of the contract module, as shown in the example here.
+While this allows you to group related events in separate modules or files, remember that you must still include all event variants in the contract's `Event` enum.
+
+:::
From c35893201783bac17ad938c92fde411cd2d3bcfb Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Thu, 23 Jan 2025 14:02:45 +0100
Subject: [PATCH 34/44] feat: migrate storing_custom_types to snforge
---
Scarb.lock | 3 ++
.../storing_custom_types/Scarb.toml | 3 +-
.../storing_custom_types/src/contract.cairo | 47 +++++++++----------
routes.ts | 12 ++---
4 files changed, 33 insertions(+), 32 deletions(-)
diff --git a/Scarb.lock b/Scarb.lock
index 00726fac..af2ff3d3 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -322,6 +322,9 @@ version = "0.1.0"
[[package]]
name = "storing_custom_types"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "struct_as_mapping_key"
diff --git a/listings/getting-started/storing_custom_types/Scarb.toml b/listings/getting-started/storing_custom_types/Scarb.toml
index 44f881b8..17eda441 100644
--- a/listings/getting-started/storing_custom_types/Scarb.toml
+++ b/listings/getting-started/storing_custom_types/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/storing_custom_types/src/contract.cairo b/listings/getting-started/storing_custom_types/src/contract.cairo
index cd2ad282..d5f6986a 100644
--- a/listings/getting-started/storing_custom_types/src/contract.cairo
+++ b/listings/getting-started/storing_custom_types/src/contract.cairo
@@ -1,30 +1,31 @@
+// [!region contract]
#[starknet::interface]
-pub trait IStoringCustomType {
+trait IStoringCustomType {
fn set_person(ref self: TContractState, person: Person);
fn set_name(ref self: TContractState, name: felt252);
}
-// [!region contract]
// Deriving the starknet::Store trait
// allows us to store the `Person` struct in the contract's storage.
#[derive(Drop, Serde, Copy, starknet::Store)]
-pub struct Person {
- pub age: u8,
- pub name: felt252,
+struct Person {
+ age: u8,
+ name: felt252,
}
#[starknet::contract]
-pub mod StoringCustomType {
+mod StoringCustomType {
use starknet::storage::StoragePointerWriteAccess;
use super::Person;
+ use super::IStoringCustomType;
#[storage]
struct Storage {
- pub person: Person,
+ person: Person,
}
#[abi(embed_v0)]
- impl StoringCustomType of super::IStoringCustomType {
+ impl StoringCustomType of IStoringCustomType {
fn set_person(ref self: ContractState, person: Person) {
self.person.write(person);
}
@@ -40,30 +41,26 @@ pub mod StoringCustomType {
#[cfg(test)]
mod tests {
- use super::{IStoringCustomType, StoringCustomType, Person};
- use starknet::storage::StoragePointerReadAccess;
+ use super::{IStoringCustomTypeDispatcherTrait, IStoringCustomTypeDispatcher, Person};
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
+
+ fn deploy() -> IStoringCustomTypeDispatcher {
+ let contract = declare("StoringCustomType").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
+ IStoringCustomTypeDispatcher { contract_address }
+ }
+
#[test]
fn can_call_set_person() {
- let mut state = StoringCustomType::contract_state_for_testing();
-
+ let mut contract = deploy();
let person = Person { age: 10, name: 'Joe' };
-
- state.set_person(person);
-
- let read_person = state.person.read();
-
- assert_eq!(person.age, read_person.age);
- assert_eq!(person.name, read_person.name);
+ contract.set_person(person);
}
#[test]
fn can_call_set_name() {
- let mut state = StoringCustomType::contract_state_for_testing();
-
- state.set_name('John');
-
- let read_person = state.person.read();
- assert_eq!(read_person.name, 'John');
+ let mut contract = deploy();
+ contract.set_name('John');
}
}
diff --git a/routes.ts b/routes.ts
index 2f0b226b..99e74560 100644
--- a/routes.ts
+++ b/routes.ts
@@ -42,21 +42,21 @@ const config: Sidebar = [
text: "Events",
link: "/getting-started/basics/events",
},
- {
- text: "Syscalls",
- link: "/getting-started/basics/syscalls",
- },
{
text: "Storing Custom Types",
link: "/getting-started/basics/storing-custom-types",
},
+ {
+ text: "Custom Types in Entrypoints",
+ link: "/getting-started/basics/custom-types-in-entrypoints",
+ },
{
text: "Mappings",
link: "/getting-started/basics/mappings",
},
{
- text: "Custom types in entrypoints",
- link: "/getting-started/basics/custom-types-in-entrypoints",
+ text: "Syscalls",
+ link: "/getting-started/basics/syscalls",
},
{
text: "Documentation",
From c2c3c29aa6c00b60bcfb4088c13759f199fb0d69 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Thu, 23 Jan 2025 14:23:13 +0100
Subject: [PATCH 35/44] feat: rework storing_custom_types
---
.../basics/storing-custom-types.md | 14 ---------
.../basics/storing_custom_types.md | 29 +++++++++++++++++++
2 files changed, 29 insertions(+), 14 deletions(-)
delete mode 100644 pages/getting-started/basics/storing-custom-types.md
create mode 100644 pages/getting-started/basics/storing_custom_types.md
diff --git a/pages/getting-started/basics/storing-custom-types.md b/pages/getting-started/basics/storing-custom-types.md
deleted file mode 100644
index c14093a1..00000000
--- a/pages/getting-started/basics/storing-custom-types.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Storing Custom Types
-
-While native types can be stored in a contract's storage without any additional work, custom types require a bit more work. This is because at compile time, the compiler does not know how to store custom types in storage. To solve this, we need to implement the `Store` trait for our custom type. It is enough to just derive this trait, unless our custom type contains arrays or dictionaries.
-
-```cairo
-// [!include ~/listings/getting-started/storing_custom_types/src/contract.cairo:contract]
-```
-
-Note that it is also possible to individually access the members of the stored struct.
-This is possible because deriving the `Store` trait also generates the corresponding `StoragePointer` for each member.
-
-```cairo
-// [!include ~/listings/getting-started/storing_custom_types/src/contract.cairo:set_name]
-```
diff --git a/pages/getting-started/basics/storing_custom_types.md b/pages/getting-started/basics/storing_custom_types.md
new file mode 100644
index 00000000..842502d2
--- /dev/null
+++ b/pages/getting-started/basics/storing_custom_types.md
@@ -0,0 +1,29 @@
+# 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:
+
+1. Derive the `starknet::Store` trait for your struct
+2. Add any other necessary traits like `Drop`, `Serde`, and `Copy`
+3. Define your storage variables using the custom type
+
+Here's an example showing how to store a custom `Person` struct:
+
+```cairo
+// [!include ~/listings/getting-started/storing_custom_types/src/contract.cairo:contract]
+```
+
+:::note
+
+For more complex types, you might need to implement the `Store` trait manually instead of deriving it.
+
+:::
+
+## 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:
+
+```cairo
+// [!include ~/listings/getting-started/storing_custom_types/src/contract.cairo:set_name]
+```
From d13e155d1fdcf98decbb9d4a06254474a4b8f804 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Fri, 24 Jan 2025 17:42:13 +0100
Subject: [PATCH 36/44] feat: migrate custom_type_entrypoints to snforge
---
Scarb.lock | 5 ++-
.../.gitignore | 0
.../Scarb.lock | 0
.../Scarb.toml | 5 ++-
.../src/contract.cairo | 40 ++++++++-----------
.../src/lib.cairo | 0
.../basics/custom-types-in-entrypoints.md | 2 +-
7 files changed, 24 insertions(+), 28 deletions(-)
rename listings/getting-started/{custom_type_serde => custom_type_entrypoints}/.gitignore (100%)
rename listings/getting-started/{custom_type_serde => custom_type_entrypoints}/Scarb.lock (100%)
rename listings/getting-started/{custom_type_serde => custom_type_entrypoints}/Scarb.toml (66%)
rename listings/getting-started/{custom_type_serde => custom_type_entrypoints}/src/contract.cairo (60%)
rename listings/getting-started/{custom_type_serde => custom_type_entrypoints}/src/lib.cairo (100%)
diff --git a/Scarb.lock b/Scarb.lock
index af2ff3d3..34dddc6e 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -81,8 +81,11 @@ dependencies = [
]
[[package]]
-name = "custom_type_serde"
+name = "custom_type_entrypoints"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "ecdsa_verification"
diff --git a/listings/getting-started/custom_type_serde/.gitignore b/listings/getting-started/custom_type_entrypoints/.gitignore
similarity index 100%
rename from listings/getting-started/custom_type_serde/.gitignore
rename to listings/getting-started/custom_type_entrypoints/.gitignore
diff --git a/listings/getting-started/custom_type_serde/Scarb.lock b/listings/getting-started/custom_type_entrypoints/Scarb.lock
similarity index 100%
rename from listings/getting-started/custom_type_serde/Scarb.lock
rename to listings/getting-started/custom_type_entrypoints/Scarb.lock
diff --git a/listings/getting-started/custom_type_serde/Scarb.toml b/listings/getting-started/custom_type_entrypoints/Scarb.toml
similarity index 66%
rename from listings/getting-started/custom_type_serde/Scarb.toml
rename to listings/getting-started/custom_type_entrypoints/Scarb.toml
index 1a3dfe62..e7d79c8d 100644
--- a/listings/getting-started/custom_type_serde/Scarb.toml
+++ b/listings/getting-started/custom_type_entrypoints/Scarb.toml
@@ -1,5 +1,5 @@
[package]
-name = "custom_type_serde"
+name = "custom_type_entrypoints"
version.workspace = true
edition.workspace = true
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/custom_type_serde/src/contract.cairo b/listings/getting-started/custom_type_entrypoints/src/contract.cairo
similarity index 60%
rename from listings/getting-started/custom_type_serde/src/contract.cairo
rename to listings/getting-started/custom_type_entrypoints/src/contract.cairo
index abd441ea..a6d4c42a 100644
--- a/listings/getting-started/custom_type_serde/src/contract.cairo
+++ b/listings/getting-started/custom_type_entrypoints/src/contract.cairo
@@ -1,27 +1,28 @@
+// [!region contract]
#[starknet::interface]
-pub trait ISerdeCustomType {
+trait ISerdeCustomType {
fn person_input(ref self: TContractState, person: Person);
fn person_output(self: @TContractState) -> Person;
}
-// [!region contract]
// Deriving the `Serde` trait allows us to use
// the `Person` type as an entrypoint parameter and as a return value
#[derive(Drop, Serde)]
-pub struct Person {
- pub age: u8,
- pub name: felt252,
+struct Person {
+ age: u8,
+ name: felt252,
}
#[starknet::contract]
-pub mod SerdeCustomType {
+mod SerdeCustomType {
use super::Person;
+ use super::ISerdeCustomType;
#[storage]
struct Storage {}
#[abi(embed_v0)]
- impl SerdeCustomType of super::ISerdeCustomType {
+ impl SerdeCustomType of ISerdeCustomType {
fn person_input(ref self: ContractState, person: Person) {}
fn person_output(self: @ContractState) -> Person {
@@ -33,40 +34,31 @@ pub mod SerdeCustomType {
#[cfg(test)]
mod tests {
- use super::{
- SerdeCustomType, Person, ISerdeCustomTypeDispatcher, ISerdeCustomTypeDispatcherTrait,
- };
- use starknet::syscalls::deploy_syscall;
+ use super::{ISerdeCustomTypeDispatcher, ISerdeCustomTypeDispatcherTrait, Person};
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
fn deploy() -> ISerdeCustomTypeDispatcher {
- let (contract_address, _) = deploy_syscall(
- SerdeCustomType::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
+ let contract = declare("SerdeCustomType").unwrap().contract_class();
+ let (contract_address, _) = contract.deploy(@array![]).unwrap();
ISerdeCustomTypeDispatcher { contract_address }
}
- #[test]
- fn should_deploy() {
- deploy();
- }
-
#[test]
fn should_get_person_output() {
let contract = deploy();
let expected_person = Person { age: 10, name: 'Joe' };
+
let received_person = contract.person_output();
let age_received = received_person.age;
let name_received = received_person.name;
- assert(age_received == expected_person.age, 'Wrong age value');
- assert(name_received == expected_person.name, 'Wrong name value');
+ assert_eq!(age_received, expected_person.age);
+ assert_eq!(name_received, expected_person.name);
}
#[test]
- #[available_gas(2000000000)]
fn should_call_person_input() {
- let contract = deploy();
+ let mut contract = deploy();
let expected_person = Person { age: 10, name: 'Joe' };
contract.person_input(expected_person);
}
diff --git a/listings/getting-started/custom_type_serde/src/lib.cairo b/listings/getting-started/custom_type_entrypoints/src/lib.cairo
similarity index 100%
rename from listings/getting-started/custom_type_serde/src/lib.cairo
rename to listings/getting-started/custom_type_entrypoints/src/lib.cairo
diff --git a/pages/getting-started/basics/custom-types-in-entrypoints.md b/pages/getting-started/basics/custom-types-in-entrypoints.md
index c54b0691..8ec76b8d 100644
--- a/pages/getting-started/basics/custom-types-in-entrypoints.md
+++ b/pages/getting-started/basics/custom-types-in-entrypoints.md
@@ -4,7 +4,7 @@ Using custom types in entrypoints requires our type to implement the `Serde` tra
Thankfully, we can just derive the `Serde` trait for our custom type.
```cairo
-// [!include ~/listings/getting-started/custom_type_serde/src/contract.cairo:contract]
+// [!include ~/listings/getting-started/custom_type_entrypoints/src/contract.cairo:contract]
```
:::note
From b9d04503373f5b33b1e4fc66d35abafa64d14da5 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Fri, 24 Jan 2025 18:06:44 +0100
Subject: [PATCH 37/44] feat: rework custom_type_entrypoints
---
.../basics/custom-types-in-entrypoints.md | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/pages/getting-started/basics/custom-types-in-entrypoints.md b/pages/getting-started/basics/custom-types-in-entrypoints.md
index 8ec76b8d..4dc59e2a 100644
--- a/pages/getting-started/basics/custom-types-in-entrypoints.md
+++ b/pages/getting-started/basics/custom-types-in-entrypoints.md
@@ -1,12 +1,21 @@
-# Custom types in entrypoints
+# Custom Types in Entrypoints
-Using custom types in entrypoints requires our type to implement the `Serde` trait. This is because when calling an entrypoint, the input is sent as an array of `felt252` to the entrypoint, and we need to be able to deserialize it into our custom type. Similarly, when returning a custom type from an entrypoint, we need to be able to serialize it into an array of `felt252`.
-Thankfully, we can just derive the `Serde` trait for our custom type.
+When using custom types in Starknet contract entrypoints, you need to handle serialization and deserialization of data. This is because:
+
+1. Input parameters are sent to entrypoints as arrays of `felt252`
+2. Return values must be converted back to arrays of `felt252`
+3. 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:
```cairo
// [!include ~/listings/getting-started/custom_type_entrypoints/src/contract.cairo:contract]
```
:::note
-The purpose of this example is to demonstrate the ability to use custom types as inputs and outputs in contract calls. For simplicity, we are not using getters and setters to manage the contract's state.
+For some complex types, you might need to implement the `Serde` trait manually. This gives you control over how your type is serialized and deserialized.
+
+The `Serde` trait is distinct from the `Store` trait - `Serde` is for passing data in and out of entrypoints, while `Store` is for persisting data in contract storage.
:::
From 24d3d089056e3c2c9f507b56587e180b2c4d279f Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Sat, 25 Jan 2025 10:32:45 +0100
Subject: [PATCH 38/44] feat: update syscalls (close #283)
---
pages/getting-started/basics/syscalls.md | 273 -----------------------
pages/getting-started/syscalls.md | 250 +++++++++++++++++++++
routes.ts | 8 +-
3 files changed, 254 insertions(+), 277 deletions(-)
delete mode 100644 pages/getting-started/basics/syscalls.md
create mode 100644 pages/getting-started/syscalls.md
diff --git a/pages/getting-started/basics/syscalls.md b/pages/getting-started/basics/syscalls.md
deleted file mode 100644
index bacccda4..00000000
--- a/pages/getting-started/basics/syscalls.md
+++ /dev/null
@@ -1,273 +0,0 @@
-# Syscalls
-
-At the protocol level, the Starknet Operating System (OS) is the program that manages the whole Starknet network.
-
-Some of the OS functionalities are exposed to smart contracts through the use of syscalls (system calls). Syscalls can be used to get information about the state of the Starknet network, to interact with/deploy contracts, emit events, send messages, and perform other low-level operations.
-
-Syscalls return a `SyscallResult` which is either `Success` or `Failure`, allowing the contract to handle errors.
-
-Here's the available syscalls:
-
-- [get_block_hash](#get_block_hash)
-- [get_execution_info](#get_execution_info)
-- [call_contract](#call_contract)
-- [deploy](#deploy)
-- [emit_event](#emit_event)
-- [library_call](#library_call)
-- [send_message_to_L1](#send_message_to_L1)
-- [replace_class](#replace_class)
-- [storage_read](#storage_read)
-- [storage_write](#storage_write)
-
-
-#### get_block_hash
-
-```cairo
-fn get_block_hash_syscall(block_number: u64) -> SyscallResult
-```
-
-Get the hash of the block number `block_number`.
-
-Only within the range `[first_v0_12_0_block, current_block - 10]`.
-
-#### get_execution_info
-
-```cairo
-fn get_execution_info_syscall() -> SyscallResult>
-```
-
-Get information about the current execution context.
-The returned `ExecutionInfo` is defined as :
-
-```cairo
-#[derive(Copy, Drop, Debug)]
-pub struct ExecutionInfo {
- pub block_info: Box,
- pub tx_info: Box,
- pub caller_address: ContractAddress,
- pub contract_address: ContractAddress,
- pub entry_point_selector: felt252,
-}
-
-#[derive(Copy, Drop, Debug, Serde)]
-pub struct BlockInfo {
- pub block_number: u64,
- pub block_timestamp: u64,
- pub sequencer_address: ContractAddress,
-}
-
-#[derive(Copy, Drop, Debug, Serde)]
-pub struct TxInfo {
- // The version of the transaction. Always fixed (1)
- pub version: felt252,
- // The account contract from which this transaction originates.
- pub account_contract_address: ContractAddress,
- // The max_fee field of the transaction.
- pub max_fee: u128,
- // The signature of the transaction.
- pub signature: Span,
- // The hash of the transaction.
- pub transaction_hash: felt252,
- // The identifier of the chain.
- // This field can be used to prevent replay of testnet transactions on mainnet.
- pub chain_id: felt252,
- // The transaction's nonce.
- pub nonce: felt252,
- // A span of ResourceBounds structs.
- pub resource_bounds: Span,
- // The tip.
- pub tip: u128,
- // If specified, the paymaster should pay for the execution of the tx.
- // The data includes the address of the paymaster sponsoring the transaction, followed by
- // extra data to send to the paymaster.
- pub paymaster_data: Span,
- // The data availability mode for the nonce.
- pub nonce_data_availability_mode: u32,
- // The data availability mode for the account balance from which fee will be taken.
- pub fee_data_availability_mode: u32,
- // If nonempty, will contain the required data for deploying and initializing an account
- // contract: its class hash, address salt and constructor calldata.
- pub account_deployment_data: Span,
-}
-```
-
-`starknet::info` provides helper functions to access the `ExecutionInfo` fields in a more convenient way:
-
-- `get_execution_info() -> Box`
-- `get_caller_address() -> ContractAddress`
-- `get_contract_address() -> ContractAddress`
-- `get_block_info() -> Box`
-- `get_tx_info() -> Box`
-- `get_block_timestamp() -> u64`
-- `get_block_number() -> u64`
-
-#### call_contract
-
-```cairo
-fn call_contract_syscall(
- address: ContractAddress, entry_point_selector: felt252, calldata: Span
-) -> SyscallResult>
-```
-
-Call a contract at `address` with the given `entry_point_selector` and `calldata`.
-Failure can't be caught for this syscall, and if the call fails, the whole transaction will revert.
-
-This is not the recommended way to call a contract. Instead, use the dispatcher generated from the contract interface as shown in the [Calling other contracts](/getting-started/interacting/calling_other_contracts) chapter.
-
-
-
-#### deploy
-
-```cairo
-fn deploy_syscall(
- class_hash: ClassHash,
- contract_address_salt: felt252,
- calldata: Span,
- deploy_from_zero: bool,
-) -> SyscallResult<(ContractAddress, Span::)>
-```
-
-Deploy a new contract of the predeclared class `class_hash` with `calldata`.
-The success result is a tuple containing the deployed contract address and the return value of the constructor.
-
-`contract_address_salt` and `deploy_from_zero` are used to compute the contract address.
-
-Example of the usage of the `deploy` syscall from the [Factory pattern](/getting-started/interacting/factory):
-
-```cairo
-// [!include ~/listings/getting-started/factory/src/simple_factory.cairo:deploy]
-```
-
-#### emit_event
-
-```cairo
-fn emit_event_syscall(
- keys: Span, data: Span
-) -> SyscallResult<()>
-```
-
-Emit an event with the given `keys` and `data`.
-
-Example of the usage of the `emit_event` syscall from the [Events](/getting-started/basics/events) chapter:
-
-```cairo
-// [!include ~/listings/getting-started/events/src/counter.cairo:emit]
-```
-
-
-
-
-#### library_call
-
-```cairo
-fn library_call_syscall(
- class_hash: ClassHash, function_selector: felt252, calldata: Span
-) -> SyscallResult>
-```
-
-Call the function `function_selector` of the class `class_hash` with `calldata`.
-This is analogous to a delegate call in Ethereum, but only a single class is called.
-
-
-
-#### send_message_to_L1
-
-```cairo
-fn send_message_to_l1_syscall(
- to_address: felt252, payload: Span
-) -> SyscallResult<()>
-```
-
-Send a message to the L1 contract at `to_address` with the given `payload`.
-
-
-
-#### replace_class
-
-```cairo
-fn replace_class_syscall(
- class_hash: ClassHash
-) -> SyscallResult<()>
-```
-
-Replace the class of the calling contract with the class `class_hash`.
-
-This is used for contract upgrades. Here's an example from the [Upgradeable Contract](/applications/upgradeable_contract):
-
-```cairo
-// [!include ~/listings/applications/upgradeable_contract/src/upgradeable_contract_v0.cairo:upgrade]
-```
-
-The new class code will only be used for future calls to the contract.
-The current transaction containing the `replace_class` syscall will continue to use the old class code. Note that you can explicitly use the new class code in the same transaction by calling `call_contract` after the `replace_class` syscall.
-
-#### storage_read
-
-```cairo
-fn storage_read_syscall(
- address_domain: u32, address: StorageAddress,
-) -> SyscallResult
-```
-
-This low-level syscall is used to get the value in the storage of a specific key at `address` in the `address_domain`.
-
-`address_domain` is used to distinguish between data availability modes.
-Currently, only mode `ONCHAIN` (`0`) is supported.
-
-#### storage_write
-
-```cairo
-fn storage_write_syscall(
- address_domain: u32, address: StorageAddress, value: felt252
-) -> SyscallResult<()>
-```
-
-Similar to `storage_read`, this low-level syscall is used to write the value `value` in the storage of a specific key at `address` in the `address_domain`.
-
-## Documentation
-
-Syscalls are defined in [`starknet::syscall`](https://github.com/starkware-libs/cairo/blob/ec14a5e2c484190ff40811c973a72a53739cedb7/corelib/src/starknet/syscalls.cairo).
-
-You can also read the [official documentation page](https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/system-calls-cairo1/) for more details.
-
-
diff --git a/pages/getting-started/syscalls.md b/pages/getting-started/syscalls.md
new file mode 100644
index 00000000..39f6793f
--- /dev/null
+++ b/pages/getting-started/syscalls.md
@@ -0,0 +1,250 @@
+# System Calls (Syscalls)
+
+System calls (syscalls) are the interface between Starknet smart contracts and the Starknet Operating System (OS). They provide essential functionalities for:
+
+- Reading blockchain state and context
+- Contract interactions (deployment, calls)
+- Event emission
+- Cross-layer communication
+- Storage operations
+
+All syscalls return a `SyscallResult` type, which can be either `Success` or `Failure`, enabling proper error handling in your contracts.
+
+## Available Syscalls
+
+### Blockchain State
+
+- [get_execution_info](#get_execution_info) - Get current execution context
+- [get_block_hash](#get_block_hash) - Retrieve a block's hash
+- [get_class_hash_at](#get_class_hash_at) - Get the class hash of a contract at a specific address
+
+### Contract Operations
+
+- [call_contract](#call_contract) - Call another contract
+- [deploy](#deploy) - Deploy a new contract
+- [library_call](#library_call) - Make a delegate call
+- [replace_class](#replace_class) - Upgrade contract code
+
+### Events and Messaging
+
+- [emit_event](#emit_event) - Emit contract events
+- [send_message_to_l1](#send_message_to_l1) - Send messages to Ethereum (L1)
+
+### Storage Operations
+
+- [storage_read](#storage_read) - Read from contract storage
+- [storage_write](#storage_write) - Write to contract storage
+
+### Cryptographic Operations
+
+- [keccak](#keccak) - Compute Keccak hash
+- [sha256_process_block](#sha256_process_block) - Compute SHA256 hash
+
+## Detailed Reference
+
+### get_block_hash
+
+```cairo
+fn get_block_hash_syscall(block_number: u64) -> SyscallResult
+```
+
+Retrieves the hash of a specific block by its number. Only works for blocks within the range `[first_v0_12_0_block, current_block - 10]`.
+
+### get_execution_info
+
+:::info
+
+Instead of using this syscall directly, the `starknet::info` module provides convenient helper functions:
+
+- `get_execution_info()` - Full execution context
+- `get_caller_address()` - Current caller
+- `get_contract_address()` - Current contract
+- `get_block_info()` - Block information
+- `get_tx_info()` - Transaction details
+- `get_block_timestamp()` - Current block time
+- `get_block_number()` - Current block number
+
+:::
+
+```cairo
+fn get_execution_info_v2_syscall() -> SyscallResult>
+fn get_execution_info_syscall() -> SyscallResult>
+```
+
+Returns information about the current execution context.
+
+### get_class_hash_at
+
+:::warning
+This syscall will only be supported from Starknet v0.13.4 onwards.
+:::
+
+```cairo
+fn get_class_hash_at_syscall(contract_address: ContractAddress) -> SyscallResult
+```
+
+Returns the class hash of a contract at a specific address.
+
+### call_contract
+
+:::info
+For safer contract calls, use the dispatcher pattern shown in [Calling other contracts](/getting-started/interacting/calling_other_contracts)
+:::
+
+```cairo
+fn call_contract_syscall(
+ address: ContractAddress,
+ entry_point_selector: felt252,
+ calldata: Span
+) -> SyscallResult>
+```
+
+Calls a contract at the specified address. Failures cannot be caught and will revert the entire transaction.
+
+### deploy
+
+```cairo
+fn deploy_syscall(
+ class_hash: ClassHash,
+ contract_address_salt: felt252,
+ calldata: Span,
+ deploy_from_zero: bool,
+) -> SyscallResult<(ContractAddress, Span::)>
+```
+
+Deploys a new contract instance. Returns the deployed address and constructor result.
+
+The [Simple Factory](/getting-started/interacting/factory) uses the `deploy` syscall under the hood:
+
+```cairo
+// [!include ~/listings/getting-started/factory/src/simple_factory.cairo:deploy]
+```
+
+### emit_event
+
+```cairo
+fn emit_event_syscall(
+ keys: Span,
+ data: Span
+) -> SyscallResult<()>
+```
+
+Emits an event with indexed keys and data values.
+
+See the [Events](/getting-started/basics/events) section for more information:
+
+```cairo
+// [!include ~/listings/getting-started/events/src/counter.cairo:emit]
+```
+
+### library_call
+
+```cairo
+fn library_call_syscall(
+ class_hash: ClassHash,
+ function_selector: felt252,
+ calldata: Span
+) -> SyscallResult>
+```
+
+Makes a delegate call to execute code from another contract class within the current contract's context. Similar to Ethereum's delegatecall but limited to a single class.
+
+### send_message_to_l1
+
+```cairo
+fn send_message_to_l1_syscall(
+ to_address: felt252,
+ payload: Span
+) -> SyscallResult<()>
+```
+
+Sends a message to an Ethereum (L1) contract.
+
+### replace_class
+
+```cairo
+fn replace_class_syscall(
+ class_hash: ClassHash
+) -> SyscallResult<()>
+```
+
+Upgrades the contract's code by replacing its class hash.
+
+This syscall is used in [Upgradeable Contract](/applications/upgradeable_contract):
+
+```cairo
+// [!include ~/listings/applications/upgradeable_contract/src/upgradeable_contract_v0.cairo:upgrade]
+```
+
+:::note
+The new code only affects future calls. The current transaction continues with the old code unless explicitly called through `call_contract`.
+:::
+
+### storage_read
+
+:::warning
+Using this syscall directly is not recommended. Follow the [Storage](/getting-started/basics/storage) section for more information.
+:::
+
+```cairo
+fn storage_read_syscall(
+ address_domain: u32,
+ address: StorageAddress,
+) -> SyscallResult
+```
+
+Low-level storage read operation.
+
+### storage_write
+
+:::warning
+Using this syscall directly is not recommended. Follow the [Storage](/getting-started/basics/storage) section for more information.
+:::
+
+```cairo
+fn storage_write_syscall(
+ address_domain: u32,
+ address: StorageAddress,
+ value: felt252
+) -> SyscallResult<()>
+```
+
+Low-level storage write operation.
+
+### keccak
+
+```cairo
+fn keccak_syscall(input: Span) -> SyscallResult
+```
+
+Computes the Keccak hash of the input data as a little-endian 256-bit number, where `input` is a Span of 64-bits little-endian words.
+
+:::note
+**Padding**
+
+- The input must be a multiple of 1088 bits (== 17 u64 words)
+- The input must be pre-padded following the Keccak padding rule (pad10\*1):
+ 1. Add a '1' bit
+ 2. Add zero or more '0' bits
+ 3. Add a final '1' bit
+ The total length after padding must be a multiple of 1088 bits
+ :::
+
+### sha256_process_block
+
+```cairo
+fn sha256_process_block_syscall(
+ state: core::sha256::Sha256StateHandle, input: Box<[u32; 16]>,
+) -> SyscallResult {}
+```
+
+Computes the next SHA-256 state of the input block with the given state.
+
+:::note
+The system call does not add any padding and the input needs to be a multiple of 512 bits (== 16 u32 words).
+:::
+
+## Additional Resources
+
+- [Official Syscalls Documentation](https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/system-calls-cairo1/)
+- [Source Code](https://github.com/starkware-libs/cairo/blob/v2.9.2/corelib/src/starknet/syscalls.cairo)
diff --git a/routes.ts b/routes.ts
index 99e74560..11f33295 100644
--- a/routes.ts
+++ b/routes.ts
@@ -54,10 +54,6 @@ const config: Sidebar = [
text: "Mappings",
link: "/getting-started/basics/mappings",
},
- {
- text: "Syscalls",
- link: "/getting-started/basics/syscalls",
- },
{
text: "Documentation",
link: "/getting-started/basics/documentation",
@@ -89,6 +85,10 @@ const config: Sidebar = [
text: "Testing contracts",
link: "/getting-started/testing/contract-testing",
},
+ {
+ text: "Syscalls Reference",
+ link: "/getting-started/syscalls",
+ },
],
},
{
From 4cbc935e9c323eab6472126dc9b4fead75b2f6bd Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Sun, 26 Jan 2025 16:33:03 +0100
Subject: [PATCH 39/44] feat: remove interacting/interface chapter
---
.../interfaces_traits/.gitignore | 1 -
.../interfaces_traits/Scarb.lock | 6 --
.../interfaces_traits/Scarb.toml | 15 ----
.../interfaces_traits/src/explicit.cairo | 56 -------------
.../interfaces_traits/src/implicit.cairo | 50 ------------
.../src/implicit_internal.cairo | 79 -------------------
.../interfaces_traits/src/lib.cairo | 3 -
.../interacting/interfaces-traits.md | 39 ---------
routes.ts | 4 -
9 files changed, 253 deletions(-)
delete mode 100644 listings/getting-started/interfaces_traits/.gitignore
delete mode 100644 listings/getting-started/interfaces_traits/Scarb.lock
delete mode 100644 listings/getting-started/interfaces_traits/Scarb.toml
delete mode 100644 listings/getting-started/interfaces_traits/src/explicit.cairo
delete mode 100644 listings/getting-started/interfaces_traits/src/implicit.cairo
delete mode 100644 listings/getting-started/interfaces_traits/src/implicit_internal.cairo
delete mode 100644 listings/getting-started/interfaces_traits/src/lib.cairo
delete mode 100644 pages/getting-started/interacting/interfaces-traits.md
diff --git a/listings/getting-started/interfaces_traits/.gitignore b/listings/getting-started/interfaces_traits/.gitignore
deleted file mode 100644
index eb5a316c..00000000
--- a/listings/getting-started/interfaces_traits/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-target
diff --git a/listings/getting-started/interfaces_traits/Scarb.lock b/listings/getting-started/interfaces_traits/Scarb.lock
deleted file mode 100644
index c5dde57a..00000000
--- a/listings/getting-started/interfaces_traits/Scarb.lock
+++ /dev/null
@@ -1,6 +0,0 @@
-# Code generated by scarb DO NOT EDIT.
-version = 1
-
-[[package]]
-name = "interfaces_traits"
-version.workspace = true
diff --git a/listings/getting-started/interfaces_traits/Scarb.toml b/listings/getting-started/interfaces_traits/Scarb.toml
deleted file mode 100644
index 0cc1a25d..00000000
--- a/listings/getting-started/interfaces_traits/Scarb.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "interfaces_traits"
-version.workspace = true
-edition.workspace = true
-
-[dependencies]
-starknet.workspace = true
-
-[dev-dependencies]
-cairo_test.workspace = true
-
-[scripts]
-test.workspace = true
-
-[[target.starknet-contract]]
diff --git a/listings/getting-started/interfaces_traits/src/explicit.cairo b/listings/getting-started/interfaces_traits/src/explicit.cairo
deleted file mode 100644
index 4454cc23..00000000
--- a/listings/getting-started/interfaces_traits/src/explicit.cairo
+++ /dev/null
@@ -1,56 +0,0 @@
-// [!region contract]
-#[starknet::interface]
-pub trait IExplicitInterfaceContract {
- fn get_value(self: @TContractState) -> u32;
- fn set_value(ref self: TContractState, value: u32);
-}
-
-#[starknet::contract]
-pub mod ExplicitInterfaceContract {
- use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
-
- #[storage]
- struct Storage {
- value: u32,
- }
-
- #[abi(embed_v0)]
- impl ExplicitInterfaceContract of super::IExplicitInterfaceContract {
- fn get_value(self: @ContractState) -> u32 {
- self.value.read()
- }
-
- fn set_value(ref self: ContractState, value: u32) {
- self.value.write(value);
- }
- }
-}
-// [!endregion contract]
-
-#[cfg(test)]
-mod tests {
- use super::{
- ExplicitInterfaceContract, IExplicitInterfaceContractDispatcher,
- IExplicitInterfaceContractDispatcherTrait,
- };
- use starknet::syscalls::deploy_syscall;
-
- #[test]
- fn test_interface() {
- let (contract_address, _) = deploy_syscall(
- ExplicitInterfaceContract::TEST_CLASS_HASH.try_into().unwrap(),
- 0,
- array![].span(),
- false,
- )
- .unwrap();
- let mut contract = IExplicitInterfaceContractDispatcher { contract_address };
-
- let value: u32 = 20;
- contract.set_value(value);
-
- let read_value = contract.get_value();
-
- assert_eq!(read_value, value);
- }
-}
diff --git a/listings/getting-started/interfaces_traits/src/implicit.cairo b/listings/getting-started/interfaces_traits/src/implicit.cairo
deleted file mode 100644
index 0ab2d6d2..00000000
--- a/listings/getting-started/interfaces_traits/src/implicit.cairo
+++ /dev/null
@@ -1,50 +0,0 @@
-// [!region contract]
-#[starknet::contract]
-pub mod ImplicitInterfaceContract {
- use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
-
- #[storage]
- struct Storage {
- value: u32,
- }
-
- #[abi(per_item)]
- #[generate_trait]
- pub impl ImplicitInterfaceContract of IImplicitInterfaceContract {
- #[external(v0)]
- fn get_value(self: @ContractState) -> u32 {
- self.value.read()
- }
-
- #[external(v0)]
- fn set_value(ref self: ContractState, value: u32) {
- self.value.write(value);
- }
- }
-}
-// [!endregion contract]
-
-#[cfg(test)]
-mod tests {
- use super::{ImplicitInterfaceContract, ImplicitInterfaceContract::IImplicitInterfaceContract};
- use starknet::{syscalls::deploy_syscall, testing::set_contract_address};
-
- #[test]
- fn test_interface() {
- let (contract_address, _) = deploy_syscall(
- ImplicitInterfaceContract::TEST_CLASS_HASH.try_into().unwrap(),
- 0,
- array![].span(),
- false,
- )
- .unwrap();
- set_contract_address(contract_address);
- let mut state = ImplicitInterfaceContract::contract_state_for_testing();
-
- let value = 42;
- state.set_value(value);
- let read_value = state.get_value();
-
- assert_eq!(read_value, value);
- }
-}
diff --git a/listings/getting-started/interfaces_traits/src/implicit_internal.cairo b/listings/getting-started/interfaces_traits/src/implicit_internal.cairo
deleted file mode 100644
index 74bdca0f..00000000
--- a/listings/getting-started/interfaces_traits/src/implicit_internal.cairo
+++ /dev/null
@@ -1,79 +0,0 @@
-// [!region contract]
-#[starknet::interface]
-pub trait IImplicitInternalContract {
- fn add(ref self: TContractState, nb: u32);
- fn get_value(self: @TContractState) -> u32;
- fn get_const(self: @TContractState) -> u32;
-}
-
-#[starknet::contract]
-pub mod ImplicitInternalContract {
- use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
-
- #[storage]
- struct Storage {
- value: u32,
- }
-
- #[generate_trait]
- impl InternalFunctions of InternalFunctionsTrait {
- fn set_value(ref self: ContractState, value: u32) {
- self.value.write(value);
- }
-
- fn get_const() -> u32 {
- 42
- }
- }
-
- #[constructor]
- fn constructor(ref self: ContractState) {
- self.set_value(0);
- }
-
- #[abi(embed_v0)]
- impl ImplicitInternalContract of super::IImplicitInternalContract {
- fn add(ref self: ContractState, nb: u32) {
- self.set_value(self.value.read() + nb);
- }
-
- fn get_value(self: @ContractState) -> u32 {
- self.value.read()
- }
-
- fn get_const(self: @ContractState) -> u32 {
- self.get_const()
- }
- }
-}
-// [!endregion contract]
-
-#[cfg(test)]
-mod tests {
- use super::{
- ImplicitInternalContract, IImplicitInternalContractDispatcher,
- IImplicitInternalContractDispatcherTrait,
- };
- use starknet::syscalls::deploy_syscall;
-
- #[test]
- fn test_interface() {
- // Set up.
- let (contract_address, _) = deploy_syscall(
- ImplicitInternalContract::TEST_CLASS_HASH.try_into().unwrap(),
- 0,
- array![].span(),
- false,
- )
- .unwrap();
- let mut contract = IImplicitInternalContractDispatcher { contract_address };
-
- let initial_value: u32 = 0;
- assert_eq!(contract.get_value(), initial_value);
-
- let add_value: u32 = 10;
- contract.add(add_value);
-
- assert_eq!(contract.get_value(), initial_value + add_value);
- }
-}
diff --git a/listings/getting-started/interfaces_traits/src/lib.cairo b/listings/getting-started/interfaces_traits/src/lib.cairo
deleted file mode 100644
index 500b0482..00000000
--- a/listings/getting-started/interfaces_traits/src/lib.cairo
+++ /dev/null
@@ -1,3 +0,0 @@
-mod explicit;
-mod implicit_internal;
-mod implicit;
diff --git a/pages/getting-started/interacting/interfaces-traits.md b/pages/getting-started/interacting/interfaces-traits.md
deleted file mode 100644
index 751c931a..00000000
--- a/pages/getting-started/interacting/interfaces-traits.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Contract interfaces and Traits generation
-
-Contract interfaces define the structure and behavior of a contract, serving as the contract's public ABI. They list all the function signatures that a contract exposes. For a detailed explanation of interfaces, you can refer to the [Cairo Book](https://book.cairo-lang.org/ch13-02-anatomy-of-a-simple-contract.html#the-interface-the-contracts-blueprint).
-
-In Cairo, to specify the interface you need to define a trait annotated with `#[starknet::interface]` and then implement that trait in the contract.
-
-When a function needs to access the contract state, it must have a `self` parameter of type `ContractState`. This implies that the corresponding function signature in the interface trait must also take a `TContractState` type as a parameter. It's important to note that every function in the contract interface must have this `self` parameter of type `TContractState`.
-
-You can use the `#[generate_trait]` attribute to implicitly generate the trait for a specific implementation block. This attribute automatically generates a trait with the same functions as the ones in the implemented block, replacing the `self` parameter with a generic `TContractState` parameter. However, you will need to annotate the block with the `#[abi(per_item)]` attribute, and each function with the appropriate attribute depending on whether it's an external function, a constructor or an L1 handler.
-
-In summary, there's two ways to handle interfaces:
-
-- Explicitly, by defining a trait annotated with `#[starknet::interface]`
-- Implicitly, by using `#[generate_trait]` combined with the `#[abi(per_item)]` attributes, and annotating each function inside the implementation block with the appropriate attribute.
-
-## Explicit interface
-
-```cairo
-// [!include ~/listings/getting-started/interfaces_traits/src/explicit.cairo:contract]
-```
-
-## Implicit interface
-
-```cairo
-// [!include ~/listings/getting-started/interfaces_traits/src/implicit.cairo:contract]
-```
-
-:::note
-You can import an implicitly generated contract interface with `use contract::{GeneratedContractInterface}`. However, the `Dispatcher` will not be generated automatically.
-:::
-
-## Internal functions
-
-You can also use `#[generate_trait]` for your internal functions.
-Since this trait is generated in the context of the contract, you can define pure functions as well (functions without the `self` parameter).
-
-```cairo
-// [!include ~/listings/getting-started/interfaces_traits/src/implicit_internal.cairo:contract]
-```
diff --git a/routes.ts b/routes.ts
index 11f33295..04eff920 100644
--- a/routes.ts
+++ b/routes.ts
@@ -67,10 +67,6 @@ const config: Sidebar = [
text: "How to deploy",
link: "/getting-started/interacting/how_to_deploy",
},
- {
- text: "Contract interfaces and Traits generation",
- link: "/getting-started/interacting/interfaces-traits",
- },
{
text: "Calling other contracts",
link: "/getting-started/interacting/calling_other_contracts",
From dd00697850974d7db2170d26f5ee82558575ba06 Mon Sep 17 00:00:00 2001
From: CountryCousin
Date: Sun, 26 Jan 2025 14:02:15 +0100
Subject: [PATCH 40/44] Update simple_vault.cairo
update of functions using ref and snapshots
---
listings/applications/simple_vault/src/simple_vault.cairo | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/listings/applications/simple_vault/src/simple_vault.cairo b/listings/applications/simple_vault/src/simple_vault.cairo
index 8ae7d074..fc708ec8 100644
--- a/listings/applications/simple_vault/src/simple_vault.cairo
+++ b/listings/applications/simple_vault/src/simple_vault.cairo
@@ -31,8 +31,8 @@ pub trait IERC20 {
pub trait ISimpleVault {
fn deposit(ref self: TContractState, amount: u256);
fn withdraw(ref self: TContractState, shares: u256);
- fn user_balance_of(ref self: TContractState, account: ContractAddress) -> u256;
- fn contract_total_supply(ref self: TContractState) -> u256;
+ fn user_balance_of(self: @TContractState, account: ContractAddress) -> u256;
+ fn contract_total_supply(self: @TContractState) -> u256;
}
#[starknet::contract]
@@ -71,11 +71,11 @@ pub mod SimpleVault {
#[abi(embed_v0)]
impl SimpleVault of super::ISimpleVault {
- fn user_balance_of(ref self: ContractState, account: ContractAddress) -> u256 {
+ fn user_balance_of(self: @ContractState, account: ContractAddress) -> u256 {
self.balance_of.read(account)
}
- fn contract_total_supply(ref self: ContractState) -> u256 {
+ fn contract_total_supply(self: @ContractState) -> u256 {
self.total_supply.read()
}
From 48a3667312f8ff5105ee0dc4d4d02e98e93b8951 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Mon, 27 Jan 2025 17:12:03 +0100
Subject: [PATCH 41/44] feat: migrate calling_other_contract to snforge
---
Scarb.lock | 7 +-
.../calling_other_contracts/Scarb.toml | 3 +-
.../calling_other_contracts/src/caller.cairo | 68 +++++++++----------
3 files changed, 39 insertions(+), 39 deletions(-)
diff --git a/Scarb.lock b/Scarb.lock
index 34dddc6e..1f544d5c 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -25,6 +25,9 @@ version = "0.1.0"
[[package]]
name = "calling_other_contracts"
version = "0.1.0"
+dependencies = [
+ "snforge_std",
+]
[[package]]
name = "coin_flip"
@@ -121,10 +124,6 @@ version = "0.1.0"
name = "hash_trait"
version = "0.1.0"
-[[package]]
-name = "interfaces_traits"
-version = "0.1.0"
-
[[package]]
name = "library_calls"
version = "0.1.0"
diff --git a/listings/getting-started/calling_other_contracts/Scarb.toml b/listings/getting-started/calling_other_contracts/Scarb.toml
index 2595a9bd..717546c9 100644
--- a/listings/getting-started/calling_other_contracts/Scarb.toml
+++ b/listings/getting-started/calling_other_contracts/Scarb.toml
@@ -7,7 +7,8 @@ edition.workspace = true
starknet.workspace = true
[dev-dependencies]
-cairo_test.workspace = true
+assert_macros.workspace = true
+snforge_std.workspace = true
[scripts]
test.workspace = true
diff --git a/listings/getting-started/calling_other_contracts/src/caller.cairo b/listings/getting-started/calling_other_contracts/src/caller.cairo
index 7a088711..133c6340 100644
--- a/listings/getting-started/calling_other_contracts/src/caller.cairo
+++ b/listings/getting-started/calling_other_contracts/src/caller.cairo
@@ -1,31 +1,37 @@
// [!region callee_contract]
// This will automatically generate ICalleeDispatcher and ICalleeDispatcherTrait
#[starknet::interface]
-pub trait ICallee {
- fn set_value(ref self: TContractState, value: u128) -> u128;
+trait ICallee {
+ fn set_value(ref self: TContractState, value: u128);
+ fn get_value(self: @TContractState) -> u128;
}
#[starknet::contract]
-pub mod Callee {
- use starknet::storage::StoragePointerWriteAccess;
+mod Callee {
+ use starknet::storage::{StoragePointerWriteAccess, StoragePointerReadAccess};
+ use super::ICallee;
#[storage]
struct Storage {
- pub value: u128,
+ value: u128,
}
#[abi(embed_v0)]
- impl ICalleeImpl of super::ICallee {
- fn set_value(ref self: ContractState, value: u128) -> u128 {
+ impl ICalleeImpl of ICallee {
+ fn set_value(ref self: ContractState, value: u128) {
self.value.write(value);
- value
+ }
+ fn get_value(self: @ContractState) -> u128 {
+ self.value.read()
}
}
}
// [!endregion callee_contract]
+// Interface for the contract that will make the calls
#[starknet::interface]
-pub trait ICaller {
+trait ICaller {
+ // Call another contract to set its value
fn set_value_from_address(
ref self: TContractState, addr: starknet::ContractAddress, value: u128,
);
@@ -33,18 +39,19 @@ pub trait ICaller {
// [!region caller_contract]
#[starknet::contract]
-pub mod Caller {
- // We need to import the dispatcher of the callee contract
- // If you don't have a proper import, you can redefine the interface by yourself
+mod Caller {
+ // Import the generated dispatcher types for the Callee contract
use super::{ICalleeDispatcher, ICalleeDispatcherTrait};
+ use super::ICaller;
use starknet::ContractAddress;
#[storage]
struct Storage {}
#[abi(embed_v0)]
- impl ICallerImpl of super::ICaller {
+ impl ICallerImpl of ICaller {
fn set_value_from_address(ref self: ContractState, addr: ContractAddress, value: u128) {
+ // Create a dispatcher instance and call the target contract
ICalleeDispatcher { contract_address: addr }.set_value(value);
}
}
@@ -53,37 +60,30 @@ pub mod Caller {
#[cfg(test)]
mod tests {
- use super::{Callee, ICalleeDispatcher, Caller, ICallerDispatcher, ICallerDispatcherTrait};
- use starknet::{testing::set_contract_address, syscalls::deploy_syscall};
- use starknet::storage::StoragePointerReadAccess;
+ use super::{
+ ICalleeDispatcher, ICalleeDispatcherTrait, ICallerDispatcher, ICallerDispatcherTrait,
+ };
+ use snforge_std::{ContractClassTrait, DeclareResultTrait, declare};
fn deploy() -> (ICalleeDispatcher, ICallerDispatcher) {
- let (address_callee, _) = deploy_syscall(
- Callee::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
- let (address_caller, _) = deploy_syscall(
- Caller::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false,
- )
- .unwrap();
+ let contract = declare("Callee").unwrap().contract_class();
+ let (callee_contract_address, _) = contract.deploy(@array![]).unwrap();
+
+ let contract = declare("Caller").unwrap().contract_class();
+ let (caller_contract_address, _) = contract.deploy(@array![]).unwrap();
(
- ICalleeDispatcher { contract_address: address_callee },
- ICallerDispatcher { contract_address: address_caller },
+ ICalleeDispatcher { contract_address: callee_contract_address },
+ ICallerDispatcher { contract_address: caller_contract_address },
)
}
#[test]
fn test_caller() {
- let init_value: u128 = 42;
-
let (callee, caller) = deploy();
- caller.set_value_from_address(callee.contract_address, init_value);
-
- let state = @Callee::contract_state_for_testing();
- set_contract_address(callee.contract_address);
+ let value: u128 = 42;
- let value_read: u128 = state.value.read();
+ caller.set_value_from_address(callee.contract_address, value);
- assert_eq!(value_read, init_value);
+ assert_eq!(callee.get_value(), value);
}
}
From ac41ee50f40c682b4c59a08dce9482db77121326 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Mon, 27 Jan 2025 17:12:22 +0100
Subject: [PATCH 42/44] feat: rework calling_other_contract
---
.../interacting/calling_other_contracts.md | 35 ++++++++++++++-----
1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/pages/getting-started/interacting/calling_other_contracts.md b/pages/getting-started/interacting/calling_other_contracts.md
index 55288e6b..7be9dc90 100644
--- a/pages/getting-started/interacting/calling_other_contracts.md
+++ b/pages/getting-started/interacting/calling_other_contracts.md
@@ -1,22 +1,41 @@
-# Calling other contracts
+# Calling Other Contracts
-There are two different ways to call other contracts in Cairo.
+In Starknet, contracts can interact with each other through contract calls. The recommended way to make these calls is using the dispatcher pattern, which provides type safety and better error handling.
-The easiest way to call other contracts is by using the dispatcher of the contract you want to call.
-You can read more about Dispatchers in the [Cairo Book](https://book.cairo-lang.org/ch15-02-interacting-with-another-contract.html#calling-contracts-using-the-contract-dispatcher).
+## Understanding Dispatchers
-The other way is to use the `starknet::call_contract_syscall` syscall yourself. However, this method is not recommended and will not be covered in this chapter.
+A dispatcher is an automatically generated struct that handles the serialization and deserialization of contract calls. To use dispatchers:
-In order to call other contracts using dispatchers, you will need to define the called contract's interface as a trait annotated with the `#[starknet::interface]` attribute, and then import the `IContractDispatcher` and `IContractDispatcherTrait` items in your contract.
+1. Define the target contract's interface as a trait with `#[starknet::interface]` (`IContract`)
+2. Import the generated dispatcher types (`IContractDispatcher` and `IContractDispatcherTrait`)
+3. Create a dispatcher instance with the target contract's address
-Here's the `Callee` contract interface and implementation:
+Let's look at a practical example where one contract (`Caller`) interacts with another (`Callee`). The `Callee` contract stores a value that can be set and retrieved:
```cairo
// [!include ~/listings/getting-started/calling_other_contracts/src/caller.cairo:callee_contract]
```
-The following `Caller` contract uses the `Callee` dispatcher to call the `Callee` contract:
+The `Caller` contract demonstrates how to use the dispatcher to interact with `Callee`:
```cairo
// [!include ~/listings/getting-started/calling_other_contracts/src/caller.cairo:caller_contract]
```
+
+### Key Points:
+
+- The `#[starknet::interface]` attribute automatically generates the dispatcher types
+- Dispatchers handle all the low-level details of contract interaction
+- Contract calls are type-safe and checked at compile time
+- Each contract maintains its own storage and state
+
+For more details about dispatchers, check out the [Cairo Book](https://book.cairo-lang.org/ch102-02-interacting-with-another-contract.html).
+
+:::note
+While you can use the low-level `call_contract_syscall` directly, it's not recommended as it:
+
+- Requires manual serialization/deserialization
+- Lacks compile-time type checking
+- Is more easy to make mistakes
+
+:::
From b78c30cc1f7db35f39598274cc998b093a5063c5 Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 28 Jan 2025 23:07:35 +0100
Subject: [PATCH 43/44] fix: cairo cheatsheet in sub listing
---
Scarb.lock | 4 ----
listings/cairo_cheatsheet/{ => listing}/.gitignore | 0
listings/cairo_cheatsheet/{ => listing}/Scarb.lock | 0
listings/cairo_cheatsheet/{ => listing}/Scarb.toml | 0
.../cairo_cheatsheet/{ => listing}/src/array_example.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/dict_example.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/enum_example.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/felt_example.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/if_let_example.cairo | 0
listings/cairo_cheatsheet/{ => listing}/src/lib.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/loop_example.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/mapping_example.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/match_example.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/struct_example.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/tuple_example.cairo | 0
.../{ => listing}/src/type_casting_example.cairo | 0
.../cairo_cheatsheet/{ => listing}/src/while_example.cairo | 0
.../{ => listing}/src/while_let_example.cairo | 0
pages/cairo_cheatsheet/arrays.md | 2 +-
pages/cairo_cheatsheet/dict.md | 2 +-
pages/cairo_cheatsheet/enums.md | 4 ++--
pages/cairo_cheatsheet/felt.md | 4 ++--
pages/cairo_cheatsheet/if_let.md | 2 +-
pages/cairo_cheatsheet/loop.md | 2 +-
pages/cairo_cheatsheet/mapping.md | 2 +-
pages/cairo_cheatsheet/match.md | 2 +-
pages/cairo_cheatsheet/struct.md | 2 +-
pages/cairo_cheatsheet/tuples.md | 2 +-
pages/cairo_cheatsheet/type_casting.md | 2 +-
pages/cairo_cheatsheet/while.md | 2 +-
pages/cairo_cheatsheet/while_let.md | 2 +-
pages/getting-started/testing/contract-testing.md | 2 +-
32 files changed, 16 insertions(+), 20 deletions(-)
rename listings/cairo_cheatsheet/{ => listing}/.gitignore (100%)
rename listings/cairo_cheatsheet/{ => listing}/Scarb.lock (100%)
rename listings/cairo_cheatsheet/{ => listing}/Scarb.toml (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/array_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/dict_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/enum_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/felt_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/if_let_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/lib.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/loop_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/mapping_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/match_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/struct_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/tuple_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/type_casting_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/while_example.cairo (100%)
rename listings/cairo_cheatsheet/{ => listing}/src/while_let_example.cairo (100%)
diff --git a/Scarb.lock b/Scarb.lock
index 1f544d5c..d6ab17ce 100644
--- a/Scarb.lock
+++ b/Scarb.lock
@@ -18,10 +18,6 @@ dependencies = [
"snforge_std",
]
-[[package]]
-name = "cairo_cheatsheet"
-version = "0.1.0"
-
[[package]]
name = "calling_other_contracts"
version = "0.1.0"
diff --git a/listings/cairo_cheatsheet/.gitignore b/listings/cairo_cheatsheet/listing/.gitignore
similarity index 100%
rename from listings/cairo_cheatsheet/.gitignore
rename to listings/cairo_cheatsheet/listing/.gitignore
diff --git a/listings/cairo_cheatsheet/Scarb.lock b/listings/cairo_cheatsheet/listing/Scarb.lock
similarity index 100%
rename from listings/cairo_cheatsheet/Scarb.lock
rename to listings/cairo_cheatsheet/listing/Scarb.lock
diff --git a/listings/cairo_cheatsheet/Scarb.toml b/listings/cairo_cheatsheet/listing/Scarb.toml
similarity index 100%
rename from listings/cairo_cheatsheet/Scarb.toml
rename to listings/cairo_cheatsheet/listing/Scarb.toml
diff --git a/listings/cairo_cheatsheet/src/array_example.cairo b/listings/cairo_cheatsheet/listing/src/array_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/array_example.cairo
rename to listings/cairo_cheatsheet/listing/src/array_example.cairo
diff --git a/listings/cairo_cheatsheet/src/dict_example.cairo b/listings/cairo_cheatsheet/listing/src/dict_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/dict_example.cairo
rename to listings/cairo_cheatsheet/listing/src/dict_example.cairo
diff --git a/listings/cairo_cheatsheet/src/enum_example.cairo b/listings/cairo_cheatsheet/listing/src/enum_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/enum_example.cairo
rename to listings/cairo_cheatsheet/listing/src/enum_example.cairo
diff --git a/listings/cairo_cheatsheet/src/felt_example.cairo b/listings/cairo_cheatsheet/listing/src/felt_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/felt_example.cairo
rename to listings/cairo_cheatsheet/listing/src/felt_example.cairo
diff --git a/listings/cairo_cheatsheet/src/if_let_example.cairo b/listings/cairo_cheatsheet/listing/src/if_let_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/if_let_example.cairo
rename to listings/cairo_cheatsheet/listing/src/if_let_example.cairo
diff --git a/listings/cairo_cheatsheet/src/lib.cairo b/listings/cairo_cheatsheet/listing/src/lib.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/lib.cairo
rename to listings/cairo_cheatsheet/listing/src/lib.cairo
diff --git a/listings/cairo_cheatsheet/src/loop_example.cairo b/listings/cairo_cheatsheet/listing/src/loop_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/loop_example.cairo
rename to listings/cairo_cheatsheet/listing/src/loop_example.cairo
diff --git a/listings/cairo_cheatsheet/src/mapping_example.cairo b/listings/cairo_cheatsheet/listing/src/mapping_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/mapping_example.cairo
rename to listings/cairo_cheatsheet/listing/src/mapping_example.cairo
diff --git a/listings/cairo_cheatsheet/src/match_example.cairo b/listings/cairo_cheatsheet/listing/src/match_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/match_example.cairo
rename to listings/cairo_cheatsheet/listing/src/match_example.cairo
diff --git a/listings/cairo_cheatsheet/src/struct_example.cairo b/listings/cairo_cheatsheet/listing/src/struct_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/struct_example.cairo
rename to listings/cairo_cheatsheet/listing/src/struct_example.cairo
diff --git a/listings/cairo_cheatsheet/src/tuple_example.cairo b/listings/cairo_cheatsheet/listing/src/tuple_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/tuple_example.cairo
rename to listings/cairo_cheatsheet/listing/src/tuple_example.cairo
diff --git a/listings/cairo_cheatsheet/src/type_casting_example.cairo b/listings/cairo_cheatsheet/listing/src/type_casting_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/type_casting_example.cairo
rename to listings/cairo_cheatsheet/listing/src/type_casting_example.cairo
diff --git a/listings/cairo_cheatsheet/src/while_example.cairo b/listings/cairo_cheatsheet/listing/src/while_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/while_example.cairo
rename to listings/cairo_cheatsheet/listing/src/while_example.cairo
diff --git a/listings/cairo_cheatsheet/src/while_let_example.cairo b/listings/cairo_cheatsheet/listing/src/while_let_example.cairo
similarity index 100%
rename from listings/cairo_cheatsheet/src/while_let_example.cairo
rename to listings/cairo_cheatsheet/listing/src/while_let_example.cairo
diff --git a/pages/cairo_cheatsheet/arrays.md b/pages/cairo_cheatsheet/arrays.md
index 23b3d794..4a54ab89 100644
--- a/pages/cairo_cheatsheet/arrays.md
+++ b/pages/cairo_cheatsheet/arrays.md
@@ -20,5 +20,5 @@ trait ArrayTrait {
For example:
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/array_example.cairo]
+// [!include ~/listings/cairo_cheatsheet/listing/src/array_example.cairo]
```
diff --git a/pages/cairo_cheatsheet/dict.md b/pages/cairo_cheatsheet/dict.md
index d7bd1248..fc5bd0b2 100644
--- a/pages/cairo_cheatsheet/dict.md
+++ b/pages/cairo_cheatsheet/dict.md
@@ -8,5 +8,5 @@ A dictionary is a data structure used to store key-value pairs, enabling efficie
For example:
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/dict_example.cairo:sheet]
+// [!include ~/listings/cairo_cheatsheet/listing/src/dict_example.cairo:sheet]
```
diff --git a/pages/cairo_cheatsheet/enums.md b/pages/cairo_cheatsheet/enums.md
index 23b85766..81d192dc 100644
--- a/pages/cairo_cheatsheet/enums.md
+++ b/pages/cairo_cheatsheet/enums.md
@@ -5,7 +5,7 @@ Just like other programming languages, enums (enumerations) are used in cairo to
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.
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/enum_example.cairo:enums]
+// [!include ~/listings/cairo_cheatsheet/listing/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.
@@ -24,5 +24,5 @@ Enums can be declared both inside and outside a contract. If declared outside, t
Here is an example of a contract illustrating the above statements :
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/enum_example.cairo:enum_contract]
+// [!include ~/listings/cairo_cheatsheet/listing/src/enum_example.cairo:enum_contract]
```
diff --git a/pages/cairo_cheatsheet/felt.md b/pages/cairo_cheatsheet/felt.md
index a47da1cc..e4ce25d1 100644
--- a/pages/cairo_cheatsheet/felt.md
+++ b/pages/cairo_cheatsheet/felt.md
@@ -1,10 +1,10 @@
# Felt
`felt252` is a fundamental data type in Cairo from which all other data types are derived.
-`felt252` can also be used to store [short string representations](/getting-started/basics/bytearrays-strings#short-strings) with a maximum length of 31 characters.
+`felt252` can also be used to store 'short string representations' with a maximum length of 31 characters.
For example:
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/felt_example.cairo:sheet]
+// [!include ~/listings/cairo_cheatsheet/listing/src/felt_example.cairo:sheet]
```
diff --git a/pages/cairo_cheatsheet/if_let.md b/pages/cairo_cheatsheet/if_let.md
index d4904664..7496a5a6 100644
--- a/pages/cairo_cheatsheet/if_let.md
+++ b/pages/cairo_cheatsheet/if_let.md
@@ -3,7 +3,7 @@
A `if let` statement is a combination of an `if` statement and a `let` statement. It allows you to execute the block only if the pattern matches. It's a cleaner way to handle a `match` statement with only one pattern that you want to handle.
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/if_let_example.cairo:sheet]
+// [!include ~/listings/cairo_cheatsheet/listing/src/if_let_example.cairo:sheet]
```
### See also
diff --git a/pages/cairo_cheatsheet/loop.md b/pages/cairo_cheatsheet/loop.md
index a15fe539..70fbd1a3 100644
--- a/pages/cairo_cheatsheet/loop.md
+++ b/pages/cairo_cheatsheet/loop.md
@@ -5,7 +5,7 @@ A `loop` specifies a block of code that will run repetitively until a halting co
For example:
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/loop_example.cairo:sheet]
+// [!include ~/listings/cairo_cheatsheet/listing/src/loop_example.cairo:sheet]
```
### See also
diff --git a/pages/cairo_cheatsheet/mapping.md b/pages/cairo_cheatsheet/mapping.md
index e32a4d05..9c61e48d 100644
--- a/pages/cairo_cheatsheet/mapping.md
+++ b/pages/cairo_cheatsheet/mapping.md
@@ -3,5 +3,5 @@
The `Map` type can be used to represent a collection of key-value.
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/mapping_example.cairo]
+// [!include ~/listings/cairo_cheatsheet/listing/src/mapping_example.cairo]
```
diff --git a/pages/cairo_cheatsheet/match.md b/pages/cairo_cheatsheet/match.md
index 1a5048cf..0f2b7d29 100644
--- a/pages/cairo_cheatsheet/match.md
+++ b/pages/cairo_cheatsheet/match.md
@@ -5,5 +5,5 @@ The `match` expression in Cairo allows us to control the flow of our code by com
For example:
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/match_example.cairo]
+// [!include ~/listings/cairo_cheatsheet/listing/src/match_example.cairo]
```
diff --git a/pages/cairo_cheatsheet/struct.md b/pages/cairo_cheatsheet/struct.md
index f4938402..16fd879b 100644
--- a/pages/cairo_cheatsheet/struct.md
+++ b/pages/cairo_cheatsheet/struct.md
@@ -5,5 +5,5 @@ A struct is a data type similar to a tuple. Like tuples, they can be used to hol
For example:
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/struct_example.cairo]
+// [!include ~/listings/cairo_cheatsheet/listing/src/struct_example.cairo]
```
diff --git a/pages/cairo_cheatsheet/tuples.md b/pages/cairo_cheatsheet/tuples.md
index d438f646..77251500 100644
--- a/pages/cairo_cheatsheet/tuples.md
+++ b/pages/cairo_cheatsheet/tuples.md
@@ -5,5 +5,5 @@ Tuples is a data type to group a fixed number of items of potentially different
For example:
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/tuple_example.cairo:sheet]
+// [!include ~/listings/cairo_cheatsheet/listing/src/tuple_example.cairo:sheet]
```
diff --git a/pages/cairo_cheatsheet/type_casting.md b/pages/cairo_cheatsheet/type_casting.md
index ad49878c..cd24d670 100644
--- a/pages/cairo_cheatsheet/type_casting.md
+++ b/pages/cairo_cheatsheet/type_casting.md
@@ -6,5 +6,5 @@ The `into` method is used for conversion from a smaller data type to a larger da
For example:
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/type_casting_example.cairo:sheet]
+// [!include ~/listings/cairo_cheatsheet/listing/src/type_casting_example.cairo:sheet]
```
diff --git a/pages/cairo_cheatsheet/while.md b/pages/cairo_cheatsheet/while.md
index 9c923fc0..8708df02 100644
--- a/pages/cairo_cheatsheet/while.md
+++ b/pages/cairo_cheatsheet/while.md
@@ -3,7 +3,7 @@
A `while` loop allows you to specify a condition that must be true for the loop to continue.
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/while_example.cairo:sheet]
+// [!include ~/listings/cairo_cheatsheet/listing/src/while_example.cairo:sheet]
```
### See also
diff --git a/pages/cairo_cheatsheet/while_let.md b/pages/cairo_cheatsheet/while_let.md
index f6013a94..8b77510a 100644
--- a/pages/cairo_cheatsheet/while_let.md
+++ b/pages/cairo_cheatsheet/while_let.md
@@ -3,7 +3,7 @@
A `while let` loop is a combination of a `while` loop and a `let` statement. It allows you to execute the loop body only if the pattern matches.
```cairo
-// [!include ~/listings/cairo_cheatsheet/src/while_let_example.cairo:sheet]
+// [!include ~/listings/cairo_cheatsheet/listing/src/while_let_example.cairo:sheet]
```
### See also
diff --git a/pages/getting-started/testing/contract-testing.md b/pages/getting-started/testing/contract-testing.md
index 2deff011..bb650962 100644
--- a/pages/getting-started/testing/contract-testing.md
+++ b/pages/getting-started/testing/contract-testing.md
@@ -72,7 +72,7 @@ To make testing more convenient, the `testing` module of the corelib provides so
- `pop_log>(address: ContractAddress) -> Option`
- `pop_l2_to_l1_message(address: ContractAddress) -> Option<(felt252, Span)>`
-You may also need the `info` module from the corelib, which allows you to access information about the current execution context (see [syscalls](/getting-started/basics/syscalls)):
+You may also need the `info` module from the corelib, which allows you to access information about the current execution context (see [syscalls](/getting-started/syscalls)):
- `get_caller_address() -> ContractAddress`
- `get_contract_address() -> ContractAddress`
From 43282229bcba56b62c4011ca97c5edb4473e1fcc Mon Sep 17 00:00:00 2001
From: julio4 <30329843+julio4@users.noreply.github.com>
Date: Tue, 28 Jan 2025 23:07:45 +0100
Subject: [PATCH 44/44] feat: update CI/CD
---
.github/workflows/verify_cairo_programs.yml | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/verify_cairo_programs.yml b/.github/workflows/verify_cairo_programs.yml
index 151ceee2..879699a3 100644
--- a/.github/workflows/verify_cairo_programs.yml
+++ b/.github/workflows/verify_cairo_programs.yml
@@ -4,9 +4,16 @@ on:
pull_request:
branches:
- dev
+ - main
workflow_dispatch:
jobs:
+ typos:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: crate-ci/typos@master
+
compile_and_verify:
runs-on: ubuntu-latest
@@ -19,7 +26,7 @@ jobs:
- name: Configure upstream repository
run: |
git remote add upstream https://github.com/NethermindEth/StarknetByExample
- git fetch upstream main
+ git fetch upstream
- name: Install scarb
uses: software-mansion/setup-scarb@v1
@@ -27,13 +34,7 @@ jobs:
- name: Install snforge
uses: foundry-rs/setup-snfoundry@v3
- - name: Run build script
+ - name: Verify changes
run: |
chmod +x scripts/cairo_programs_verifier.sh
./scripts/cairo_programs_verifier.sh
-
- typos:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: crate-ci/typos@master