Solidity Cheatsheet

Variables and Visibility

Use this Solidity reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Variable Kinds

KindLocationPersists?Declared where
State variablecontract storageyes (on-chain)top level of contract
Local variablememory / stackno (per call)inside function body
Global / built-inEVM contextn/aanywhere

State Variables

contract Vault {
    // Basic declarations
    uint256 public  totalSupply;      // auto-getter
    address private owner;
    bool    internal paused;

    // Constant (compile-time, no storage slot)
    uint256 public constant MAX_SUPPLY = 1_000_000 * 1e18;

    // Immutable (set once in constructor, stored in bytecode)
    address public immutable token;

    constructor(address _token) {
        token = _token;  // only assignment allowed here
        owner = msg.sender;
    }
}

Visibility Specifiers

SpecifierExternal callsDerived contractsSame contract
publicyes (auto-getter for state vars)yesyes
externalyesno (must use this.f())no
internalnoyesyes
privatenonoyes

private does not hide data. All storage is readable on-chain via eth_getStorageAt. It only prevents Solidity-level access from other contracts.

contract Base {
    uint256 public  a = 1;  // external + internal
    uint256 internal b = 2; // internal only
    uint256 private  c = 3; // this contract only

    function getC() external view returns (uint256) { return c; }
}

contract Child is Base {
    function getB() external view returns (uint256) { return b; } // OK
    // function getC() ... { return c; }  // compile error
}

constant vs immutable

// constant — value known at compile time; no storage, no SLOAD
uint256 public constant FEE_BPS = 30;
string  public constant NAME    = "MyToken";
bytes32 public constant ROLE    = keccak256("ADMIN_ROLE");

// immutable — set in constructor; stored in code (not storage)
// cheaper reads than storage, more flexible than constant
address public immutable factory;
uint256 public immutable deployBlock;

constructor(address _factory) {
    factory     = _factory;
    deployBlock = block.number;
}
// Immutables cannot be read inside the constructor before assignment

Data Locations

Every reference-type variable (array, bytes, string, struct) must declare a location.

LocationCostUse case
storagemost expensive (SLOAD/SSTORE)state variables
memorycheap (RAM per call)local variables, return values
calldatacheapest (read-only)external function parameters
transient (0.8.24)medium (EIP-1153, cleared after tx)reentrancy locks, temp state
function process(
    uint256[] calldata ids,   // cheapest — read-only, no copy
    string memory name        // copied into memory
) external returns (uint256[] memory result) {
    result = new uint256[](ids.length);   // memory array
    uint256[] storage stored = items;     // pointer to storage
    for (uint i; i < ids.length; ++i) {
        result[i] = stored[ids[i]];
    }
}

Assigning a storage pointer to a local variable gives you a reference — mutations write through to storage.

Global Variables and Context

Block

block.chainid        // uint256 — EIP-155 chain ID
block.coinbase       // address payable — current validator
block.difficulty     // uint256 — deprecated post-merge; use block.prevrandao
block.prevrandao     // uint256 — beacon chain randomness (EIP-4399, 0.8.18+)
block.gaslimit       // uint256
block.number         // uint256 — current block number
block.timestamp      // uint256 — seconds since unix epoch (±15 s accuracy)
block.basefee        // uint256 — base fee in wei (EIP-1559, 0.8.7+)
block.blobbasefee    // uint256 — EIP-4844 blob base fee (0.8.24+)

Transaction and Message

msg.sender    // address — immediate caller (EOA or contract)
msg.value     // uint256 — wei sent with this call
msg.data      // bytes calldata — full calldata
msg.sig       // bytes4 — first 4 bytes of calldata (function selector)

tx.origin     // address — original EOA that started the transaction
tx.gasprice   // uint256 — gas price in wei

gasleft()     // uint256 — remaining gas

Do not use tx.origin for authorization — it is vulnerable to phishing via malicious contracts.

Cryptography and Math

keccak256(bytes memory input) returns (bytes32)
sha256(bytes memory input)    returns (bytes32)
ripemd160(bytes memory input) returns (bytes20)
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)

addmod(uint x, uint y, uint k) returns (uint)  // (x + y) % k, no overflow
mulmod(uint x, uint y, uint k) returns (uint)  // (x * y) % k, arbitrary precision

Address and Contract Utilities

address(this)                // this contract's address
address(this).balance        // wei balance

selfdestruct(payable(addr))  // DEPRECATED — send ether + destroy (EIP-6049)
                             // Cancun: no longer destroys storage/code

Type Information

type(uint256).min
type(uint256).max
type(int8).min      // -128
type(MyContract).creationCode   // bytes
type(MyContract).runtimeCode    // bytes
type(IMyInterface).interfaceId  // bytes4 — EIP-165

Transient Storage (0.8.24 / EIP-1153)

// Declared with `transient` keyword — cleared at end of transaction
uint256 transient private _entered;

modifier nonReentrant() {
    require(_entered == 0, "reentrant");
    _entered = 1;
    _;
    _entered = 0; // optional — auto-cleared anyway after tx
}

Shadowing and Scope Gotchas

uint256 x = 10; // state variable

function foo() public {
    uint256 x = 20; // local — shadows state var (compiler warning since 0.6)
    // Use `this` or rename to avoid confusion
}

// Variable declarations are block-scoped (0.5+)
for (uint i; i < 10; ++i) {
    uint256 val = i * 2; // val is scoped to loop body
}
// val is not accessible here