diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 40f4b0ef..7a570db3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,6 +10,12 @@ You should never open a pull request to merge your changes directly into `main`.
The `dev` branch is deployed at
+The release branch is `main`. The development branch is `dev` and is considered stable (but not released yet).
+When you want to contribute, please create a new branch from `dev` and open a pull request to merge your changes back into `dev`.
+You should never open a pull request to merge your changes directly into `main`.
+
+The `dev` branch is deployed at https://starknet-by-example-dev.voyager.online/
+
Please note we have a code of conduct, please follow it in all your interactions with the project.
## Table of Contents
diff --git a/package.json b/package.json
index 1d9f92f5..93089196 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,12 @@
"vocs": "patches/vocs.patch"
},
"overrides": {
+ "vocs>shiki": "^1.23.0",
+ "vocs>@shikijs/core": "^1.23.0",
+ "vocs>@shikijs/types": "^1.23.0",
+ "vocs>@shikijs/rehype": "^1.23.0",
+ "vocs>@shikijs/twoslash": "^1.23.0",
+ "vocs>@shikijs/transformers": "^1.23.0"
"vocs>shiki": "^1.23.0 <1.24.0",
"vocs>@shikijs/core": "^1.23.0 <1.24.0",
"vocs>@shikijs/types": "^1.23.0 <1.24.0",
diff --git a/pages/advanced-concepts/optimisations/cairo-profiler.md b/pages/advanced-concepts/optimisations/cairo-profiler.md
new file mode 100644
index 00000000..64769556
--- /dev/null
+++ b/pages/advanced-concepts/optimisations/cairo-profiler.md
@@ -0,0 +1,282 @@
+# Cairo Profiler Technical Guide: Gas Optimization
+
+**Understanding Available Metrics**
+
+The profiler collects several key metrics per function call:
+
+```cairo
+
+measurements = {
+ "steps": n_steps,
+ "memory_holes": n_memory_holes,
+ "calls": 1, // count of function invocations
+ "l2_l1_message_sizes": message_sizes_sum,
+ // builtin counters (pedersen, range_check, etc.)
+}
+```
+
+## Running the Profiler
+
+```cairo
+cairo-profiler [OPTIONS]
+
+Options:
+ -o, --output-path Output path [default: profile.pb.gz]
+ --show-details Show contract addresses and selectors
+ --max-function-stack-trace-depth Maximum depth of function tree [default: 100]
+ --split-generics Split generic functions by type
+ --show-inlined-functions Show inlined function calls
+ --versioned-constants-path Custom resource costs file
+
+```
+
+### Example: Optimizing Storage Access
+
+### Unoptimized Code
+
+```cairo
+#[starknet::contract]
+mod UnoptimizedContract {
+ #[storage]
+ struct Storage {
+ balances: LegacyMap,
+ allowances: LegacyMap<(ContractAddress, ContractAddress), u256>,
+ }
+
+ #[external]
+ fn transfer_all_from_list(
+ ref self: ContractState,
+ from: ContractAddress,
+ to_list: Array
+ ) {
+ // Unoptimized: Multiple storage reads/writes
+ let len = to_list.len();
+ let mut i: usize = 0;
+ loop {
+ if i >= len {
+ break;
+ }
+ let recipient = *to_list.at(i);
+ let balance = self.balances.read(from);
+ self.balances.write(from, 0);
+ self.balances.write(recipient, balance);
+ i += 1;
+ };
+ }
+}
+```
+
+## Profiler Output Analysis
+
+```cairo
+Function Call Trace:
+├── transfer_all_from_list (100%)
+ ├── storage_read (45%)
+ │ └── steps: 2500
+ ├── storage_write (40%)
+ │ └── steps: 2200
+ └── array_operations (15%)
+ └── steps: 800
+
+```
+
+### Key Issues Identified:
+
+1. Repeated storage reads for the same value
+2. Multiple write operations that could be batched
+3. High step count from storage operations
+
+### Optimized Version
+
+```cairo
+
+#[starknet::contract]
+mod OptimizedContract {
+ #[storage]
+ struct Storage {
+ balances: LegacyMap,
+ allowances: LegacyMap<(ContractAddress, ContractAddress), u256>,
+ }
+
+ #[external]
+ fn transfer_all_from_list(
+ ref self: ContractState,
+ from: ContractAddress,
+ to_list: Array
+ ) {
+ // Read once
+ let total_balance = self.balances.read(from);
+ let len = to_list.len();
+
+ // Clear sender balance first
+ self.balances.write(from, 0);
+
+ // Calculate individual amount
+ let amount_per_recipient = total_balance / len.into();
+
+ // Batch write operations
+ let mut i: usize = 0;
+ loop {
+ if i >= len {
+ break;
+ }
+ let recipient = *to_list.at(i);
+ self.balances.write(recipient, amount_per_recipient);
+ i += 1;
+ };
+ }
+}
+```
+
+### Performance Comparison
+
+- Before:
+
+```cairo
+Total Steps: 5,500
+Storage Reads: 100 (for list size 100)
+Storage Writes: 200
+```
+
+- After
+
+```cairo
+Total Steps: 2,300 (58% reduction)
+Storage Reads: 1
+Storage Writes: 101
+```
+
+## Resources and Tools
+
+1. **pprof visualization**:
+
+```cairo
+go tool pprof -http=:8080 profile.pb.gz
+```
+
+2. **Flame graph generation**:
+
+```cairo
+cairo-profiler trace.bin --output-path flame.svg --format=flamegraph
+```
+
+## Advanced Profiling Features
+
+1. **Inlined Function Analysis**
+
+Enable with `--show-inlined-functions` to see detailed call stacks including inlined functions:
+
+```cairo
+cairo-profiler trace.bin --show-inlined-functions
+```
+
+2. **Generic Function Analysis**
+
+Use `--split-generics` to analyze generic functions by their concrete types:
+
+```cairo
+cairo-profiler trace.bin --split-generics
+```
+
+3. **Custom Resource Costs**
+
+Specify custom resource costs for different Starknet versions:
+
+```cairo
+cairo-profiler trace.bin --versioned-constants-path custom_costs.json
+```
+
+## Best Practices
+
+1. **Storage Pattern Optimization**
+
+- Cache storage reads in memory when accessed multiple times
+- Batch storage writes where possible
+- Use storage spans for array operations
+
+2. **Function Call Optimization**
+
+- Monitor inlined vs non-inlined function costs
+- Use `--show-inlined-functions` to identify expensive inlined operations
+- Consider function composition based on profiler output
+
+3. **Memory Management**
+
+- Track memory_holes metric to identify inefficient memory usage
+- Use appropriate data structures to minimize memory fragmentation
+- Consider span operations for array access
+
+4. **L1 Communication Optimization**
+
+- Monitor l2_l1_message_sizes for cross-layer communication
+- Batch L1 messages when possible
+- Compress data when sending to L1
+
+## Common Optimization Patterns
+
+1. **Storage Read Caching**
+
+```cairo
+// Before
+let value1 = storage.read(key);
+let value2 = storage.read(key);
+
+// After
+let cached = storage.read(key);
+let value1 = cached;
+let value2 = cached;
+```
+
+2. **Batch Operations**
+
+```cairo
+// Before
+for item in items {
+ storage.write(item.key, item.value);
+}
+
+// After
+let mut writes = Array::new();
+// ... collect writes ...
+storage.write_multiple(writes);
+```
+
+3. **Memory Efficiency**
+
+```cairo
+// Before
+let mut array = ArrayTrait::new();
+// ... multiple push operations ...
+
+// After
+let mut array = ArrayTrait::with_capacity(size);
+// ... reserve space first ...
+```
+
+## Profile-Guided Optimization Process
+
+1. **Baseline Profiling**
+
+```cairo
+cairo-profiler initial_trace.bin --output-path baseline.pb.gz
+```
+
+2. **Identify Hotspots**
+
+```cairo
+Use flame graphs to identify expensive operations
+Focus on functions with high step counts
+Look for repeated storage operations
+```
+
+3. **Optimize and Compare**
+
+```cairo
+cairo-profiler optimized_trace.bin --output-path optimized.pb.gz
+```
+
+4. **Validate Improvements**
+
+- Compare step counts and resource usage
+- Verify optimization doesn't introduce new inefficiencies
+- Document performance gains
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 06575621..73883771 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -5,6 +5,16 @@ settings:
excludeLinksFromLockfile: false
overrides:
+ vocs>shiki: ^1.23.0
+ vocs>@shikijs/core: ^1.23.0
+ vocs>@shikijs/types: ^1.23.0
+ vocs>@shikijs/rehype: ^1.23.0
+ vocs>@shikijs/twoslash: ^1.23.0
+ vocs>@shikijs/transformers: ^1.23.0
+
+patchedDependencies:
+ vocs:
+ hash: 7wumpnts656yvepr4seo2mjn34
vocs>shiki: ^1.23.0 <1.24.0
vocs>@shikijs/core: ^1.23.0 <1.24.0
vocs>@shikijs/types: ^1.23.0 <1.24.0
@@ -41,6 +51,8 @@ importers:
version: 5.7.3
vocs:
specifier: 1.0.0-alpha.62
+ version: 1.0.0-alpha.62(patch_hash=7wumpnts656yvepr4seo2mjn34)(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.24.3)(typescript@5.6.3)
+ version: 1.0.0-alpha.62(patch_hash=472ea4771ea28176fd7a35b8f83fe164ce160c07b9b50111ae749eb8da92176d)(@types/node@22.13.1)(@types/react-dom@18.3.5(@types/react@19.0.8))(@types/react@19.0.8)(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.34.6)(typescript@5.7.3)
version: 1.0.0-alpha.62(patch_hash=472ea4771ea28176fd7a35b8f83fe164ce160c07b9b50111ae749eb8da92176d)(@types/node@22.13.1)(@types/react-dom@18.3.5(@types/react@19.0.9))(@types/react@19.0.9)(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.34.6)(typescript@5.7.3)
devDependencies:
'@types/react-dom':
@@ -1384,6 +1396,9 @@ packages:
emoji-regex-xs@1.0.0:
resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
+ emoji-regex-xs@1.0.0:
+ resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
+
emoji-regex@10.4.0:
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
@@ -2287,12 +2302,16 @@ packages:
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+ regex-recursion@4.2.1:
+ resolution: {integrity: sha512-QHNZyZAeKdndD1G3bKAbBEKOSSK4KOHQrAJ01N1LJeb0SoH4DJIeFhp0uUpETgONifS4+P3sOgoA1dhzgrQvhA==}
regex-recursion@4.3.0:
resolution: {integrity: sha512-5LcLnizwjcQ2ALfOj95MjcatxyqF5RPySx9yT+PaXu3Gox2vyAtLDjHB8NTJLtMGkvyau6nI3CfpwFCjPUIs/A==}
regex-utilities@2.3.0:
resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
+ regex@5.0.2:
+ resolution: {integrity: sha512-/pczGbKIQgfTMRV0XjABvc5RzLqQmwqxLHdQao2RTXPk+pmTXB2P0IaUHYdYyk412YLwUIkaeMd5T+RzVgTqnQ==}
regex@5.1.1:
resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==}
@@ -3501,6 +3520,7 @@ snapshots:
'@shikijs/engine-javascript': 1.23.1
'@shikijs/engine-oniguruma': 1.23.1
'@shikijs/types': 1.23.1
+ '@shikijs/vscode-textmate': 9.3.0
'@shikijs/vscode-textmate': 9.3.1
'@types/hast': 3.0.4
hast-util-to-html: 9.0.4
@@ -3508,12 +3528,14 @@ snapshots:
'@shikijs/engine-javascript@1.23.1':
dependencies:
'@shikijs/types': 1.23.1
+ '@shikijs/vscode-textmate': 9.3.0
'@shikijs/vscode-textmate': 9.3.1
oniguruma-to-es: 0.4.1
'@shikijs/engine-oniguruma@1.23.1':
dependencies:
'@shikijs/types': 1.23.1
+ '@shikijs/vscode-textmate': 9.3.0
'@shikijs/vscode-textmate': 9.3.1
'@shikijs/rehype@1.23.1':
@@ -3528,7 +3550,11 @@ snapshots:
'@shikijs/transformers@1.23.1':
dependencies:
shiki: 1.23.1
-
+ '@shikijs/twoslash@1.23.1(typescript@5.6.3)':
+ dependencies:
+ '@shikijs/core': 1.23.1
+ '@shikijs/types': 1.23.1
+ twoslash: 0.2.12(typescript@5.6.3)
'@shikijs/twoslash@1.23.1(typescript@5.7.3)':
dependencies:
'@shikijs/core': 1.23.1
@@ -3937,6 +3963,8 @@ snapshots:
emoji-regex-xs@1.0.0: {}
+ emoji-regex-xs@1.0.0: {}
+
emoji-regex@10.4.0: {}
emoji-regex@8.0.0: {}
@@ -5023,6 +5051,8 @@ snapshots:
oniguruma-to-es@0.4.1:
dependencies:
emoji-regex-xs: 1.0.0
+ regex: 5.0.2
+ regex-recursion: 4.2.1
regex: 5.1.1
regex-recursion: 4.3.0
@@ -5268,12 +5298,14 @@ snapshots:
regenerator-runtime@0.14.1: {}
+ regex-recursion@4.2.1:
regex-recursion@4.3.0:
dependencies:
regex-utilities: 2.3.0
regex-utilities@2.3.0: {}
+ regex@5.0.2:
regex@5.1.1:
dependencies:
regex-utilities: 2.3.0
@@ -5488,6 +5520,7 @@ snapshots:
'@shikijs/engine-javascript': 1.23.1
'@shikijs/engine-oniguruma': 1.23.1
'@shikijs/types': 1.23.1
+ '@shikijs/vscode-textmate': 9.3.0
'@shikijs/vscode-textmate': 9.3.1
'@types/hast': 3.0.4
@@ -5777,6 +5810,8 @@ snapshots:
'@types/node': 22.13.1
fsevents: 2.3.3
+ vocs@1.0.0-alpha.62(patch_hash=7wumpnts656yvepr4seo2mjn34)(@types/node@22.8.6)(@types/react-dom@18.3.1)(@types/react@18.3.12)(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.24.3)(typescript@5.6.3):
+ vocs@1.0.0-alpha.62(patch_hash=472ea4771ea28176fd7a35b8f83fe164ce160c07b9b50111ae749eb8da92176d)(@types/node@22.13.1)(@types/react-dom@18.3.5(@types/react@19.0.8))(@types/react@19.0.8)(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.34.6)(typescript@5.7.3):
vocs@1.0.0-alpha.62(patch_hash=472ea4771ea28176fd7a35b8f83fe164ce160c07b9b50111ae749eb8da92176d)(@types/node@22.13.1)(@types/react-dom@18.3.5(@types/react@19.0.9))(@types/react@19.0.9)(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.34.6)(typescript@5.7.3):
dependencies:
'@floating-ui/react': 0.26.28(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -5785,6 +5820,19 @@ snapshots:
'@mdx-js/rollup': 3.1.0(acorn@8.14.0)(rollup@4.34.6)
'@noble/hashes': 1.7.1
'@radix-ui/colors': 3.0.0
+ '@radix-ui/react-accordion': 1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-dialog': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-icons': 1.3.1(react@18.3.1)
+ '@radix-ui/react-label': 2.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-navigation-menu': 1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-popover': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-tabs': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@shikijs/rehype': 1.23.1
+ '@shikijs/transformers': 1.23.1
+ '@shikijs/twoslash': 1.23.1(typescript@5.6.3)
+ '@vanilla-extract/css': 1.16.0
+ '@radix-ui/react-accordion': 1.2.3(@types/react-dom@18.3.5(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-dialog': 1.1.6(@types/react-dom@18.3.5(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-accordion': 1.2.3(@types/react-dom@18.3.5(@types/react@19.0.9))(@types/react@19.0.9)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-dialog': 1.1.6(@types/react-dom@18.3.5(@types/react@19.0.9))(@types/react@19.0.9)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-icons': 1.3.2(react@19.0.0)
@@ -5835,6 +5883,7 @@ snapshots:
remark-parse: 11.0.0
serve-static: 1.16.2
shiki: 1.23.1
+ tailwindcss: 3.4.14
tailwindcss: 3.4.17
toml: 3.0.0
twoslash: 0.2.12(typescript@5.7.3)