← Back
Web3与WASM 2026.03.09

Solidity Value Types Explained in Detail

Web3与WASM

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:

UnsignedSignedBit WidthRange
uint8int88 bits0255 / -128127
uint16int1616 bits065535 / -3276832767
uint32int3232 bits0~4,294,967,295
uint256int256256 bits0~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:

OperatorDescription
!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:

TypeDescriptionSpecific Methods
addressRegular addressbalance, code, codehash, call, delegatecall, staticcall
address payablePayable addressAdds 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:

TypeDescriptionDistinguishing Method
EOA (Externally Owned Account)Controlled by a private key, can initiate transactionsaddr.code.length == 0
Contract AccountControlled by code, execution triggered by transactionsaddr.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

CharacteristicDescription
Index accessdata[0] gets the first byte, returns bytes1
length propertyReturns the number of bytes (determined at compile time, immutable)
Read-only, not writabledata[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:

TypeDefault Value
boolfalse
uint / int0
address0x0000000000000000000000000000000000000000 (zero address)
bytes1 ~ bytes32All zero bytes
enumFirst member (index 0)
stringEmpty 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.