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.
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
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
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
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
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<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
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
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<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 }
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}
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"