5 Solidity data types every non-developer should recognize

5 Solidity data types every non-developer should recognize

Every value in a smart contract has a type. Pick the wrong one and you waste gas, lose precision, or open a security hole. Here is what each type does.

February 16th, 2026 · Build

If you are building a product on a blockchain, every piece of data your contract stores, reads, or passes between functions has a type. Token balances, wallet addresses, on/off flags, user names: each one needs to be declared with the right type before the compiler will accept it. Pick the wrong one and you waste gas, lose numerical precision, or introduce a vulnerability that an attacker can exploit.

What are data types, in plain English

Think of a spreadsheet. Each column has a format: one column holds whole numbers, another holds text, another holds true/false checkboxes. You would not paste a sentence into a number column or try to do math on a checkbox. Solidity works the same way, except the compiler enforces these rules at build time rather than letting you discover problems at runtime.

Every variable you create in a smart contract must declare its type up front. A variable typed as uint256 can hold a positive whole number up to 2^256 minus 1 (a number with 78 digits). A variable typed as address holds exactly 20 bytes, enough for an Ethereum wallet or contract address. A variable typed as bool holds exactly two possible values: true or false.

There is no “auto-detect” and no loose typing. The compiler needs to know the exact size and shape of every value so it can allocate storage on the blockchain.

This strictness is intentional. Storage on Ethereum costs real money (gas), and the EVM operates on fixed 256-bit (32-byte) words. Every type maps directly to how data is packed into those words, which means your choice of type affects both correctness and cost.

Why this matters for your project

Gas costs are tied to how data is stored. The EVM processes everything in 32-byte chunks. A single uint256 fits perfectly into one storage slot. A uint8 also occupies a full 32-byte slot when stored alone, because the EVM pads it to fill the word. That means a lone uint8 costs exactly the same gas as a uint256.

The savings only appear when you pack multiple smaller types into the same slot, for example, grouping several uint8 fields together in a struct or as consecutive state variables. When packed correctly, you can fit up to 32 uint8 values into a single slot, cutting storage costs dramatically.

Security depends on correct types. Before Solidity 0.8, arithmetic on unsigned integers could silently overflow. Adding 1 to the maximum uint256 value would wrap around to zero instead of throwing an error. This class of bug was responsible for real exploits.

Solidity 0.8 and later versions add built-in overflow checks that revert the transaction automatically, but you still need to choose the right type for the job. Using uint8 for a token balance that could exceed 255 will cause reverts even with overflow protection.

Address types carry specific meaning. A plain address can receive calls but not ETH. If your contract needs to send ETH to an address, it must be declared as address payable. Since Solidity 0.8, msg.sender returns a plain address by default, so sending ETH to the caller requires an explicit cast: payable(msg.sender). Missing this distinction is one of the most common beginner errors.

How it works (the 30-second version)

Solidity divides types into two categories. Value types are copied when assigned or passed to functions: integers, booleans, addresses, and fixed-size byte arrays. Reference types point to data stored elsewhere and require a location annotation (storage, memory, or calldata): strings, dynamic byte arrays, arrays, mappings, and structs.

Here are the types you will use most often:

TypeWhat it storesExampleCommon use
uint256Positive whole number (0 to 2^256 minus 1)1000000Token balances, counters
address20-byte Ethereum address0xAb5...Wallet addresses, contract references
boolTrue or falsetrueFlags, toggles
stringVariable-length text"Hello"Names, metadata
int256Positive or negative whole number-42Price deltas, signed math
bytes32Fixed 32-byte raw data0xff...Hashes, identifiers
uint8Small number (0 to 255)18Decimals, small enums

In Solidity code, declaring variables looks like this:

uint256 public totalSupply = 1000000;
address public owner = msg.sender;
bool public paused = false;
string public name = "MyToken";

Each line declares the type, visibility, name, and an optional default value. The compiler rejects any assignment that does not match the declared type.

Arrays extend any type by appending []. A uint256[] is a dynamic list of numbers. An address[] is a list of wallet addresses. You can push, pop, and access elements by index.

Mappings create key-value lookups. A mapping(address => uint256) maps each wallet address to a number, which is exactly how ERC-20 token balances work. Mappings cannot be iterated or enumerated. They only support direct key lookups.

How Doodledapp makes this visual

In Doodledapp, you do not type uint256 or address into a text editor. You select from a dropdown that uses plain-language labels. When you pick “Number,” the generated Solidity outputs uint256. When you pick “Wallet Address,” it outputs address.

The mapping is consistent across every node that uses a type selector: State Variable, Create Variable, Literal Value, Mapping, Function parameters, and Struct member definitions.

Solidity typeDoodledapp label
uint256Number
addressWallet Address
boolTrue / False
stringText
uint128Number (medium)
uint8Number (0-255)
int256Number (+/-)
bytesRaw Data
bytes32Fixed Data (32B)

Arrays are supported by appending ”[]” to any type. A uint256[] appears as “List of Numbers” in the dropdown.

Here is how common Solidity declarations map to Doodledapp nodes:

Solidity conceptDoodledapp equivalent
uint256 public balance;State Variable node with Type “Number” and name “balance”
address owner;State Variable node with Type “Wallet Address”
mapping(address => uint256)Mapping node with Key Type “Wallet Address”, Value Type “Number”
uint256[] public scores;State Variable node with Type “List of Numbers”
bool paused;State Variable node with Type “True / False”

The type dropdown appears on every node that needs one, so you make the decision once per variable and the correct Solidity is generated automatically. Non-developers never need to memorize uint256 or bytes32. They see “Number” and “Fixed Data (32B)” and the compiler gets the right type every time.

Common mistakes to avoid

Using uint8 to save gas when the variable is stored alone. A single uint8 state variable costs exactly the same gas as a uint256 because the EVM pads it to fill a 32-byte storage slot. Worse, operations on uint8 require extra gas for the conversion to uint256 that the EVM performs internally. Only use smaller integer types when you can pack multiple values into the same slot, such as consecutive state variables or struct fields.

Choosing a type too small for the data. If you declare a variable as uint8 and the value ever exceeds 255, the transaction will revert. This is easy to overlook with counters that grow over time. When in doubt, use uint256. Storage cost is the same for a standalone variable, and you avoid unexpected reverts.

Forgetting the difference between address and address payable. If your function needs to send ETH to an address, that address must be address payable. This is a common beginner mistake. Calling .transfer() or .send() on a plain address will not compile. Since Solidity 0.8, msg.sender is a plain address, so you need payable(msg.sender) to send ETH back to the caller.

Storing large text or binary data on-chain as string or bytes. Every byte of on-chain storage costs gas. A 1 KB string costs over 640,000 gas to store for the first time (roughly 20,000 gas per 32-byte slot, times 32 slots). Store large data off-chain (IPFS, a database) and keep only the hash or URI on-chain.

The bottom line

Data types are the foundation of every smart contract. They determine how much gas your users pay, whether your math is safe from overflow, and whether your contract can send ETH where it needs to go. Choosing the right type for each variable is a small decision that compounds across every function, every transaction, and every user interaction your contract will ever handle.

Spot an inaccuracy or a bug?