-
Notifications
You must be signed in to change notification settings - Fork 206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docs: Added new topic: Serialization of types in Cairo #1041
Changes from 10 commits
569e5f8
bd4f2d2
beec417
0b4f2bd
f49c4ab
fd89006
d9fe9e0
02ea0b1
6554dfa
67caf26
4e7bfb3
e674148
89c0e03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
{} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,284 @@ | ||
[id="serialization_of_types_in_Cairo"] | ||
= Serialization of types in Cairo | ||
|
||
When you interact with contracts, especially if you are a library or SDK developer that wants to create transactions, you need to understand how Cairo handles types that are larger than 252 bits so you can correctly formulate the calldata in a transaction. | ||
|
||
The field element (`felt252`), which contains 252 bits, is the only actual type in the Cairo VM. So all high-level Cairo types that are larger than 252 bits, such as `u256` or arrays, are ultimately represented by a list of felts. In order to interact with a contract, you need to know how to encode its arguments as a list of felts so you can correctly formulate the calldata in the transaction. | ||
SDKs, such as starknet.js, encode the calldata for you, so you can simply specify any type and the SDK properly formulates the calldata. For example, you don’t need to know that a `u256` value is represented by two `felt252` values. You can simply specify a single integer in your code, and the SDK takes care of the serialization and encoding. | ||
|
||
|
||
[#data_types_of_252_bits_or_less] | ||
== Data types of 252 bits or less | ||
|
||
The following types are smaller than 252 bits. For these types, each value is serialized as a list that contains a single value, of type `felt252`. | ||
|
||
* `ContractAddress` | ||
* `EthAddress` | ||
* `StorageAddress` | ||
* `ClassHash` | ||
* Unsigned integers smaller than 252 bits: `u8`, `u16`, `u32`, `u64`, `u128`, and `usize` | ||
* `bytes31` | ||
* `felt252` | ||
* Signed integers smaller than 252 bits: `i8`, `i16`, `i32`, `i64`, and `i128`. | ||
+ | ||
A negative value, stem:[-x], is serialized as stem:[P-x], where: | ||
+ | ||
[stem] | ||
++++ | ||
P = 2^{251} + 17*2^{192} + 1 | ||
++++ | ||
+ | ||
For example, `-5` is serialized as stem:[P-5]. For more information on the value of stem:[P], see xref:architecture_and_concepts:Cryptography/p-value.adoc[The STARK field]. | ||
|
||
|
||
[#data_types_greater_than_252_bits] | ||
== Data types greater than 252 bits | ||
|
||
The following Cairo data types have non-trivial serialization: | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The following data types in Cairo have a non-trivial serialization: |
||
* `u256` and `u512` | ||
* arrays | ||
* enums | ||
* structs | ||
* `ByteArray`, which represents strings | ||
|
||
|
||
[#serialization_of_unsigned_integers] | ||
== Serialization of unsigned integers | ||
|
||
Among unsigned integers, only `u256` and `u512` have non-trivial serialization. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Among uints, only |
||
[#serialization_in_u256_values] | ||
=== Serialization of `u256` values | ||
|
||
A `u256` value in Cairo is represented by two `felt252` values, as follows: | ||
|
||
* The first `felt252` value contains the 128 most significant bits, usually referred to as the high part of the original `u256` value. | ||
* The second `felt252` value contains the 128 least significant bits, usually referred to as the low part of the original `u256` value. | ||
|
||
For example: | ||
|
||
* A `u256` variable whose decimal value is `2` is serialized as `[0,2]`. To understand why, examine the binary representation of `2` and split it into two 128-bit parts, as follows: | ||
+ | ||
[stem] | ||
++++ | ||
\underbrace{0\cdots0}_{\text{128 bits}} | | ||
\underbrace{0\cdots10}_{\text{128 bits}} | ||
++++ | ||
// | ||
// [#binary_representation_of_u256] | ||
// .Binary representation of `2` in a serialized `u256` | ||
// [%autowidth,cols="2"] | ||
// |=== | ||
// |`felt252`~1~ = `0`~binary~ = `0`~decimal~|`felt252`~2~ = `10`~binary~ = `2~decimal~` | ||
// | ||
// a|//`0b000...000` | ||
// [stem] | ||
// ++++ | ||
// \underbrace{0\cdots0}_{\text{128 bits}} | ||
// ++++ | ||
// a| //`0b000...000` | ||
// [stem] | ||
// ++++ | ||
// \underbrace{0\cdots0}_{\text{128 bits}} | ||
// \underbrace{0\cdots10}_{\text{128 bits}} | ||
// ++++ | ||
// |=== | ||
|
||
* A `u256` variable whose decimal value is `2^128^` is serialized as `[1,0]`. To understand why, examine the binary representation of `2^128^` and split it into two 128-bit parts, as follows: | ||
+ | ||
[stem] | ||
++++ | ||
\underbrace{0\cdots01}_{\text{128 bits}} | | ||
\underbrace{0\cdots0}_{\text{128 bits}} | ||
++++ | ||
|
||
* A `u256` variable whose decimal value is `2^129^+2^128^+20`, is serialized as `[3,20]`. To understand why, examine the binary representation of the `2^129^+2^128^+20` and split it into two 128-bit parts, as follows: | ||
+ | ||
[stem] | ||
++++ | ||
\underbrace{0\cdots011}_{\text{128 bits}} \| | ||
\underbrace{0\cdots10100}_{\text{128 bits}} | ||
++++ | ||
|
||
[#serialization_in_u512_values] | ||
=== Serialization of `u512` values | ||
|
||
The `u512` type in Cairo is a struct containing four `felt252` members, each representing a 128-bit limb of the original integer, similar to the `u256` type. | ||
|
||
|
||
[#serialization_of_arrays] | ||
== Serialization of arrays | ||
|
||
Arrays are serialized as follows: | ||
|
||
`<__array_length__>, <__serialized_member_0__>,..., <__serialized_member_n__>` | ||
|
||
For example, consider the following array of `u256` values: | ||
|
||
[source,cairo] | ||
---- | ||
let POW_2_128: u256 = 0x100000000000000000000000000000000 | ||
let array: Array<u256> = array![10, 20, POW_2_128] | ||
---- | ||
|
||
Each `u256` value in the array is represented by two `felt252` values. So the array above is serialized as follows: | ||
|
||
[stem] | ||
++++ | ||
\underbrace{3}_{\textit{number_of_array_members}} , | ||
\underbrace{0,10}_{\textit{serialized_member_0}} , | ||
\underbrace{0,20}_{\textit{serialized_member_1}} , | ||
\underbrace{1,0}_{\textit{serialized_member_2}} | ||
++++ | ||
|
||
Combining the above, the array is serialized as follows: `[3,0,10,0,20,1,0]` | ||
|
||
[#serialization_of_enums] | ||
== Serialization of enums | ||
|
||
An enum is serialized as follows: | ||
|
||
`<__index_of_enum_variant__>,<__serialized_variant_type__>` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above re __ |
||
|
||
.Example: Serialization in an enum 1 | ||
|
||
[source,cairo] | ||
---- | ||
enum Week { | ||
Sunday: (), // index=1, the variant type is the unit type | ||
Monday: u256, // index=2 | ||
} | ||
---- | ||
|
||
The `Week` enum is serialized as follows: | ||
|
||
[cols=",,",] | ||
|=== | ||
|Instance |Description |Serialized values to pass | ||
|
||
|`Week::Sunday` |index=`1`, the variant type is the unit type | `1` | ||
|`Week::Monday` a| index=`2` |The variant's type is `u256`. It's serialization is `[2,<__serialized_variant_type__>]`. | ||
|=== | ||
|
||
.Example: Serialization in an enum 2 | ||
[source,cairo] | ||
---- | ||
enum MessageType { | ||
A, | ||
#[default] | ||
B | ||
C: | ||
} | ||
---- | ||
|
||
The variant A is serialized as 1, variant B as 0, because it is marked as `#[default]`, and variant C as 2. So the `MessageType` enum is serialized as follows: `[1,0,2]` | ||
|
||
[#serialization_of_structs] | ||
== Serialization of structs | ||
|
||
You serialize a struct by serializing its members one at a time. | ||
|
||
Its members are serialized in the order in which they appear in the definition of the struct. | ||
|
||
|
||
For example, consider the following definition of the struct `MyStruct` and its instantiation as `struct`: | ||
|
||
[source,cairo] | ||
---- | ||
struct MyStruct { | ||
a: u256, | ||
b: felt252, | ||
c: Array<felt252> | ||
} | ||
|
||
---- | ||
|
||
The calldata is the same for both of the following instantiations of the struct's variants: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to emphasize this point, it's enough to say it's determined by the struct definition. |
||
|
||
[cols="2"] | ||
|=== | ||
a|[source,cairo] | ||
---- | ||
let struct1 = MyStruct { | ||
a: 2, b: 5, c: [1,2,3] | ||
}; | ||
---- | ||
|
||
a|[source,cairo] | ||
---- | ||
let struct1 = MyStruct { | ||
b: 5, c: [1,2,3], a: 2 | ||
}; | ||
---- | ||
|=== | ||
|
||
The serialization of `MyStruct` is determined as shown in the table xref:#serialization_for_a_struct_in_cairo[]. | ||
|
||
[#serialization_for_a_struct_in_cairo] | ||
.Serialization for a struct in Cairo | ||
[cols="3"] | ||
|=== | ||
|Member |Description |Values to pass in serialization | ||
|
||
| `a: 2` | ||
| For information on serializing `u256` values, see xref:#serialization_in_u256_values[] | ||
| [`0,2`] | ||
| `b: 5` | ||
| One `felt252` value | ||
| `5` | ||
| `c: [1,2,3]` | ||
| An array of three `felt252` values | ||
| [`3,1,2,3`] | ||
|=== | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
Combining the above, the struct is serialized as follows: `[0,2,5,3,1,2,3]` | ||
|
||
[#serialization_of_byte_arrays] | ||
== Serialization of byte arrays | ||
|
||
A string is represented in Cairo as a `ByteArray` type. A byte array is actually a struct with the following members: | ||
|
||
. *`data: Array<felt252>`* + | ||
Contains one or more `felt252` values, each containing a maximum of 31 bytes. | ||
|
||
. *`pending_word: felt252`* + | ||
The last, or only, value is a `felt252` value that represents the remainder of the byte array. It consists of at most 30 bytes. | ||
|
||
. *`pending_word_len: usize`* + | ||
The number of bytes in `pending_word`. | ||
|
||
.Example 1: A string shorter than 31 characters | ||
|
||
Consider the string `hello`, which is represented by the 5-byte hex value `0x68656c6c6f`. The resulting byte array is serialized as follows: | ||
|
||
[source,cairo] | ||
---- | ||
|
||
0, // Number of 31-byte words in the data array. | ||
0x68656c6c6f, // Pending word | ||
5 // Length of the pending word, in bytes | ||
|
||
---- | ||
|
||
.Example 2: A string longer than 31 bytes | ||
|
||
Consider the string `Long string, more than 31 characters.`, which is represented by the following hex values: | ||
|
||
* `0x4c6f6e6720737472696e672c206d6f7265207468616e203331206368617261` (31-byte word) | ||
* `0x63746572732e` (6-byte pending word) | ||
|
||
The resulting byte array is serialized as follows: | ||
|
||
[source,cairo] | ||
---- | ||
1, // Number of 31-byte words in the array construct. | ||
0x4c6f6e6720737472696e672c206d6f7265207468616e203331206368617261, // 31-byte word. | ||
0x63746572732e, // Pending word | ||
6 // Length of the pending word, in bytes | ||
---- | ||
|
||
== Additional resources | ||
|
||
* link:https://book.cairo-lang.org/ch02-02-data-types.html#integer-types[Integer types] in _The Cairo Programming Language_. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest em dashes instead of commas here, but not crucial if you prefer the commas
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Mrericsumner Yeah, although the Google style guide allows them, I've always been able to get around them.