-
Notifications
You must be signed in to change notification settings - Fork 764
Cairo 1.0 Q&A #2267
-
Ask all your questions about Cairo 1.0
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 19 comments 37 replies
-
Hi, Is it possible to execute bitwise operations on felts?.
Something like:
let a: felt = 1;
let b: felt = 3;
let c = a & b;
Beta Was this translation helpful? Give feedback.
All reactions
-
I also have this question. I do not see any bitwise operations for felt yet. What is the effort to implement this?
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
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
Beta Was this translation helpful? Give feedback.
All reactions
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
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"
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
How can I generate from a cairo file the memory and trace data as can be found in the old playground debug panel?
Beta Was this translation helpful? Give feedback.
All reactions
-
how can i get current block number in cairo contract? Just like block.number in solidity
Beta Was this translation helpful? Give feedback.
All reactions
-
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:
- 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?
- 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 fieldvariable_values. What exactly are the variables and why are they used? - 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!
Beta Was this translation helpful? Give feedback.
All reactions
-
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:
- 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).
- This are values of gas redeposits and withdraws and burns (happens at
branch_alignandwithdraw_gas{_,all}). - This the cost per branch of the libfunc.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
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.)?
Beta Was this translation helpful? Give feedback.
All reactions
-
- 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_aligncommand which burns the extra unused gas from the cheap branch. in general, there could've been more usage of theredeposit_gascommand 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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
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(); } } }
Beta Was this translation helpful? Give feedback.
All reactions
-
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();
Beta Was this translation helpful? Give feedback.
All reactions
-
@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?
Beta Was this translation helpful? Give feedback.
All reactions
-
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)
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 2
-
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!
Beta Was this translation helpful? Give feedback.
All reactions
-
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?? } }
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
Thanks.
Beta Was this translation helpful? Give feedback.
All reactions
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Oh, so efficiency isn't an issue? If so, the loop it totally fine, as it is just an example.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
if you are extending an existing string, this is basically the best you can do for the time being.
Beta Was this translation helpful? Give feedback.
All reactions
-
Ok, thanks for being patient with me!
Beta Was this translation helpful? Give feedback.
All reactions
-
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).
Beta Was this translation helpful? Give feedback.
All reactions
-
TBH I think we could use a category on discussions and post each question as separate post. Kinda Stack-overflow like
Beta Was this translation helpful? Give feedback.
All reactions
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
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)?
Beta Was this translation helpful? Give feedback.
All reactions
-
and destructuring.
let [e0, e1] = arr;
it was partially useful for some specific instances - just wasn't for the usage as array case.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Glad to be of help! Will open an issue
Beta Was this translation helpful? Give feedback.
All reactions
-
you can - but was already solved on main - and should be released soon.
Beta Was this translation helpful? Give feedback.
All reactions
-
Oh, didn't realize that. Will close it then
Beta Was this translation helpful? Give feedback.
All reactions
-
Beta Was this translation helpful? Give feedback.
All reactions
-
- There was no associated type at the time.
- Yes - but it would probably require some major high level changes (since we would want these traits to have a relation with the original
intotraits.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Yes I have. I found that the language server seems not working properly.
image
But I have already installed scrab.
Beta Was this translation helpful? Give feedback.
All reactions
-
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
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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).
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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 }
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
This would be a nice-to-have feature, nothing too important
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 1
-
|
How can I output the execution trace table from Cairo? Something like this:
I couldn't find any information on the Internet. The ChatGPT suggests |
Beta Was this translation helpful? Give feedback.