Section 01 · Definition
What is the address type in Solidity?
An address is a 20 byte identity on Ethereum. It is how the chain refers to a wallet or a contract — and Solidity gives you a first-class type for it.
Quick answer
What is address? address is a Solidity value type that holds a 160-bit Ethereum identifier. It is most often the address of a user's wallet (an externally owned account, or EOA) or the address of another smart contract. Solidity treats it as its own type so the compiler can enforce the difference between an address that is allowed to receive ether (address payable) and one that is not.
Every transaction on Ethereum has a sender address and (for non-deployment transactions) a recipient address. Inside Solidity those values appear as msg.sender and address(this). You will store addresses inside mappings to track who owns what, inside arrays to keep lists of participants, and inside structs alongside other identity fields.
Section 02 · Where it comes from
How an address is derived
An Ethereum address is not random. It is the last 20 bytes of the keccak256 hash of a public key. That fact has practical consequences for security and verification.
Because addresses are derived from public keys (for EOAs) or from a known formula involving the deployer's address and nonce (for contracts), they are not guessable in any practical sense. Two different keys produce two different addresses. Two different deployers, or the same deployer at two different nonces, produce two different contract addresses. Before pasting any address into a contract, run it through the Ethereum Address Validator to confirm shape and EIP-55 checksum.
Section 03 · address payable
The payable modifier on the type itself
address payable is a stricter sub-type. The compiler will not let you call .transfer or .send on a plain address — you have to acknowledge the cast.
address public router; // can read state, can call functions
address payable public treasury; // can also receive ether
function pay(uint256 amount) external {
treasury.transfer(amount); // ok
// router.transfer(amount); // compile error
payable(router).transfer(amount); // explicit cast — only do this if you know router is safe
}The intent behind the split is safety. Sending ether to an address that does not have a payable fallback or receive function will revert, locking funds inside your contract until you fix the call. Forcing the developer to write payable(...) makes the assumption explicit and easier to audit.
Section 04 · Built-in members
What you can do with an address value
The address type comes with a small set of built-in members that you will use constantly. Memorise these — every contract reads or sets at least one of them.
address user = msg.sender;
uint256 wei_ = user.balance; // ether (in wei) held by the address
bytes memory bc = user.code; // bytecode at the address (empty for EOAs)
bytes32 hash_ = user.codehash; // keccak256 of code
// Low level call — the modern way to send ether or call any function
(bool ok, bytes memory data) = payable(user).call{value: 1 ether}("");
require(ok, "transfer failed");The legacy .transfer(amount) and .send(amount) forward only 2 300 gas, which can fail unexpectedly with contracts that have non-trivial fallback logic. The recommended pattern in modern Solidity is the low-level call shown above, combined with a reentrancy guard modifier when the call could give control back to the caller.
EOA vs contract address
There is no compile-time way to tell whether an address holds a wallet or a contract. The runtime check is `addr.code.length > 0` — non-zero means a contract is deployed there. Be careful: during a contract's own constructor, the address has no code yet, so the check returns false and gives a misleading answer.
Section 05 · Real-world uses
Where address shows up in production code
A short tour of the patterns you will see in the first contracts you read.
// 1. Owner / admin
address public owner;
// 2. ERC20 balances and allowances
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
// 3. NFT ownership
mapping(uint256 => address) public ownerOf;
// 4. Whitelist
mapping(address => bool) public isWhitelisted;
// 5. Multisig signers
address[] public signers;
// 6. Hardcoded protocol contracts
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;Section 07 · FAQ
Frequently asked questions
What is the difference between address and address payable?
address payable is a sub-type of address. It can receive ether through .transfer, .send, or low-level .call{value:...}. Plain address cannot. The split is enforced by the compiler so accidental ether sends to a non-payable contract are caught at build time.
How do I check whether an address is a contract?
Use addr.code.length > 0. A non-zero result means there is bytecode at that address — i.e. a contract has been deployed. The check returns false for an externally owned account and false for a contract that is still inside its own constructor.
Why should I avoid tx.origin?
tx.origin is always the EOA that started the transaction, even if the call passed through several intermediate contracts. Using it for access control opens phishing-style attacks where a user is tricked into calling a malicious contract that then calls yours, and tx.origin is the user. Use msg.sender instead.
Is address(0) special?
Yes. The zero address (0x0000...0000) is the default value of any uninitialised address variable, and is conventionally used to mean 'no one' or 'burn'. ERC20 transfers to address(0) are how tokens are burnt. Always require recipients are not address(0) on user-facing functions.
Can I send ether without using address payable?
Not directly. You either store the recipient as address payable from the start, or cast at the call site with payable(recipient).transfer(...) or payable(recipient).call{value:...}(""). Modern Solidity prefers the low-level .call form for forwarding all available gas safely.