Solidity Value Types Explained in Detail
Learning Objectives
- Master Solidity's value types such as integers, booleans, addresses, fixed-size byte arrays, and enums
- Understand the impact of EVM's 256-bit machine architecture on type design
- Master type conversion rules (implicit vs. explicit)
- Learn about the history and solutions for overflow issues
- Understand the default value mechanism for all types in Solidity
I. Integers (int / uint)
1.1 Basic Concepts
Solidity provides signed integers int and unsigned integers uint, ranging from 8 bits to 256 bits, in 8-bit increments:
| Unsigned | Signed | Bit Width | Range |
|---|---|---|---|
uint8 |
int8 |
8 bits | 0~255 / -128~127 |
uint16 |
int16 |
16 bits | 0~65535 / -32768~32767 |
uint32 |
int32 |
32 bits | 0~4,294,967,295 |
| ... | ... | ... | ... |
uint256 |
int256 |
256 bits | 0~2^256-1 |
uintis an alias foruint256intis an alias forint256
1.2 Why 256 bits?
The EVM (Ethereum Virtual Machine) is a 256-bit stack-based virtual machine, with its internal registers and stack slots both having a width of 256 bits. Therefore, uint256 / int256 are the native operational types of the EVM. Using types with other bit widths may, in some cases, consume more Gas (due to additional masking operations).
1.3 Overflow Issues
Integer overflow is a significant security vulnerability for smart contracts:
Older versions (< 0.8.0): Modulo behavior
uint8 x = 255;
x = x + 1; // Result is 0, overflow occurs but no error!
This silent overflow led to numerous security incidents (e.g., the BEC Token vulnerability in 2018).
Newer versions (>= 0.8.0): Automatic checks, overflow throws an exception
uint8 x = 255;
x = x + 1; // Transaction reverts, overflow automatically detected
SafeMath Library (Historical Solution)
Before 0.8.0, developers needed to use OpenZeppelin's SafeMath library to manually prevent overflows. After 0.8.0, the compiler has built-in overflow checks, making SafeMath no longer necessary.
If you genuinely need modulo behavior on overflow (in very rare scenarios), you can use an
uncheckedblock:solidity
unchecked { x = x + 1; }
1.4 Getting Type Bounds
Use type(T).min and type(T).max to get the minimum and maximum values of a type:
type(uint8).min // 0
type(uint8).max // 255
type(int8).min // -128
type(int8).max // 127
type(uint256).max // 2^256 - 1 (an extremely large number)
1.5 Type Conversion
Implicit Conversion (Narrow → Wide): Safe, automatic
uint8 x = 8;
uint16 y = x; // OK: uint8 → uint16, range widens, no data loss
Explicit Conversion (Forced): May lose data, requires manual declaration
uint16 y = 256;
uint8 x = uint8(y); // Forced truncation: x = 0 (256 is out of uint8 range)
1.6 Complete Example
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
contract BasicType {
function testInt() public pure returns (uint) {
uint256 max = type(uint256).max;
uint8 x = 8;
uint16 y = 9;
y = x; // Implicit conversion: y has a wider range than x
x = uint8(y); // Explicit forced type conversion
return max;
}
// Enum type
enum OrderState {
layorder,
payment
}
function enumTest() public pure returns (OrderState) {
OrderState state = OrderState.payment;
return state;
}
}
II. Boolean Type (bool)
The boolean type has only two values: true and false.
bool isActive = true;
bool isExpired = false;
Although logically only 1 bit is needed, in the EVM, boolean values actually occupy 8 bits (1 byte) of storage space. Supported operators:
| Operator | Description |
|---|---|
! |
Logical NOT |
&& |
Logical AND (short-circuit evaluation) |
\|\| |
Logical OR (short-circuit evaluation) |
== |
Equals |
!= |
Not equals |
Note: Solidity does not support converting boolean values to integers and vice versa (unlike C, where 0 is false and non-zero is true).
III. Address Type (address)
3.1 Basic Concepts
The address type is one of the most important types in Solidity, with a length of 20 bytes (160 bits):
address addr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
3.2 address vs. address payable
Solidity distinguishes between two address types:
| Type | Description | Specific Methods |
|---|---|---|
address |
Regular address | balance, code, codehash, call, delegatecall, staticcall |
address payable |
Payable address | Adds transfer and send on top of address |
address payable recipient = payable(0x123...);
recipient.transfer(1 ether); // Sends ETH, automatically reverts on failure
bool success = recipient.send(1 ether); // Sends ETH, returns false on failure
3.3 Type Conversion
Address types can be converted to and from uint160 and bytes20:
address addr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
uint160 num = uint160(addr); // address → uint160
bytes20 data = bytes20(addr); // address → bytes20
address back = address(num); // uint160 → address
3.4 Contract Address vs. EOA Address
Ethereum has two account types, and their address formats are identical:
| Type | Description | Distinguishing Method |
|---|---|---|
| EOA (Externally Owned Account) | Controlled by a private key, can initiate transactions | addr.code.length == 0 |
| Contract Account | Controlled by code, execution triggered by transactions | addr.code.length > 0 |
3.5 Common Members
addr.balance // Get the ETH balance of the address (in wei)
addr.code // Get the contract bytecode of the address
addr.call(data) // Low-level call, returns (bool, bytes)
addr.delegatecall(data) // Delegate call
addr.staticcall(data) // Static call (read-only)
3.6 Example
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
contract ComplexValueType {
function testAddress() public view returns (address) {
address addr = msg.sender; // Account address of the function caller
return addr;
}
function testMyAddress() public view returns (address) {
address addr = address(this); // Current contract address
return addr;
}
// Contract type
function testContract() public view {
ComplexValueType mycontract = this;
}
// Fixed-size byte array: read-only, not writable
function testFixedByteArray() public pure returns (bytes3) {
bytes3 data = 0x111111;
bytes1 first = data[0]; // Index access
return first;
}
}
IV. Contract Type
4.1 Basic Concepts
Each contract definition generates an independent data type. Variables of contract type essentially hold an address reference to that contract.
contract MyToken { ... }
// In another contract
MyToken token = MyToken(0x123...); // Instantiate contract reference with an address
token.transfer(to, amount); // Call contract function
4.2 Core Characteristics
- Implicit conversion to parent contract (polymorphism): If
ChildinheritsParent, thenChildtype can be implicitly converted toParenttype - Explicit conversion to address:
address(myContract)gets the contract address - Operators not supported: Contract types cannot perform arithmetic or comparison operations
- Deploy new contracts using
new:
contract Factory {
function createToken() public returns (MyToken) {
MyToken token = new MyToken(); // Deploy a new contract and return its reference
return token;
}
}
V. Fixed-size Byte Arrays (bytes1 ~ bytes32)
5.1 Basic Concepts
Solidity provides 32 fixed-size byte array types, from bytes1 to bytes32.
Important distinction: Fixed-size byte arrays (e.g.,
bytes32) arevalue types, while dynamic byte arrays (bytes) and general arrays arereference types.
5.2 Characteristics
| Characteristic | Description |
|---|---|
| Index access | data[0] gets the first byte, returns bytes1 |
length property |
Returns the number of bytes (determined at compile time, immutable) |
| Read-only, not writable | data[0] = 0xff is illegal |
5.3 Application Scenarios
Fixed-size byte arrays should be used as awhole, rather than being treated as mutable arrays:
bytes32 hash = keccak256(abi.encodePacked("hello")); // Hash value
bytes4 selector = bytes4(keccak256("transfer(address,uint256)")); // Function selector
bytes20 addrBytes = bytes20(msg.sender); // Byte representation of an address
VI. Enum Type (enum)
6.1 Basic Concepts
Enums are one of Solidity's user-defined types (the other two being contract and struct). They are used to define a set of named constants.
6.2 Rules
- Minimum of 1 member, maximum of 256 members (because they are stored as
uint8internally) - Default value is the first member (index 0)
- Conversion to and from integers must use explicit conversion
enum Color { Red, Green, Blue }
uint8 idx = uint8(Color.Green); // 1
Color c = Color(0); // Color.Red
6.3 Complete Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
contract Enum {
enum Status {
None, Pending, Shipped, Completed, Rejected, Canceled
}
Status public status;
struct Order {
address buyer;
Status status;
}
Order public orders;
function get() external view returns (Status) {
return status;
}
function set(Status _status) external {
status = _status;
}
function ship() external {
status = Status.Shipped;
}
function reset() external {
delete status; // Resets to the default value (the first defined value)
}
}
VII. Default Values
In Solidity, there is no concept of undefined or null. All variables, if not assigned a value upon declaration, are automatically initialized to their type's default value:
| Type | Default Value |
|---|---|
bool |
false |
uint / int |
0 |
address |
0x0000000000000000000000000000000000000000 (zero address) |
bytes1 ~ bytes32 |
All zero bytes |
enum |
First member (index 0) |
string |
Empty string "" |
bytes (dynamic) |
Empty byte array |
Using the
deletekeyword can reset a variable to its default value:solidity
uint x = 42;
delete x; // x reverts to 0
VIII. Comprehensive Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract ValueTypes {
bool b = true;
uint public u = 123;
int public i = -123;
int public minInt = type(int).min;
int public maxInt = type(int).max;
address public addr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
bytes32 public b32 = keccak256(abi.encodePacked("This is a test value"));
}
After deploying this contract, you can directly click on each public variable name in Remix to view their values:
u→ 123i→ -123minInt→ -57896044618658097711785492504343953926634992332820282019728792003956564819968maxInt→ 57896044618658097711785492504343953926634992332820282019728792003956564819967addr→ 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4b32→ A 32-byte keccak256 hash value
Summary
In this section, we thoroughly learned about Solidity's value type system. The core characteristic of value types is that data is fully copied (rather than referenced) when assigned or passed as arguments. Mastering these basic types is a prerequisite for writing secure and efficient smart contracts. In the next section, we will learn about reference types (arrays, structs, mappings) and data storage locations (storage / memory / calldata).
This article is the 4th of 12 in the Web3 Blockchain series.
主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://walker-learn.xyz/archives/7488