Solidity Value Types Explained in Detail

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
  • uint is an alias for uint256
  • int is an alias for int256

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 unchecked block:
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 Child inherits Parent, then Child type can be implicitly converted to Parent type
  • 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 uint8 internally)
  • 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 delete keyword 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 → 123
  • i → -123
  • minInt → -57896044618658097711785492504343953926634992332820282019728792003956564819968
  • maxInt → 57896044618658097711785492504343953926634992332820282019728792003956564819967
  • addr → 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
  • b32 → 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

(0)
Walker的头像Walker
上一篇 5 days ago
下一篇 Nov 25, 2025 02:00

Related Posts

EN
简体中文 繁體中文 English