Arbitrum Stylus logo

Stylus by Example

Storage Data Types in Stylus

The Stylus Rust SDK provides a way to define state variables that are stored on the blockchain, similar but not the same to how state variables work in Solidity. These variables persist across contract calls and represent the contract's persistent storage. In Stylus, you define these storage variables within a struct annotated with #[storage].

In summary, state variables in the Stylus Rust SDK have the following types:

  • StorageBool
  • StorageAddress
  • StorageUint
  • StorageSigned
  • StorageFixedBytes
  • StorageVec
  • StorageString
  • StorageBytes
  • StorageMap
  • StorageArray

StorageString is essentially a wrapper around StorageBytes, providing convenient methods for working with UTF-8 encoded strings.

StorageMap and StorageArray will be discussed in separate sections due to their more complex nature. So this section focuses on the first 8 types.

Defining the Storage Struct

1#![cfg_attr(not(any(feature = "export-abi", test)), no_main)]
2extern crate alloc;
3
4use stylus_sdk::{
5    prelude::*,
6    alloy_primitives::{I256, U256, Address, FixedBytes},
7    storage::*,
8};
9
10#[storage]
11#[entrypoint]
12pub struct Data {
13    my_bool: StorageBool,
14    my_address: StorageAddress,
15    my_uint: StorageU256,
16    my_signed: StorageI256,
17    my_fixed_bytes: StorageFixedBytes<4>,
18    my_bytes: StorageBytes,
19    my_string: StorageString,
20    my_vec: StorageVec<StorageU256>,
21}
22
23#[public]
24impl Data {
25    //... (Getters and Setters as shown below)
26}
1#![cfg_attr(not(any(feature = "export-abi", test)), no_main)]
2extern crate alloc;
3
4use stylus_sdk::{
5    prelude::*,
6    alloy_primitives::{I256, U256, Address, FixedBytes},
7    storage::*,
8};
9
10#[storage]
11#[entrypoint]
12pub struct Data {
13    my_bool: StorageBool,
14    my_address: StorageAddress,
15    my_uint: StorageU256,
16    my_signed: StorageI256,
17    my_fixed_bytes: StorageFixedBytes<4>,
18    my_bytes: StorageBytes,
19    my_string: StorageString,
20    my_vec: StorageVec<StorageU256>,
21}
22
23#[public]
24impl Data {
25    //... (Getters and Setters as shown below)
26}

The #[storage] attribute on the Data struct tells Stylus that the fields within this struct represent the contract's state storage.

StorageBool

StorageBool stores a boolean value (true or false).

1//... inside the #[public] impl Data block
2
3    pub fn get_bool(&self) -> bool {
4        self.my_bool.get()
5    }
6
7    pub fn set_bool(&mut self, value: bool) {
8        self.my_bool.set(value);
9    }
1//... inside the #[public] impl Data block
2
3    pub fn get_bool(&self) -> bool {
4        self.my_bool.get()
5    }
6
7    pub fn set_bool(&mut self, value: bool) {
8        self.my_bool.set(value);
9    }

StorageAddress

StorageAddress stores an Ethereum address (alloy_primitives::Address).

1//... inside the #[public] impl Data block
2
3    pub fn get_address(&self) -> Address {
4        self.my_address.get()
5    }
6
7    pub fn set_address(&mut self, value: Address) {
8        self.my_address.set(value);
9    }
1//... inside the #[public] impl Data block
2
3    pub fn get_address(&self) -> Address {
4        self.my_address.get()
5    }
6
7    pub fn set_address(&mut self, value: Address) {
8        self.my_address.set(value);
9    }

StorageU256

StorageU256 stores an unsigned 256-bit integer (alloy_primitives::U256).

1//... inside the #[public] impl Data block
2
3    pub fn get_uint(&self) -> U256 {
4        self.my_uint.get()
5    }
6
7    pub fn set_uint(&mut self, value: U256) {
8        self.my_uint.set(value);
9    }
1//... inside the #[public] impl Data block
2
3    pub fn get_uint(&self) -> U256 {
4        self.my_uint.get()
5    }
6
7    pub fn set_uint(&mut self, value: U256) {
8        self.my_uint.set(value);
9    }

StorageU256 is an alias of StorageUint, you can also define StorageU128 or other byte length uint.

StorageI256

StorageI256 stores a signed 256-bit integer (alloy_primitives::I256).

1//... inside the #[public] impl Data block
2
3    pub fn get_signed(&self) -> I256 {
4        self.my_signed.get()
5    }
6
7    pub fn set_signed(&mut self, value: I256) {
8        self.my_signed.set(value);
9    }
1//... inside the #[public] impl Data block
2
3    pub fn get_signed(&self) -> I256 {
4        self.my_signed.get()
5    }
6
7    pub fn set_signed(&mut self, value: I256) {
8        self.my_signed.set(value);
9    }

StorageI256 is an alias of StorageSigned, you can also define StorageI128 or other byte length int.

StorageFixedBytes

StorageFixedBytes<N> stores a fixed-size byte array of N bytes (alloy_primitives::FixedBytes<N>).

1//... inside the #[public] impl Data block
2
3    pub fn get_fixed_bytes(&self) -> FixedBytes<4> {
4        self.my_fixed_bytes.get()
5    }
6
7    pub fn set_fixed_bytes(&mut self, value: FixedBytes<4>) {
8        self.my_fixed_bytes.set(value);
9    }
1//... inside the #[public] impl Data block
2
3    pub fn get_fixed_bytes(&self) -> FixedBytes<4> {
4        self.my_fixed_bytes.get()
5    }
6
7    pub fn set_fixed_bytes(&mut self, value: FixedBytes<4>) {
8        self.my_fixed_bytes.set(value);
9    }

StorageBytes

StorageBytes stores a dynamic byte array (Vec<u8>).

1//... inside the #[public] impl Data block
2
3    pub fn get_bytes(&self) -> Vec<u8> {
4        self.my_bytes.get_bytes()
5    }
6
7    pub fn get_byte_from_bytes(&self, index: U256) -> u8 {
8        self.my_bytes.get(index).unwrap()
9    }
10
11    pub fn set_bytes(&mut self, value: Vec<u8>) {
12        self.my_bytes.set_bytes(value);
13    }
14
15    pub fn push_byte_to_bytes(&mut self, value: u8) {
16        self.my_bytes.push(value);
17    }
1//... inside the #[public] impl Data block
2
3    pub fn get_bytes(&self) -> Vec<u8> {
4        self.my_bytes.get_bytes()
5    }
6
7    pub fn get_byte_from_bytes(&self, index: U256) -> u8 {
8        self.my_bytes.get(index).unwrap()
9    }
10
11    pub fn set_bytes(&mut self, value: Vec<u8>) {
12        self.my_bytes.set_bytes(value);
13    }
14
15    pub fn push_byte_to_bytes(&mut self, value: u8) {
16        self.my_bytes.push(value);
17    }

StorageString

StorageString stores a UTF-8 encoded string (String).

1//... inside the #[public] impl Data block
2
3    pub fn get_string(&self) -> String {
4        self.my_string.get_string()
5    }
6
7    pub fn set_string(&mut self, value: String) {
8        self.my_string.set_str(value);
9    }
1//... inside the #[public] impl Data block
2
3    pub fn get_string(&self) -> String {
4        self.my_string.get_string()
5    }
6
7    pub fn set_string(&mut self, value: String) {
8        self.my_string.set_str(value);
9    }

StorageVec

StorageVec<T> stores a dynamically sized vector (array) of primitive data type T (For example, T can be alloy_primitives::U256).

1//... inside the #[public] impl Data block
2
3    pub fn get_vec(&self, index: U256) -> U256 {
4        self.my_vec.get(index).unwrap()
5    }
6
7    pub fn push_vec(&mut self, value: U256) {
8        self.my_vec.push(value);
9    }
1//... inside the #[public] impl Data block
2
3    pub fn get_vec(&self, index: U256) -> U256 {
4        self.my_vec.get(index).unwrap()
5    }
6
7    pub fn push_vec(&mut self, value: U256) {
8        self.my_vec.push(value);
9    }

Boilerplate

src/lib.rs

1#![cfg_attr(not(any(feature = "export-abi", test)), no_main)]
2extern crate alloc;
3
4use stylus_sdk::{
5    prelude::*,
6    alloy_primitives::{I256, U256, Address, FixedBytes},
7    storage::*,
8};
9
10#[storage]
11#[entrypoint]
12pub struct Data {
13    my_bool: StorageBool,
14    my_address: StorageAddress,
15    my_uint: StorageU256,
16    my_signed: StorageI256,
17    my_fixed_bytes: StorageFixedBytes<4>,
18    my_bytes: StorageBytes,
19    my_string: StorageString,
20    my_vec: StorageVec<StorageU256>,
21}
22
23#[public]
24impl Data {
25    // Getters
26    pub fn get_bool(&self) -> bool {
27        self.my_bool.get()
28    }
29
30    pub fn get_address(&self) -> Address {
31        self.my_address.get()
32    }
33
34    pub fn get_uint(&self) -> U256 {
35        self.my_uint.get()
36    }
37
38    pub fn get_signed(&self) -> I256 {
39        self.my_signed.get()
40    }
41
42    pub fn get_fixed_bytes(&self) -> FixedBytes<4> {
43        self.my_fixed_bytes.get()
44    }
45
46    pub fn get_bytes(&self) -> Vec<u8> {
47        self.my_bytes.get_bytes()
48    }
49
50    pub fn get_byte_from_bytes(&self, index: U256) -> u8 {
51        self.my_bytes.get(index).unwrap()
52    }
53
54    pub fn get_string(&self) -> String {
55        self.my_string.get_string()
56    }
57
58    pub fn get_vec(&self, index: U256) -> U256 {
59        self.my_vec.get(index).unwrap()
60    }
61
62    // Setters 
63    pub fn set_bool(&mut self, value: bool) {
64        self.my_bool.set(value);
65    }
66
67    pub fn set_address(&mut self, value: Address) {
68        self.my_address.set(value);
69    }
70
71    pub fn set_uint(&mut self, value: U256) {
72        self.my_uint.set(value);
73    }
74
75    pub fn set_signed(&mut self, value: I256) {
76        self.my_signed.set(value);
77    }
78
79    pub fn set_fixed_bytes(&mut self, value: FixedBytes<4>) {
80        self.my_fixed_bytes.set(value);
81    }
82
83    pub fn set_bytes(&mut self, value: Vec<u8>) {
84        self.my_bytes.set_bytes(value);
85    }
86
87    pub fn push_byte_to_bytes(&mut self, value: u8) {
88        self.my_bytes.push(value);
89    }
90
91    pub fn set_string(&mut self, value: String) {
92        self.my_string.set_str(value);
93    }
94
95    pub fn push_vec(&mut self, value: U256) {
96        self.my_vec.push(value);
97    }
98}
1#![cfg_attr(not(any(feature = "export-abi", test)), no_main)]
2extern crate alloc;
3
4use stylus_sdk::{
5    prelude::*,
6    alloy_primitives::{I256, U256, Address, FixedBytes},
7    storage::*,
8};
9
10#[storage]
11#[entrypoint]
12pub struct Data {
13    my_bool: StorageBool,
14    my_address: StorageAddress,
15    my_uint: StorageU256,
16    my_signed: StorageI256,
17    my_fixed_bytes: StorageFixedBytes<4>,
18    my_bytes: StorageBytes,
19    my_string: StorageString,
20    my_vec: StorageVec<StorageU256>,
21}
22
23#[public]
24impl Data {
25    // Getters
26    pub fn get_bool(&self) -> bool {
27        self.my_bool.get()
28    }
29
30    pub fn get_address(&self) -> Address {
31        self.my_address.get()
32    }
33
34    pub fn get_uint(&self) -> U256 {
35        self.my_uint.get()
36    }
37
38    pub fn get_signed(&self) -> I256 {
39        self.my_signed.get()
40    }
41
42    pub fn get_fixed_bytes(&self) -> FixedBytes<4> {
43        self.my_fixed_bytes.get()
44    }
45
46    pub fn get_bytes(&self) -> Vec<u8> {
47        self.my_bytes.get_bytes()
48    }
49
50    pub fn get_byte_from_bytes(&self, index: U256) -> u8 {
51        self.my_bytes.get(index).unwrap()
52    }
53
54    pub fn get_string(&self) -> String {
55        self.my_string.get_string()
56    }
57
58    pub fn get_vec(&self, index: U256) -> U256 {
59        self.my_vec.get(index).unwrap()
60    }
61
62    // Setters 
63    pub fn set_bool(&mut self, value: bool) {
64        self.my_bool.set(value);
65    }
66
67    pub fn set_address(&mut self, value: Address) {
68        self.my_address.set(value);
69    }
70
71    pub fn set_uint(&mut self, value: U256) {
72        self.my_uint.set(value);
73    }
74
75    pub fn set_signed(&mut self, value: I256) {
76        self.my_signed.set(value);
77    }
78
79    pub fn set_fixed_bytes(&mut self, value: FixedBytes<4>) {
80        self.my_fixed_bytes.set(value);
81    }
82
83    pub fn set_bytes(&mut self, value: Vec<u8>) {
84        self.my_bytes.set_bytes(value);
85    }
86
87    pub fn push_byte_to_bytes(&mut self, value: u8) {
88        self.my_bytes.push(value);
89    }
90
91    pub fn set_string(&mut self, value: String) {
92        self.my_string.set_str(value);
93    }
94
95    pub fn push_vec(&mut self, value: U256) {
96        self.my_vec.push(value);
97    }
98}

Cargo.toml

1[package]
2name = "stylus_storage_example"
3version = "0.1.7"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
7
8[dependencies]
9alloy-primitives = "=0.7.6"
10alloy-sol-types = "=0.7.6"
11mini-alloc = "0.4.2"
12stylus-sdk = "0.6.0"
13hex = "0.4.3"
14
15[features]
16export-abi = ["stylus-sdk/export-abi"]
17
18[lib]
19crate-type = ["lib", "cdylib"]
20
21[profile.release]
22codegen-units = 1
23strip = true
24lto = true
25panic = "abort"
26opt-level = "s"
1[package]
2name = "stylus_storage_example"
3version = "0.1.7"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
7
8[dependencies]
9alloy-primitives = "=0.7.6"
10alloy-sol-types = "=0.7.6"
11mini-alloc = "0.4.2"
12stylus-sdk = "0.6.0"
13hex = "0.4.3"
14
15[features]
16export-abi = ["stylus-sdk/export-abi"]
17
18[lib]
19crate-type = ["lib", "cdylib"]
20
21[profile.release]
22codegen-units = 1
23strip = true
24lto = true
25panic = "abort"
26opt-level = "s"