Skip to content

Commit

Permalink
SME comments
Browse files Browse the repository at this point in the history
  • Loading branch information
stoobie committed Jan 29, 2024
1 parent 6554dfa commit 67caf26
Showing 1 changed file with 60 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@

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 in the Cairo VM 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 `u256` value in your code, and the SDK takes care of the serialization and encoding.
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 to a single-member list that contains a `felt252` value.
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`
* `byte31`
* `bytes31`
* `felt252`
* Signed integers smaller than 252 bits: `i8`, `i16`, `i32`, `i64`, and `i128`.
+
Expand All @@ -37,9 +37,9 @@ For example, `-5` is serialized as stem:[P-5]. For more information on the value
The following Cairo data types have non-trivial serialization:

* `u256` and `u512`
* `array`
* enum
* struct
* arrays
* enums
* structs
* `ByteArray`, which represents strings


Expand All @@ -49,7 +49,7 @@ The following Cairo data types have non-trivial serialization:
Among unsigned integers, only `u256` and `u512` have non-trivial serialization.

[#serialization_in_u256_values]
=== Serialization in `u256` values
=== Serialization of `u256` values

A `u256` value in Cairo is represented by two `felt252` values, as follows:

Expand All @@ -58,7 +58,7 @@ A `u256` value in Cairo is represented by two `felt252` values, as follows:

For example:

* A `u256` variable whose decimal value is `2` is serialized as `[0,2]`. To understand why, examine the binary representation of the `2` and split it into two 128-bit parts, as follows:
* 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]
++++
Expand All @@ -85,26 +85,26 @@ For example:
// ++++
// |===

* A `u256` variable whose decimal value is `2^128^` is serialized as `[1,0]`. To understand why, examine the binary representation of the `2^128^` and split it into two 128-bit parts, as follows:
* 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\cdots1}_{\text{128 bits}} |
\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\cdots11}_{\text{128 bits}} \|
\underbrace{0\cdots011}_{\text{128 bits}} \|
\underbrace{0\cdots10100}_{\text{128 bits}}
++++

[#serialization_in_u512_values]
=== Serialization of `u512` values

A `u512` value in Cairo is a struct containing four `felt252` values, each representing a 128-bit limb of the original integer, starting from the most significant bit, similar to a `u256` value.
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]
Expand All @@ -124,8 +124,6 @@ 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:

// `3,0,10,0,20,1,0`

[stem]
++++
\underbrace{3}_{\textit{number_of_array_members}} ,
Expand All @@ -134,6 +132,7 @@ Each `u256` value in the array is represented by two `felt252` values. So the ar
\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
Expand All @@ -142,70 +141,52 @@ An enum is serialized as follows:

`<__index_of_enum_variant__>,<__serialized_variant_type__>`

For example, consider the following enum definition:
.Example: Serialization in an enum 1

[source,cairo]
----
enum WeekEnd \{
Saturday: (), // index=0, no associated value
Sunday: u256, // index=1, two 128-bit felts.
//The most significant bit is first.
enum Week {
Sunday: (), // index=1, the variant type is the unit type
Monday: u256, // index=2
}
----

Now consider the following instantiations of the enum, one with `Sunday`, and the other with `Monday`:

[.`Sunday`]
[source,cairo]
----
fn process(self: WeekEnd) \{
match self \{
WeekEnd::Sunday, // index=0, the variant type is the unit type
}
}
----

[.`Monday`]
[source,cairo]
----
fn process(self: WeekEnd) \{
match self \{
WeekEnd::Monday(5) // index=1, two 128-bit felts.
//The most significant bit is first.
}
}
----


The calldata for this enum is serialized as follows:
The `Week` enum is serialized as follows:

[cols=",,",]
|===
|Instance |Description |Values to pass in calldata
|Instance |Description |Serialized values to pass

|`WeekEnd::Sunday` |index=`0`, no corresponding value. |`0`
|`WeekEnd::Monday(5)` a|
index=`1`

One `u256` value=two `felt252` values of 128-bits each.

|`1,0,5`
|`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 need to represent each variant of a struct as a serialized set of `felt252` values, where each field value can hold up to 31 bytes (248 bits). This 31-byte chunk is referred to in this context as a _word_.

You serialize a struct by serializing its variants one at a time. Its values in calldata are in the order in which they appear in the _definition_ of the struct, even if the variants appear out of order in the instantiation of the struct.
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`:
For example, consider the following definition of the struct `MyStruct` and its instantiation as `struct`:

[source,cairo]
----
struct myStruct {
struct MyStruct {
a: u256,
b: felt252,
c: Array<felt252>
Expand All @@ -219,57 +200,53 @@ The calldata is the same for both of the following instantiations of the struct'
|===
a|[source,cairo]
----
let struct1 = myStruct {
let struct1 = MyStruct {
a: 2, b: 5, c: [1,2,3]
};
----

a|[source,cairo]
----
let struct1 = myStruct {
let struct1 = MyStruct {
b: 5, c: [1,2,3], a: 2
};
----
|===

The serialized calldata for this struct is determined as shown in the table xref:#calldata_serialization_for_a_struct_in_cairo[].
The serialization of `MyStruct` is determined as shown in the table xref:#serialization_for_a_struct_in_cairo[].

[#calldata_serialization_for_a_struct_in_cairo]
.Calldata 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 calldata
|Member |Description |Values to pass in serialization

| `a: 2`
| A `u256` value is serialized as two `felt252` values, the most significant bit is first.
| `0,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`
| [`3,1,2,3`]
|===

The calldata for this struct is serialized as follows: `0,2,5,3,1,2,3`



Combining the above, the struct is serialized as follows: `[0,2,5,3,1,2,3]`

[#serialization_of_ByteArray_values]
== Serialization of `ByteArray` values
[#serialization_of_byte_arrays]
== Serialization of byte arrays

A string is represented in Cairo as a `ByteArray` type. The first byte of each word in the byte array is the most significant byte in the word. A byte array has the following structure:
A string is represented in Cairo as a `ByteArray` type. A byte array is actually a struct with the following members:

[horizontal]
. *`data: Array<felt252>`* +
Contains one or more `felt252` values, each containing a maximum of 31 bytes.

. *Word Count:* Represents the number of 31-byte words in the array construct.
. *`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.

. *Data:* Consists of one or more felt252 values, each containing a maximum of 31 bytes.The last or only value is limited to 30 bytes or less.

A value of 30 bytes or less is a _pending word_.

3.The number of bytes of the pending word.
. *`pending_word_len: usize`* +
The number of bytes in `pending_word`.

.Example 1: A string shorter than 31 characters

Expand All @@ -278,12 +255,14 @@ Consider the string `hello`, which is represented by the 5-byte hex value `0x686
[source,cairo]
----
0, // Number of 31-byte words in the array construct.
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)
Expand All @@ -293,12 +272,10 @@ 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
Expand Down

0 comments on commit 67caf26

Please sign in to comment.