Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Cairo 1.0 Q&A #2267

Unanswered
AbdelStark asked this question in Q&A
Feb 24, 2023 · 19 comments · 37 replies
Discussion options

Ask all your questions about Cairo 1.0

You must be logged in to vote

Replies: 19 comments 37 replies

Comment options

Hi, Is it possible to execute bitwise operations on felts?.
Something like:

 let a: felt = 1;
 let b: felt = 3;
 let c = a & b;
You must be logged in to vote
2 replies
Comment options

I also have this question. I do not see any bitwise operations for felt yet. What is the effort to implement this?

Comment options

I also have this question. I do not see any bitwise operations for felt yet. What is the effort to implement this?

Hi @DrCapybara I think the reason is to motivate developers to use uN instead of felt252. You can also see that other basic operations (like comparisons >, <, ...) have been removed. As I remember one of the policies of this new Cairo1 is to move away from the use of felts so that could be it.

Comment options

I try to declare the official hello_starknet contract with command:
starknet declare --contract hello_starknet.sierra.json --account jc2

And I get error:

Sending the transaction with max_fee: 0.000001 ETH (1378300000000 WEI).
Got BadRequest while trying to access https://alpha4.starknet.io/gateway/add_transaction. Status code: 500; text: {"code": "StarknetErrorCode.COMPILATION_FAILED", "message": "Compilation failed. Error: Invalid Sierra program.\n"}.
Traceback (most recent call last):
 File "/home/ljc/cairo_venv/lib/python3.9/site-packages/services/external_api/client.py", line 187, in _send_request
 return await self._parse_response(
 File "/home/ljc/cairo_venv/lib/python3.9/site-packages/services/external_api/client.py", line 231, in _parse_response
 raise BadRequest(status_code=response.status, text=text)
services.external_api.client.BadRequest: HTTP error ocurred. Status: 500. Text: {"code": "StarknetErrorCode.COMPILATION_FAILED", "message": "Compilation failed. Error: Invalid Sierra program.\n"}
Error: BadRequest: HTTP error ocurred. Status: 500. Text: {"code": "StarknetErrorCode.COMPILATION_FAILED", "message": "Compilation failed. Error: Invalid Sierra program.\n"}

Maybe my starknet version is wrong? Or just the official contract does't work?

starknet version:
starknet 0.11.0.2

You must be logged in to vote
0 replies
Comment options

When I update a mutable variable inside a break condition of a loop it throw an error. This is the code:

let mut a = 0
loop {
 if condition {
 a = 1;
 break();
 }
 some_code
}

and this is the error:

error: Unsupported match. Currently, matches require one arm per variant, in the order of variant definition.
 --> fixed_bytes.cairo:110:9
 break();
 ^******^

This error is kind of odd, shouldn't this logic valid in Cairo1?

You must be logged in to vote
0 replies
Comment options

How to write code in Cairo that is similar to Rust's format! ?

format!("Hello, {}!", "world"); // => "Hello, world!"

use case:

let token_0 = 'ETH';
let token_1 = 'USDC';
let name = format!("{} / {} LP",token_0,token_1 ); // => "ETH / USDC LP"
You must be logged in to vote
0 replies
Comment options

In crates/cairo-lang-starknet/src/plugin/storage.rs, function handle_legacy_mapping_storage_var generates Cairo logic for computing addresses:

/// Generate getters and setters skeleton for a non-mapping member in the storage struct.
fn handle_legacy_mapping_storage_var(address: &str) -> String {
 format!(
 "
 mod $storage_var_name$ {{$extra_uses$
 use starknet::SyscallResultTrait;
 use starknet::SyscallResultTraitImpl;

 fn address(key: $key_type$) -> starknet::StorageBaseAddress {{
 starknet::storage_base_address_from_felt252(
 hash::LegacyHash::<$key_type$>::hash({address}, key))
 }}
 fn read(key: $key_type$) -> $value_type$ {{
 // Only address_domain 0 is currently supported.
 let address_domain = 0_u32;
 starknet::StorageAccess::<$value_type$>::read(
 address_domain,
 address(key),
 ).unwrap_syscall()
 }}
 fn write(key: $key_type,ドル value: $value_type$) {{
 // Only address_domain 0 is currently supported.
 let address_domain = 0_u32;
 starknet::StorageAccess::<$value_type$>::write(
 address_domain,
 address(key),
 value,
 ).unwrap_syscall()
 }}
 }}"
 )
}

Note the line

 starknet::storage_base_address_from_felt252(
 hash::LegacyHash::<$key_type$>::hash({address}, key))

The result of the inner hash is in the [0, PRIME) range. However, the base address should be in the [0, 2^251 - 256) range. Thus, there is an (incredibly small) probability of getting a panic. One solution is to take the result modulo 2^251 - 256, as described here, although I imagine it is quite expensive.
Maybe the small probability of getting such a situation is not worth safe-guards, please tell if so and feel free to close.

You must be logged in to vote
0 replies
Comment options

How can I generate from a cairo file the memory and trace data as can be found in the old playground debug panel?

You must be logged in to vote
0 replies
Comment options

how can i get current block number in cairo contract? Just like block.number in solidity

You must be logged in to vote
0 replies
Comment options

Hello! I'm currently utilizing the following crate:
https://docs.rs/cairo-lang-sierra-gas/latest/cairo_lang_sierra_gas
as a part of a package that will help the users analyze their Cairo code when it comes to how much gas it consumes. So far I was able to use the package in order to get gas costs for specific functions in the generated Sierra code as well for a specific libfuncs. I still have a couple of questions regarding the process of calculating gas that I wasn't been able to answer by myself by only browsing the code. If you could answer them or point me to a documentation (that I unfortunately wasn't able to find myself), I would be very gratefyul.

Here are my questions:

  1. Why there is a differentiation between pre and post cost? From what I saw, precosts are also taken into accoun when calculating post costs and I'm not sure why are they even separated?
  2. https://docs.rs/cairo-lang-sierra-gas/latest/cairo_lang_sierra_gas/gas_info/struct.GasInfo.html
    the gas info object contains the costs info for a specific function within Sierra but it also contains the field variable_values. What exactly are the variables and why are they used?
  3. I'm using the following function:
    https://docs.rs/cairo-lang-sierra-gas/latest/cairo_lang_sierra_gas/core_libfunc_cost/fn.core_libfunc_cost.html
    to calculate a cost for a specific libfunction. Why does it return a vector? Can there be mutliple costs for a specific libfunc? If so, what are the situations when it might happen?

Thanks in advance for any clarification here!

You must be logged in to vote
3 replies
Comment options

orizi May 6, 2024
Maintainer

First of all - you can probably consider just stating the value of the gas-builtin counter - it would indicate the actual amount of used gas in the counter - if you are actually trying to consider the gas mechanism, instead of the actual used resources.
if you want the actual used resources - you should count it specifically - using the vm itself.

You need the data you requested only for the process of emulating the actual used resources using the gas mechanism.

to answer the questions specifically:

  1. Pre cost is the cost of builtins that the amount of usage of them is not affected by ap-changes or gas fetching (which are all builtins but the RangeCheck builtin), Post cost are the steps, holes and RangeChecks costs. The separation is to solve the required gas taken and ap changes in stages: the pre-cost (which is independent), the ap-changes (which only depends on the pre-costs), and the post costs (dependent on both).
  2. This are values of gas redeposits and withdraws and burns (happens at branch_align and withdraw_gas{_,all}).
  3. This the cost per branch of the libfunc.
Comment options

Thanks for the detailed answer @orizi. I'm currently building a solution that is based on the gas mechanism (if I understand it correctly) because I use Sierra and it's metadata in order to get the costs for specific functions/libfuncs and then while executing the program, to illustrate where and how much gas is consumed. If this is an acceptable approach, it could be really informative when it comes to illustrating how it works in Cairo in general.

Some new questions that came to my mind after thinking about your answer:

  • from what I understand there are actually two ways to get the gas count. The "real" one is to use the resources consumed by the VM, which is the most accurate since the sequencers use it to actually charge the users. The Sierra-based approach is kind of an approximation (since you used the word emulation?) or is it supposed to be exactly the same? If I understand correctly, Sierra itself should already give the sequencer some info about the future gas consumption before running the program to be able to tell whether the user has enough gas to execute specific code?
  • what is the definition of builtins exactly? Is this a name for some base operations (like Poseidon application etc.)?
Comment options

  • The gas mechanism is an approximation of the worst case of the actual consumed resources, so it would charge different costs of different actually taken paths, (e.g. adding two u128s and overflowing has a different actual code being run then when not overflowing). Currently, when different branches are merged - the given gas consumption is merged as well using the branch_align command which burns the extra unused gas from the cheap branch. in general, there could've been more usage of the redeposit_gas command which would make this approximation be more tight with the actual usage of resources, currently it is a very non-tight upper bound.
  • Builtins are extensions of the Cairo CPU that enables more complex actions such as Pedersen, Poseidon, bitwise operations and so on.
Comment options

Hello!
I have a contract with a constructor that takes in a ByteArray parameter, and I want to deploy this contract from a factory using deploy_syscall. The problem is that this syscall expects all constructor parameters to be cast into Array<felt252>, which is a problem to do for a ByteArray parameter.

How can this be done?

Here's an example:

#[starknet::interface]
trait ITest<TContractState> {}
#[starknet::contract]
mod Test {
 #[storage]
 struct Storage {
 byte_array: ByteArray
 }
 #[constructor]
 fn constructor(ref self: ContractState, byte_array: ByteArray) {
 self.byte_array.write(byte_array);
 }
}
use starknet::ClassHash;
#[starknet::interface]
trait IFactory<TContractState> {
 fn create_test(self: @TContractState, class_hash: ClassHash, byte_array: ByteArray);
}
#[starknet::contract]
mod Factory {
 use starknet::syscalls::{deploy_syscall};
 use starknet::ClassHash;
 use starknet::SyscallResultTrait;
 #[storage]
 struct Storage {}
 #[abi(embed_v0)]
 impl Factory of super::IFactory<ContractState> {
 fn create_test(self: @ContractState, class_hash: ClassHash, byte_array: ByteArray) {
 let mut constructor_calldata: Array::<felt252> = array![];
 constructor_calldata.append(byte_array.into()); // <-- here's the problem
 // Trait has no implementation in context: core::traits::Into::<core::byte_array::ByteArray, core::felt252>.
 let (deployed_address, _) = deploy_syscall(
 class_hash, 0, constructor_calldata.span(), false
 )
 .unwrap_syscall();
 }
 }
}
You must be logged in to vote
4 replies
Comment options

orizi Jun 7, 2024
Maintainer

the calldata is the serialization of all parameters for the constructor:

let mut calldata = array![];
(param0, param1, param2).serialize(ref calldata);
let (deployed_address, _) = deploy_syscall(
 class_hash, 0, calldata.span(), false
 )
 .unwrap_syscall();
Comment options

@orizi so that's another way to serialize data for the constructor, awesome.


I used this approach in my own project and as soon as my tuple grows to 5+ elements, e.g.:

let mut constructor_calldata: Array::<felt252> = array![];
(caller, title, target, duration, this).serialize(ref constructor_calldata);

the compiler complains with:

Method `serialize` could not be called on type `(core::starknet::contract_address::ContractAddress, core::felt252, core::integer::u256, core::integer::u64, core::starknet::contract_address::ContractAddress)`.
Candidate `Serde::serialize` inference failed with: Trait has no implementation in context: core::serde::Serde::<(core::starknet::contract_address::ContractAddress, core::felt252, core::integer::u256, core::integer::u64, core::starknet::contract_address::ContractAddress)>.

But when I remove any of the tuple members, the compiler error disappears.

I even tried this with 5 elements of the same type, and got the same compiler error.

This seems to be some hard cap on the maximum number of elements in a tuple that can be serialized.

Is this expected behavior?

Comment options

orizi Jun 7, 2024
Maintainer

Yes, we have implementation for tuples of up to 4 elements.
You can solve this by multiple calls, or just a tuple of tuples (it would logically be flattened)

Comment options

You can solve this by... a tuple of tuples (it would logically be flattened)

🤯

So simple, yet magical. Thanks for the great and timely help!

Comment options

Is there a way to get the current class hash inside a contract?

Something like:

#[starknet::interface]
trait ITest<TContractState> {
 fn test(self: @TContractState);
}
#[starknet::contract]
mod Test {
 use starknet::ClassHash;
 #[storage]
 struct Storage {}
 fn test(self: @ContractState) {
 let cur_class_hash: ClassHash = // what function to use to get the current contract's class hash??
 }
}
You must be logged in to vote
2 replies
Comment options

ATM you can't. We want to add two small syscalls that will address this (you only need the first but listing both anyway):

  • get_class_hash_at(contract_address: ContractAddress)
  • is_declared(class_hash: ClassHash)

This is coupled with Starknet support, and can only make it to Starknet >= 0.13.3, hence it won't be present in the next few months.

Comment options

Thanks.

Comment options

What is the most gas-efficient way to accumulate string concatenations in a loop?

fn print_he_10000_times() {
 let mut he_10000_times: ByteArray = "";
 let mut n: u8 = 0;
 while n < 10000 {
 he_10000_times.append(@"he");
 n += 1;
 };
 println!("{he_10000_times}");
}
fn main() {
 print_he_10000_times();
}

P.S. Unrelated, but if I change the println!("{he_10000_times}"); line to be println!(he_10000_times);, the compiler complains with "Plugin diagnostic: Format string argument must be a string literal.".
Is this expected behavior?
If so, why not enable printing the ByteArray variable directly?

You must be logged in to vote
8 replies
Comment options

I have a very specific use-case - I'm creating a Cairo track on Exercism. One of the exercises I'm implementing requires the learner to concatenate a string a bunch of times to create the famous "99 Bottles of Beer" song. The exercise is supposed to help the learner practice using loops, if-statements and strings.

Comment options

Oh, so efficiency isn't an issue? If so, the loop it totally fine, as it is just an example.

Comment options

Yeah, in retrospect, I should've included this in my original message, it would have added some important context.

I was wondering whether there exists some function or trait (or approach) that does this string concatenation more efficiently, so that I can include it in the solution explanation as a fun tip for the learner.
Is there by any chance anything like that in Cairo?

Comment options

if you are extending an existing string, this is basically the best you can do for the time being.

Comment options

Ok, thanks for being patient with me!

Comment options

In general, this issue isn't really scalable, I suggest asking in the cairo discord, or the cairo questions tg group (the latter requires filling a form to show you're a real person).

You must be logged in to vote
1 reply
Comment options

TBH I think we could use a category on discussions and post each question as separate post. Kinda Stack-overflow like

Comment options

Fixed-size arrays are available in Cairo, but I how can I use array-related function with them?
When I try using get, at, etc., the compiler complains that those methods are not defined on fixed-size arrays.

E.g. for this code:

fn main() {
 let arr: [felt252; 2] = [1, 2];
 let sp: Span<felt252> = arr.span();
}

I get the following error:
image

Does that mean fixed-size arrays are not yet fully supported in the language?

You must be logged in to vote
3 replies
Comment options

only .span() is actually fully allowed on them for the time being.
your case seems like an old version case - after the next release .span() would definitely work.

Comment options

It's kind of confusing to be using a regular release (so not "nightly") and not have full features for a feature.
So fixed-size arrays basically cannot be used in code for the time being (other then just declaring them)?

Comment options

and destructuring.

let [e0, e1] = arr;

it was partially useful for some specific instances - just wasn't for the usage as array case.

Comment options

I am trying to understand why the compiler is not able to infer the type of Option::None literal when using it inside the assert_eq! macro.

Example code:

fn to_option(num: usize) -> Option<usize> {
 if num > 10 {
 Option::Some(num)
 } else {
 Option::None
 }
}
fn from_option(opt_num: Option<usize>) -> usize {
 if let Option::Some(num) = opt_num {
 num
 } else {
 0
 }
}
#[cfg(test)]
mod tests {
 #[test]
 fn test_to_option() {
 let num: usize = 5;
 assert_eq!(super::to_option(num), Option::None); // Type annotations needed. Failed to infer ?15
 }
 #[test]
 fn test_from_option() {
 assert_eq!(super::from_option(Option::None), 0); // this infers the Option::None type with no issues
 }
}

Why is the compiler able to infer the type of the Option::None literal in the latter test, while it failed to do so in the former?
Seems weird.

You must be logged in to vote
4 replies
Comment options

Thanks for finding this bug - will be fixed with the next release.
the issue is that since Option::None is a path - it was considered a variable - when it should have been considered an expression.
this is a bug in the assert_eq! macro suite.

Comment options

Glad to be of help! Will open an issue

Comment options

you can - but was already solved on main - and should be released soon.

Comment options

Oh, didn't realize that. Will close it then

Comment options

  1. What was the reason Cairo's TryInto returns an Option type, instead of Result like in Rust?
    Seems useful to return Result, as it would allow to pass the reason for conversion failure.
  2. Is there a plan to implement From and TryFrom from Rust?
You must be logged in to vote
2 replies
Comment options

  1. There was no associated type at the time.
  2. Yes - but it would probably require some major high level changes (since we would want these traits to have a relation with the original into traits.
Comment options

Is there anything like a list of (Rust-like) features that are planned to be implemented for Cairo?

I found the Cairo 0 parity roadmap, but that's not what I'm referring to; I'm talking about new traits, new syntax, new structs etc.

Comment options

Hey guys! I am using scrab and cairo with version 2.6.3. I installed Cairo 1.0 plugin in my VSC, but the syntax highlighting seems not working properly.
image
What should I do?

You must be logged in to vote
3 replies
Comment options

Your file is named hello_world.cairo. Do you have a module with the same name defined in your /src/lib.cairo file?
If not, open your /src/lib.cairo and paste the line mod hello_world anywhere inside it.

You need this for the compiler to know that hello_world.cairo is part of your project. This will also enable syntax highlighting.

Comment options

Yes I have. I found that the language server seems not working properly.
image
But I have already installed scrab.

Comment options

please install VSCode extension manually from main and then follow these steps to debug your problem: https://github.com/starkware-libs/cairo/tree/main/vscode-cairo#troubleshooting

on main I have added a bunch of trace logs that are telling what the extension sees when looking for scarb

Comment options

I'm trying to understand smart pointers and how to use them to implement a doubly linked list.

When I have two boxes box_a and box_b referencing the same value, I would expect that updating the value through box_a would be reflected in box_b, but that is not the case!

See the below example:

type List<T> = Option<Box<Node<T>>>;
#[derive(Drop, Copy, Debug)]
struct Node<T> {
 data: T,
 next: List<T>,
 previous: List<T>
}
#[derive(Drop, Copy, Debug)]
struct DoublyLinkedList<T> {
 head: List<T>,
 tail: List<T>,
 len: usize,
}
#[generate_trait]
impl DoublyLinkedListImpl<T, +Drop<T>, +core::fmt::Debug<T>, +Copy<T>> of DoublyLinkedListTrait<T> {
 fn new() -> DoublyLinkedList<T> {
 DoublyLinkedList { head: Option::None, tail: Option::None, len: 0 }
 }
 fn push(ref self: DoublyLinkedList<T>, data: T) {
 match self.tail {
 Option::None => {
 let node = BoxTrait::new(Node { data, next: Option::None, previous: Option::None });
 self.tail = Option::Some(node);
 // head & tail should now point to the same node
 self.head = self.tail;
 self.len += 1;
 },
 Option::Some(tail) => {
 let node = BoxTrait::new(
 Node { data, next: Option::None, previous: Option::Some(tail) }
 );
 let mut tail = tail.unbox();
 // Here's the problem! Since both head & tail point to the same node,
 // I would expect the below assignment to update *both* of the boxes
 // **But only `tail` gets updated!!!** (see below printlns)
 tail.next = Option::Some(node);
 self.tail = tail.next;
 self.len += 1;
 },
 }
 }
}
fn main() {
 let mut list = DoublyLinkedListTrait::<u32>::new();
 println!("init");
 println!("head: {:?}", list.head); // Option::None(())
 println!("tail: {:?}\n", list.tail); // Option::None(())
 list.push(23);
 println!("push(23)");
 println!("head: {:?}", list.head); // Option::Some(&Node { data: 23, next: Option::None(()), previous: Option::None(()) })
 println!("tail: {:?}\n", list.tail); // Option::Some(&Node { data: 23, next: Option::None(()), previous: Option::None(()) })
 list.push(5);
 println!("push(5)");
 println!("head: {:?}", list.head); // Option::Some(&Node { data: 23, next: Option::None(()), previous: Option::None(()) })
 println!("tail: {:?}\n", list.tail); // Option::Some(&Node { data: 5, next: Option::None(()), previous: Option::Some(&Node { data: 23, next: Option::None(()), previous: Option::None(()) }) })
}

Why does this behavior occur?
It seems I'm misunderstanding both what smart pointers are and how they work. I guess the problem is that when I "unbox" the struct, it gets copied, thus I'm never modifying the original struct.

Also I would really appreciate an explanation on how to accomplish this the correct Cairo way.

You must be logged in to vote
3 replies
Comment options

orizi Jul 6, 2024
Maintainer

The initial issue with this approach is the fact that Cairo's memory is immutable.
Since it is immutable - if you try to edit any node, the updated nodes address has to be different, so any pointed node must be changed.
So in the case of a doubly liked list, all nodes points to all other nodes, so you cannot change it.
A simple linked list, and editing of only its head would be fine (as the tail nodes don't point to head nodes).

Comment options

Thanks for the clarification! I figured it had something to do with memory immutability.
I'll try to think of another way to accomplish the doubly-linked list, maybe there is a way.

Comment options

For anyone facing a similar issue, I used the following structure to simulate a doubly linked list:

use core::dict::Felt252Dict;
type Index = Option<felt252>;
struct DoublyLinkedList<T> {
 dict: Felt252Dict<Nullable<Node<T>>>,
 head: Index,
 tail: Index,
 len: usize,
 next_index: felt252,
}
#[derive(Drop, Copy)]
struct Node<T> {
 station: T,
 next: Index,
 previous: Index
}
Comment options

Is there a way to compile and run a Cairo file without the use of Scarb?
E.g. create a main.cairo file and run it directly (without Scarb.toml and lib.cairo)?

For context, in Rust I can create a main.rs file and run it with rustc main.rs.

You must be logged in to vote
2 replies
Comment options

This would be a nice-to-have feature, nothing too important

Comment options

orizi Sep 9, 2024
Maintainer

if you use no dependencies (so scarb isn't used):
The cairo-run crate can be used with:
cairo-run --single-file /path/to/your/main.cairo - it will run the main fn in that file.

Comment options

How can I output the execution trace table from Cairo? Something like this:

Reg1 Reg2
13 21
610 987

I couldn't find any information on the Internet. The ChatGPT suggests cairo-run --program compiled_program.json --print_trace > trace.json command, but cairo-run doesn't have such arguments.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /