In Solidity, there are 3 types of variables: local, state, and global. Local variables are not stored on the blockchain, while state variables are (and incur a much higher cost as a result). This is true of Arbitrum Stylus Rust smart contracts as well, although how they're defined is quite different.
In Rust, local variables are just ordinary variables you assign with let
or let mut
statements. Local variables are far cheaper than state variables, even on the EVM, however, Stylus local variables are more than 100x cheaper to allocate in memory than their Solidity equivalents.
Unlike Solidity, Rust was not built inherently with the blockchain in mind. It is a general purpose programming language. We therefore define specific storage types to explicitly denote values intended to be stored permanently as part of the contract's state. State variables cost the same to store as their Solidity equivalents.
Global variables in Solidity, such as msg.sender
and block.timestamp
, are available as function calls pulled in from the stylus_sdk
with their Rust equivalents being msg::sender()
and block::timestamp()
, respectively. These variables provide information about the blockchain or the active transaction.
1#![no_main]
2#![no_std]
3extern crate alloc;
4
5#[global_allocator]
6static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
7use alloc::vec;
8use alloc::vec::Vec;
9
10use stylus_sdk::alloy_primitives::{U16, U256};
11use stylus_sdk::prelude::*;
12use stylus_sdk::storage::{StorageAddress, StorageBool, StorageU256};
13use stylus_sdk::{block, console, msg};
14
15#[solidity_storage]
16#[entrypoint]
17pub struct Contract {
18 initialized: StorageBool,
19 owner: StorageAddress,
20 max_supply: StorageU256,
21}
22
23#[external]
24impl Contract {
25 // State variables are initialized in an `init` function.
26 pub fn init(&mut self) -> Result<(), Vec<u8>> {
27 // We check if contract has been initialized before.
28 // We return if so, we initialize if not.
29 let initialized = self.initialized.get();
30 if initialized {
31 return Ok(());
32 }
33 self.initialized.set(true);
34
35 // We set the contract owner to the caller,
36 // which we get from the global msg module
37 self.owner.set(msg::sender());
38 self.max_supply.set(U256::from(10_000));
39
40 Ok(())
41 }
42
43 pub fn do_something() -> Result<(), Vec<u8>> {
44 // Local variables are not saved to the blockchain
45 // 16-bit Rust integer
46 let i = 456_u16;
47 // 16-bit int inferred from U16 Alloy primitive
48 let j = U16::from(123);
49
50 // Here are some global variables
51 let timestamp = block::timestamp();
52 let amount = msg::value();
53
54 console!("Local variables: {i}, {j}");
55 console!("Global variables: {timestamp}, {amount}");
56
57 Ok(())
58 }
59}
1#![no_main]
2#![no_std]
3extern crate alloc;
4
5#[global_allocator]
6static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
7use alloc::vec;
8use alloc::vec::Vec;
9
10use stylus_sdk::alloy_primitives::{U16, U256};
11use stylus_sdk::prelude::*;
12use stylus_sdk::storage::{StorageAddress, StorageBool, StorageU256};
13use stylus_sdk::{block, console, msg};
14
15#[solidity_storage]
16#[entrypoint]
17pub struct Contract {
18 initialized: StorageBool,
19 owner: StorageAddress,
20 max_supply: StorageU256,
21}
22
23#[external]
24impl Contract {
25 // State variables are initialized in an `init` function.
26 pub fn init(&mut self) -> Result<(), Vec<u8>> {
27 // We check if contract has been initialized before.
28 // We return if so, we initialize if not.
29 let initialized = self.initialized.get();
30 if initialized {
31 return Ok(());
32 }
33 self.initialized.set(true);
34
35 // We set the contract owner to the caller,
36 // which we get from the global msg module
37 self.owner.set(msg::sender());
38 self.max_supply.set(U256::from(10_000));
39
40 Ok(())
41 }
42
43 pub fn do_something() -> Result<(), Vec<u8>> {
44 // Local variables are not saved to the blockchain
45 // 16-bit Rust integer
46 let i = 456_u16;
47 // 16-bit int inferred from U16 Alloy primitive
48 let j = U16::from(123);
49
50 // Here are some global variables
51 let timestamp = block::timestamp();
52 let amount = msg::value();
53
54 console!("Local variables: {i}, {j}");
55 console!("Global variables: {timestamp}, {amount}");
56
57 Ok(())
58 }
59}
1[package]
2name = "variables"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7stylus-sdk = "0.4.2"
8wee_alloc = "0.4.5"
9
10[features]
11export-abi = ["stylus-sdk/export-abi"]
12
13[profile.release]
14codegen-units = 1
15strip = true
16lto = true
17panic = "abort"
18opt-level = "s"
19
20[workspace]
1[package]
2name = "variables"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7stylus-sdk = "0.4.2"
8wee_alloc = "0.4.5"
9
10[features]
11export-abi = ["stylus-sdk/export-abi"]
12
13[profile.release]
14codegen-units = 1
15strip = true
16lto = true
17panic = "abort"
18opt-level = "s"
19
20[workspace]