The Stylus SDK makes use of the popular Alloy library (from the developers of ethers-rs and Foundry) to represent various native Solidity types as Rust types and to seamlessly convert between them when needed. These are needed since there are a number of custom types (like address) and large integers that are not natively supported in Rust.
In this section, we'll focus on the following types:
U256
I256
Address
bool
More in-depth documentation about the available methods and types in the Alloy library can be found in their docs. It also helps to cross-reference with Solidity docs if you don't already have a solid understanding of those types.
Alloy defines a set of convenient Rust types to represent the typically sized integers used in Solidity. The type U256
represents a 256-bit unsigned integer, meaning it cannot be negative. The range for a U256
number is 0 to 2^256 - 1.
Negative numbers are allowed for I types, such as I256
. These represent signed integers.
U256
maps to uint256
... I256
maps to int256
U128
maps to uint128
... I128
maps to int128
U8
maps to uint8
... I8
maps to int8
1// Unsigned
2let eight_bit: U8 = U8::from(1);
3let two_fifty_six_bit: U256 = U256::from(0xff_u64);
4
5// Out: Stylus says: '8-bit: 1 | 256-bit: 255'
6console!("8-bit: {} | 256-bit: {}", eight_bit, two_fifty_six_bit);
7
8// Signed
9let eight_bit: I8 = I8::unchecked_from(-1);
10let two_fifty_six_bit: I256 = I256::unchecked_from(0xff_u64);
11
12// Out: Stylus says: '8-bit: -1 | 256-bit: 255'
13console!("8-bit: {} | 256-bit: {}", eight_bit, two_fifty_six_bit);
1// Unsigned
2let eight_bit: U8 = U8::from(1);
3let two_fifty_six_bit: U256 = U256::from(0xff_u64);
4
5// Out: Stylus says: '8-bit: 1 | 256-bit: 255'
6console!("8-bit: {} | 256-bit: {}", eight_bit, two_fifty_six_bit);
7
8// Signed
9let eight_bit: I8 = I8::unchecked_from(-1);
10let two_fifty_six_bit: I256 = I256::unchecked_from(0xff_u64);
11
12// Out: Stylus says: '8-bit: -1 | 256-bit: 255'
13console!("8-bit: {} | 256-bit: {}", eight_bit, two_fifty_six_bit);
1// Use `try_from` if you're not sure it'll fit
2let a = I256::try_from(20003000).unwrap();
3// Or parse from a string
4let b = "100".parse::<I256>().unwrap();
5// With hex characters
6let c = "-0x138f".parse::<I256>().unwrap();
7// Underscores are ignored
8let d = "1_000_000".parse::<I256>().unwrap();
9
10// Math works great
11let e = a * b + c - d;
12// Out: Stylus says: '20003000 * 100 + -5007 - 1000000 = 1999294993'
13console!("{} * {} + {} - {} = {}", a, b, c, d, e);
14
15// Useful constants
16let f = I256::MAX;
17let g = I256::MIN;
18let h = I256::ZERO;
19let i = I256::MINUS_ONE;
20
21// Stylus says: '5789...9967, -5789...9968, 0, -1'
22console!("{f}, {g}, {h}, {i}");
23// As hex: Stylus says: '0x7fff...ffff, 0x8000...0000, 0x0, 0xffff...ffff'
24console!("{:#x}, {:#x}, {:#x}, {:#x}", f, g, h, i);
1// Use `try_from` if you're not sure it'll fit
2let a = I256::try_from(20003000).unwrap();
3// Or parse from a string
4let b = "100".parse::<I256>().unwrap();
5// With hex characters
6let c = "-0x138f".parse::<I256>().unwrap();
7// Underscores are ignored
8let d = "1_000_000".parse::<I256>().unwrap();
9
10// Math works great
11let e = a * b + c - d;
12// Out: Stylus says: '20003000 * 100 + -5007 - 1000000 = 1999294993'
13console!("{} * {} + {} - {} = {}", a, b, c, d, e);
14
15// Useful constants
16let f = I256::MAX;
17let g = I256::MIN;
18let h = I256::ZERO;
19let i = I256::MINUS_ONE;
20
21// Stylus says: '5789...9967, -5789...9968, 0, -1'
22console!("{f}, {g}, {h}, {i}");
23// As hex: Stylus says: '0x7fff...ffff, 0x8000...0000, 0x0, 0xffff...ffff'
24console!("{:#x}, {:#x}, {:#x}, {:#x}", f, g, h, i);
Ethereum addresses are 20 bytes in length, or 160 bits. Alloy provides a number of helper utilities for converting to addresses from strings, bytes, numbers, and addresses.
1// From a 20 byte slice, all 1s
2let addr1 = Address::from([0x11; 20]);
3// Out: Stylus says: '0x1111111111111111111111111111111111111111'
4console!("{addr1}");
5
6// Use the address! macro to parse a string as a checksummed address
7let addr2 = address!("d8da6bf26964af9d7eed9e03e53415d37aa96045");
8// Out: Stylus says: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
9console!("{addr2}");
10
11// Format compressed addresses for output
12// Out: Stylus says: '0xd8dA…6045'
13console!("{addr2:#}");
1// From a 20 byte slice, all 1s
2let addr1 = Address::from([0x11; 20]);
3// Out: Stylus says: '0x1111111111111111111111111111111111111111'
4console!("{addr1}");
5
6// Use the address! macro to parse a string as a checksummed address
7let addr2 = address!("d8da6bf26964af9d7eed9e03e53415d37aa96045");
8// Out: Stylus says: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
9console!("{addr2}");
10
11// Format compressed addresses for output
12// Out: Stylus says: '0xd8dA…6045'
13console!("{addr2:#}");
Use native Rust primitives where it makes sense and where no equivalent Alloy primitive exists.
1let frightened: bool = true;
2// Out: Stylus says: 'Boo! Did I scare you?'
3console!("Boo! Did I scare you?");
4
5let response = match frightened {
6 true => "Yes!".to_string(),
7 false => "No!".to_string(),
8};
9
10// Out: Stylus says: 'Yes!'
11console!("{response}");
1let frightened: bool = true;
2// Out: Stylus says: 'Boo! Did I scare you?'
3console!("Boo! Did I scare you?");
4
5let response = match frightened {
6 true => "Yes!".to_string(),
7 false => "No!".to_string(),
8};
9
10// Out: Stylus says: 'Yes!'
11console!("{response}");
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::Vec;
8
9// Dependencies for these examples
10use stylus_sdk::{
11 alloy_primitives::{address, Address, I256, I8, U256, U8},
12 console,
13 stylus_proc::entrypoint,
14 ArbResult,
15};
16
17#[entrypoint]
18fn user_main(_input: Vec<u8>) -> ArbResult {
19 // Test out the above examples here
20
21 Ok(Vec::new())
22}
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::Vec;
8
9// Dependencies for these examples
10use stylus_sdk::{
11 alloy_primitives::{address, Address, I256, I8, U256, U8},
12 console,
13 stylus_proc::entrypoint,
14 ArbResult,
15};
16
17#[entrypoint]
18fn user_main(_input: Vec<u8>) -> ArbResult {
19 // Test out the above examples here
20
21 Ok(Vec::new())
22}
1[package]
2name = "primitive_data_types"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7# NOTE: Do not deploy a contract to prod with `debug` feature flag set!
8stylus-sdk = { version = "0.4.2", features = ["debug"] }
9wee_alloc = "0.4.5"
10
11[features]
12export-abi = ["stylus-sdk/export-abi"]
13
14[profile.release]
15codegen-units = 1
16strip = true
17lto = true
18panic = "abort"
19opt-level = "s"
20
21[workspace]
1[package]
2name = "primitive_data_types"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7# NOTE: Do not deploy a contract to prod with `debug` feature flag set!
8stylus-sdk = { version = "0.4.2", features = ["debug"] }
9wee_alloc = "0.4.5"
10
11[features]
12export-abi = ["stylus-sdk/export-abi"]
13
14[profile.release]
15codegen-units = 1
16strip = true
17lto = true
18panic = "abort"
19opt-level = "s"
20
21[workspace]