Solidity Cheatsheet
Functions
Use this Solidity reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Function Declaration
function <name>(<params>) <visibility> <mutability> <modifiers> returns (<types>) { // body }
// Full example function transfer(address to, uint256 amount) external nonReentrant whenNotPaused returns (bool success) { balances[msg.sender] -= amount; balances[to] += amount; return true; }
Visibility
| Keyword | Who can call |
|---|---|
external | outside calls only (cheapest for external callers — args stay in calldata) |
public | external + internal |
internal | this contract + derived contracts |
private | this contract only |
State Mutability
| Keyword | Reads storage | Writes storage | Receives ether |
|---|---|---|---|
| (none) | yes | yes | no |
view | yes | no | no |
pure | no | no | no |
payable | yes | yes | yes |
function getBalance() external view returns (uint256) { return balances[msg.sender]; } function add(uint a, uint b) external pure returns (uint) { return a + b; } function deposit() external payable { balances[msg.sender] += msg.value; }
Parameters and Return Values
// Named returns — can use implicit return function divmod(uint a, uint b) pure returns (uint quotient, uint remainder) { quotient = a / b; remainder = a % b; // implicit return (no return statement needed) } // Tuple destructuring (uint q, uint r) = divmod(10, 3); // Multiple return values function minMax(uint[] calldata arr) external pure returns (uint min, uint max) { min = type(uint).max; for (uint i; i < arr.length; ++i) { if (arr[i] < min) min = arr[i]; if (arr[i] > max) max = arr[i]; } } // Calldata vs memory for arrays function processCalldata(uint[] calldata data) external pure returns (uint) { } function processMemory(uint[] memory data) public pure returns (uint) { }
Prefer
calldataovermemoryfor external function array/struct parameters — it avoids the copy and is cheaper.
Function Overloading
function encode(uint256 x) internal pure returns (bytes memory) { } function encode(address x) internal pure returns (bytes memory) { } function encode(bytes32 x) internal pure returns (bytes memory) { } // Called based on argument type — no ambiguity allowed at compile time
virtual and override
contract Base { function greet() public virtual returns (string memory) { return "hello from Base"; } } contract Child is Base { function greet() public virtual override returns (string memory) { return "hello from Child"; } } // Multiple inheritance — must list all parents contract Multi is A, B { function greet() public override(A, B) returns (string memory) { return super.greet(); // calls most-derived parent (C3 linearization) } }
Constructors
contract Token { string public name; address public owner; constructor(string memory _name) { name = _name; owner = msg.sender; } } // With inheritance contract MyToken is Ownable, ERC20 { constructor(address initialOwner) Ownable(initialOwner) ERC20("MyToken", "MTK") { _mint(initialOwner, 1_000_000e18); } } // No constructor = default (zero-arg, internal for abstract)
Fallback and Receive
contract Wallet { // Called when msg.data is empty AND ether is sent receive() external payable { emit Received(msg.sender, msg.value); } // Called when no function matches (or receive() is absent) fallback() external payable { // Can inspect msg.data } // fallback without payable — reverts if ether is included fallback() external { } }
Call routing order:
- If
msg.datais empty and ether is sent →receive()(if defined), elsefallback() - If
msg.datais non-empty → match function selector, elsefallback()
Internal Function Calls
contract Calculator { function square(uint x) public pure returns (uint) { return _mul(x, x); // internal call — no gas overhead of external call } function _mul(uint a, uint b) internal pure returns (uint) { return a * b; } }
External Calls
// Typed interface call (preferred) IERC20 token = IERC20(tokenAddress); token.transfer(to, amount); // reverts on failure // Low-level call (returns success flag) (bool ok, bytes memory data) = addr.call{value: 1 ether, gas: 5000}( abi.encodeWithSelector(IERC20.transfer.selector, to, amount) ); require(ok, "call failed"); // delegatecall — runs callee code in caller's context (proxy pattern) (bool ok2, ) = implementation.delegatecall(msg.data); // staticcall — read-only external call (like view) (bool ok3, bytes memory res) = addr.staticcall(abi.encodeWithSignature("get()"));
Always check the return value of low-level calls.
calldoes not revert on failure.
Function Selectors
// 4-byte selector = keccak256("functionName(type1,type2,...)")[:4] bytes4 sel = IERC20.transfer.selector; // equivalent to: bytes4 sel2 = bytes4(keccak256("transfer(address,uint256)")); // Dispatching manually function dispatch(bytes calldata data) external { bytes4 sig = bytes4(data[:4]); if (sig == IERC20.transfer.selector) { // ... } }
Free Functions
Defined outside any contract — share no state, always internal.
// MyLib.sol function hashPair(bytes32 a, bytes32 b) pure returns (bytes32) { return a < b ? keccak256(abi.encode(a, b)) : keccak256(abi.encode(b, a)); } contract MerkleTree { function verify(bytes32 a, bytes32 b) external pure returns (bytes32) { return hashPair(a, b); // can call free functions directly } }
using ... for (Attached Functions)
library ArrayUtils { function contains(uint[] storage arr, uint val) internal view returns (bool) { for (uint i; i < arr.length; ++i) if (arr[i] == val) return true; return false; } } contract MyContract { using ArrayUtils for uint[]; uint[] private items; function has(uint val) external view returns (bool) { return items.contains(val); // method-call syntax } } // Global attachment (0.8.13+) using ArrayUtils for uint[] global;
Gas Optimization Patterns
// Cache storage reads in memory function sumItems() external view returns (uint256 total) { uint256 len = items.length; // 1 SLOAD for (uint i; i < len; ++i) total += items[i]; } // Use ++i (saves ~5 gas vs i++) for (uint i; i < 10; ++i) { } // Short-circuit: cheap checks first function check(address a, uint v) external view returns (bool) { return v > 0 && balances[a] >= v; // skip SLOAD if v == 0 }