
Solidity
- 3 installs
- 4 repo stars
- Updated February 25, 2026
- hairyf/blockchain-skills
Write EVM smart contracts in Solidity: source layout, types, contract structure, control flow, security patterns, and the solc compiler.
About
Reference for the Solidity language and compiler covering source layout, types, contract structure, control flow, security patterns, and ABI/internals. A developer uses it when writing or reviewing Solidity smart contracts for EVM chains.
- Covers contracts, inheritance, libraries, events, custom errors, and inline assembly
- Includes security patterns, solc CLI, ABI/metadata, and SMTChecker verification
Solidity by the numbers
- 3 all-time installs (skills.sh)
- Ranked #390 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Jul 13, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hairyf/blockchain-skills --skill solidityAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 4 |
| Last updated | February 25, 2026 |
| Repository | hairyf/blockchain-skills ↗ |
What it does
Write EVM smart contracts in Solidity: source layout, types, contract structure, control flow, security patterns, and the solc compiler.
Files
Skill based on Solidity (ethereum/solidity) docs, generated at 2026-02-09.
Solidity is a statically typed, object-oriented language for EVM smart contracts. This skill covers source layout, types, contract structure, control flow, security patterns, compiler usage, and ABI/internals.
Core References
| Topic | Description | Reference |
|---|---|---|
| Source Layout | SPDX, pragma, import, comments | core-layout |
| Contract Structure | State, functions, modifiers, events, errors, structs, enums | core-structure |
| Types | Value/reference/mapping types, operators, conversions | core-types |
| Control Structures | if/loop, internal/external calls, revert, try/catch | core-control |
| Units and Globals | Ether/time units, block/msg/tx, ABI/hash helpers | core-units-globals |
Features
Contracts
| Topic | Description | Reference |
|---|---|---|
| Contracts | Creation, visibility, modifiers, functions, events, errors, inheritance, interfaces, libraries, using-for | features-contracts |
| Inline Assembly | Yul in Solidity, access to variables, safety | features-assembly |
| Yul | Intermediate language, EVM opcodes, objects | features-yul |
| NatSpec | Tags, userdoc/devdoc output, @inheritdoc, @custom | features-natspec |
| Events | Indexed, anonymous, topics, selector, emit | features-events |
| Custom Errors | revert/require, selector, try/catch, ABI | features-errors |
| Libraries | DELEGATECALL, internal vs external, linking | features-libraries |
| Inheritance | virtual/override, super, C3, base constructors | features-inheritance |
| Interfaces | Restrictions, enum/struct, ABI alignment | features-interfaces |
| Transient Storage | EIP-1153, transaction-scoped, reentrancy locks | features-transient-storage |
| Visibility and Getters | external/public/internal/private, getter generation | features-visibility-getters |
Best Practices
| Topic | Description | Reference |
|---|---|---|
| Security | Reentrancy, gas, visibility, randomness, front-running | best-practices-security |
| Common Patterns | Withdrawal, access control, checks-effects-interactions, proxies | best-practices-patterns |
| Style and Layout | File/contract order, modifier order, naming | best-practices-style |
Advanced
| Topic | Description | Reference |
|---|---|---|
| Compiler | solc CLI, Standard JSON, optimizer, libraries, path resolution | advanced-compiler |
| Internals | Storage/memory/calldata layout, optimizer, source mappings | advanced-internals |
| ABI and Metadata | ABI spec, contract metadata, NatSpec | advanced-abi-metadata |
| SMTChecker | Formal verification, engines, targets, options | advanced-smtchecker |
| Path Resolution | VFS, base/include paths, remapping, allowed paths | advanced-path-resolution |
| Compilation Output | Bytecode, --asm, optimized vs non-optimized | advanced-compilation-output |
Generation Info
- Source:
sources/solidity - Git SHA:
79941184298882ff883e749ee8a55781f14c4a0d - Generated: 2026-02-09
ABI and Metadata
ABI (Application Binary Interface)
JSON description of contract interface: function/event/error signatures, types, inputs/outputs. Used by clients to encode calls and decode logs/return data.
- Types: uint/int, address, bool, bytes, string, arrays, tuples, fixed-size bytes.
- Function: name, type "function", inputs, outputs, stateMutability (pure/view/nonpayable/payable).
- Event: name, type "event", inputs (indexed flag).
- Error: name, type "error", inputs.
Encoding: 4-byte selector (keccak256 of signature, first 4 bytes) then ABI-encoded arguments. Events: topic0 = keccak256(signature), indexed args as further topics, non-indexed as data.
Contract Metadata
Compiler emits metadata JSON (CBOR-encoded in contract bytecode or appended) with compiler version, source info, and ABI. Used by verification and tooling. Contains IPFS/Swarm hashes when available for source retrieval. Do not strip metadata if you want on-chain verification.
NatSpec
Structured comments (/// or /** ... */) for dev/user docs and custom errors. Tags: @title, @author, @param, @return, @dev, @notice, etc. Compiler can output user-facing and dev docs from NatSpec.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/abi-spec.html
- https://docs.soliditylang.org/en/latest/metadata.html
- https://docs.soliditylang.org/en/latest/natspec-format.html
-->
Analysing Compilation Output
Use compiler output to inspect generated bytecode and assembly. Helpful for debugging, gas tuning, and comparing codegen before/after changes.
Bytecode and assembly
- `solc --bin contract.sol`: Raw bytecode (hex). Hard to read; use for hashes or deployment.
- `solc --asm contract.sol`: Human-readable EVM assembly. Comments reference source locations (e.g.
"contract.sol":17:84). Use--asmto understand what the compiler generated.
The assembly output starts with creation/constructor code. The main contract runtime is in a sub-object (e.g. sub_0). The auxdata field is the metadata hash (see ABI/metadata docs).
Optimized vs non-optimized
- `solc --optimize --asm contract.sol`: Optimized assembly. Use to compare gas or to check if two Solidity variants produce the same optimized code (e.g. diff after stripping source comments).
- Optimizer runs only when
--optimize(or equivalent in Standard JSON) is set; optimizer settings (runs, etc.) affect output.
Combined JSON and generated sources
- `solc --combined-json generated-sources,generated-sources-runtime contract.sol`: Includes internal generated files (e.g.
#utility.yul) used in the assembly. Useful to map assembly helpers back to generated Yul.
Notes
--asmoutput is not guaranteed machine-readable and may change between minor compiler versions.- For reproducible builds, rely on Standard JSON and documented output selection fields rather than parsing
--asmprogrammatically.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/analysing-compilation-output.html
-->
Using the Compiler
Command Line (solc)
- Single file:
solc --bin sourceFile.sol - Multiple outputs:
solc -o outputDir --bin --ast-compact-json --asm sourceFile.sol - Optimizer:
solc --optimize --bin sourceFile.sol. Tune with--optimize-runs N(default 200; lower = cheaper deployment, higher runtime cost).
Base Path and Import Remapping
prefix=pathmaps import prefix to directory:solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ file.sol- Paths not starting with
./or../are relative to--base-pathand--include-path. Only specified paths (and remapping targets) are allowed unless--allow-pathsis set.
Library Linking
Bytecode contains placeholders for library addresses (hex of keccak256 of fully qualified name). Link at compile time: solc --libraries "file.sol:Math=0x..." sourceFile.sol or --libraries libs.txt. Prefer linking at compile time so metadata stays correct; avoid manual bytecode patching.
Standard JSON
For tooling and reproducibility: solc --standard-json reads JSON from stdin and writes JSON to stdout. Input includes language, sources (source unit name -> content), settings (optimizer, evmVersion, viaIR, etc.). Output includes contracts, errors, etc. Always exits 0; errors in output.
solcjs
Node.js build; different CLI. Use Standard JSON for cross-environment builds.
EVM Version and Via-IR
Set target EVM (e.g. paris, shanghai) and optionally enable IR-based codegen (viaIR) for different optimization behavior.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/using-the-compiler.html
- https://docs.soliditylang.org/en/latest/path-resolution.html
-->
Internals
Storage Layout
- State variables are laid out in storage in declaration order.
- Packing: up to 32 bytes in one slot when possible (e.g. multiple small values). First item in a slot is lower-order bytes.
- Dynamic arrays: slot stores length; data at
keccak256(slot). - Mappings: slot unused; value at
keccak256(hash(key), slot). - Nested structures and dynamic types follow documented rules. Use when writing assembly or debugging storage.
Memory Layout
- Contiguous; 32-byte aligned. Free memory pointer at 0x40.
- Arrays: length word then elements. Structs: consecutive 32-byte fields.
- Memory is cleared between external calls (not between internal calls). Use for temporary data and ABI encoding.
Calldata Layout
- Read-only; used for external function parameters. Same ABI encoding as memory for decoding; no allocation. Prefer
calldatafor external read-only parameters to save gas.
Variable Cleanup
Before use, the compiler may clear padding bits (e.g. for type safety). Relevant for assembly or when comparing raw storage/memory.
Optimizer
EVM optimizer runs on opcodes (or Yul/IR if via-IR). Optimizer runs setting affects deployment vs runtime cost. See “Using the Compiler” for --optimize-runs.
Source Mappings
Compiler output can include source mapping (e.g. for debugging). Format maps bytecode range to source file/line/column for stack trace and debugging tools.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html
- https://docs.soliditylang.org/en/latest/internals/layout_in_memory.html
- https://docs.soliditylang.org/en/latest/internals/layout_in_calldata.html
- https://docs.soliditylang.org/en/latest/internals/variable_cleanup.html
- https://docs.soliditylang.org/en/latest/internals/optimizer.html
- https://docs.soliditylang.org/en/latest/internals/source_mappings.html
-->
Import Path Resolution
The compiler uses a virtual filesystem (VFS): each source has a unique source unit name. Import paths in code are resolved to source unit names; the Host Filesystem Loader (or custom import callback) then loads content.
Direct vs relative imports
- Direct: path does not start with
./or../. After remapping, the path becomes the source unit name. Example:import "lib/util.sol";→lib/util.sol. - Relative: starts with
./or../. Resolved relative to the importing file’s source unit name. Example: fromcontracts/contract.sol,import "./math.sol";→contracts/math.sol.
Use forward slashes for portability. Relative imports with leading .. are not recommended; prefer direct imports with base/include paths.
Base path and include paths
- Base path (
--base-path): prepended to source unit names by the Host Filesystem Loader. Set to project root so relative lookups are stable. - Include paths (
--include-path): additional directories to search; require non-empty base path. Use for dependencies (e.g.node_modules/).
Example:
solc contract.sol --base-path . --include-path node_modules/Then import "@openzeppelin/contracts/utils/Strings.sol"; is resolved under base path or include paths.
Import remapping
Remapping changes the translation from import path to source unit name: context:prefix=target. Omit context for global remapping.
- prefix must match the start of the resolved source unit name; target replaces it.
- Only one remapping applies per import (longest context, then longest prefix).
- Remapping does not apply to paths given on the command line or in Standard JSON
sourceskeys. - Remapping info is stored in metadata; avoid local paths in targets for reproducible builds. Prefer include paths when possible.
Example:
solc github.com/ethereum/dapp-bin/=dapp-bin/ --base-path /project source.solAllowed paths
Host Filesystem Loader only loads from certain directories (input file dirs, remapping targets, base path, include paths). Add more with --allow-paths (comma-separated). Required when sources live outside those locations. Case-sensitive; symlinks are not followed beyond allowed dirs.
Standard JSON
With Standard JSON, sources keys are the initial source unit names; content is in content or loaded via urls and an import callback. No filesystem lookup unless callback is used.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/path-resolution.html
-->
SMTChecker and Formal Verification
The SMTChecker performs automated proofs that code satisfies specifications given by require (assumptions) and assert (properties to prove). It also checks: arithmetic underflow/overflow (opt-in for >=0.8.7), division by zero, trivial/unreachable code, pop empty array, out-of-bounds access, insufficient balance for transfer.
Enabling
Select an engine via CLI or Standard JSON. Default is no engine.
- CLI:
--model-checker-engine {all,bmc,chc,none} - JSON:
settings.modelChecker.engine:"all"|"bmc"|"chc"|"none"
Engines: BMC (bounded, per-function, no multi-transaction) and CHC (Horn clauses, full contract lifecycle, supports loops). Both can run; CHC runs first, unproved properties go to BMC.
Verification targets
- CLI:
--model-checker-targets "assert,underflow,overflow,divByZero,constantCondition,popEmptyArray,outOfBounds,balance" - JSON:
settings.modelChecker.targets: array of those strings. Use"default"(CLI only) for all.
For Solidity >=0.8.7, underflow/overflow are not checked by default; add "underflow" and "overflow" explicitly if needed.
Common options
- Timeout:
--model-checker-timeout <ms>orsettings.modelChecker.timeout(0 = no timeout). - Show proved/unproved/unsupported:
--model-checker-show-proved-safe,--model-checker-show-unproved,--model-checker-show-unsupported(or JSON equivalents). - Contracts to verify:
--model-checker-contracts "source.sol:ContractName"orsettings.modelChecker.contracts:{"source.sol": ["ContractName"]}— only analyze those as deployed; reduces workload. - Trusted external calls:
--model-checker-ext-calls=trustedorsettings.modelChecker.extCalls: "trusted"— assume external contract at an address matches compile-time type (use with care; can be unsound). - Solvers:
--model-checker-solvers {all,cvc5,eld,smtlib2,z3}orsettings.modelChecker.solversarray. Defaultz3is usually sufficient.
Usage patterns
- Use
requirefor preconditions; SMTChecker treats them as assumptions. - Use
assertfor invariants; SMTChecker tries to prove they never fail. - For overflow checks (>=0.8.7): add
require(x < type(uint128).max);etc., or enableoverflow/underflowtargets. - Complex pure functions (e.g.
ecrecover) are abstracted as uninterpreted functions; assertions about equal inputs giving equal outputs can still be proved. - Reentrancy: external calls are treated as unknown; use modifiers (e.g. mutex) so the checker can infer invariants across calls.
Warnings
- "might happen here" = solver could not prove either way (timeout or too hard).
- "happens here" = proven failure; a counterexample may be given.
- Unsupported features (e.g. assembly) are over-approximated; can cause false positives, never false negatives for proved properties.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/smtchecker.html
-->
Common Patterns
Withdrawal (Pull) Pattern
Prefer users withdrawing funds over the contract pushing funds. Reduces reentrancy risk and avoids push failures (e.g. contract rejectors). Track balances and set to zero before external transfer.
mapping(address => uint) pendingWithdrawals;
function withdraw() public {
uint amount = pendingWithdrawals[msg.sender];
pendingWithdrawals[msg.sender] = 0;
(bool success,) = payable(msg.sender).call{value: amount}("");
require(success);
}Access Control
Restrict sensitive functions to an owner or role (e.g. OpenZeppelin’s Ownable or AccessControl). Use modifiers: modifier onlyOwner() { require(msg.sender == owner); _; }. Prefer role-based over single owner when multiple actors needed.
Checks-Effects-Interactions
Always: (1) validate inputs and state, (2) update contract state, (3) perform external calls. Prevents reentrancy and keeps state consistent.
Restricting Access to Contracts
Contracts cannot block ether sent via selfdestruct or miner/coinbase. They can reject normal calls in receive()/fallback() by reverting. Use withdrawal pattern for payments.
Upgradeability and Proxies
Upgradeability typically uses a proxy (delegatecall to implementation). Be aware of storage layout: proxy and implementation share the same storage layout; append new variables, do not change order or types of existing ones. Use transparent or UUPS proxy patterns and avoid constructor side effects in the implementation (use initializer functions).
<!-- Source references:
- https://docs.soliditylang.org/en/latest/common-patterns.html
- https://docs.soliditylang.org/en/latest/security-considerations.html
-->
Security Considerations
Smart contracts handle value and run in public; assume attackers. Follow checks-effects-interactions, avoid trust in randomness and tx.origin, and be aware of front-running and gas limits.
Reentrancy
Any external call (including ether transfer) hands control to the callee, which can call back. Update state before external calls (Checks-Effects-Interactions): (1) validate, (2) update storage, (3) then call out.
// Bad: state update after call
function withdraw() public {
(bool success,) = msg.sender.call{value: shares[msg.sender]}("");
if (success) shares[msg.sender] = 0;
}
// Good: update first
uint share = shares[msg.sender];
shares[msg.sender] = 0;
(bool success,) = payable(msg.sender).call{value: share}("");
require(success);Reentrancy can also occur via other contracts you depend on (multi-contract).
Gas Limit and Loops
Loops without a bounded iteration count (e.g. over storage) can exceed block gas and stall the transaction. Document unbounded loops; consider caps or pagination.
Visibility and Private Data
private and internal state is still readable from chain data. Do not store secrets in contract state. Everything in a contract is public.
Randomness and Oracles
Block data (block.timestamp, block.prevrandao) is predictable by builders. Do not use as sole source of randomness for value-bearing logic. Use commit-reveal or oracles when needed.
Sending and Receiving Ether
Contracts cannot prevent receiving ether (e.g. selfdestruct to address, coinbase reward). Use a payable function and/or withdrawal pattern to control flows. Prefer pull (withdraw) over push (send) to avoid reentrancy and griefing.
Front-Running and MEV
Transactions are visible in the mempool. Design critical operations (e.g. large trades, auctions) with MEV in mind (e.g. private order flow, commit-reveal, or accepted MEV).
Known Bugs
Check the compiler’s list of known security-relevant bugs and use a fixed/supported compiler version.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/security-considerations.html
-->
Style Guide (Concise)
Consistency matters more than any single rule. Prefer project conventions when they conflict with this guide.
File layout order
1. Pragma 2. Imports 3. Events 4. Errors 5. Interfaces 6. Libraries 7. Contracts
Contract element order
Inside contract/library/interface:
1. Type declarations (structs, enums) 2. State variables 3. Events 4. Errors 5. Modifiers 6. Functions
Function order by visibility: constructor, receive, fallback, external, public, internal, private. Within each group, put view/pure last.
Modifier order on functions
1. Visibility (public, external, etc.) 2. Mutability (view, pure) 3. virtual 4. override / override(Base1, Base2) 5. Custom modifiers
Naming
- Contracts, libraries, interfaces: CapWords (e.g.
SimpleToken,Owned). Filename should match the main contract. - Structs, enums, events: CapWords.
- Functions, parameters, local/state variables: mixedCase (e.g.
getBalance,initialSupply). - Constants: UPPER_CASE_WITH_UNDERSCORES.
- Modifiers: mixedCase.
- Avoid single letters
l,O,I(confusable with 1/0). - Leading underscore for internal/private (e.g.
_internalHelper) to distinguish from public API.
Other
- 4 spaces indent; spaces over tabs. Two blank lines between top-level declarations; one between functions.
- Max line length ~120; wrap with one argument per line, closing
);on its own line. - Imports at top of file. Braces: opening on same line as declaration, closing on own line at same indent.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/style-guide.html
-->
Expressions and Control Structures
Control Structures
if, else, while, do, for, break, continue, return — C/JS semantics. Parentheses required for conditionals; braces can be omitted for single-statement bodies. No non-boolean to boolean conversion.
Exception handling: try/catch only for external calls and contract creation. Use revert for custom errors.
Function Calls
Internal: Direct/recursive calls within same contract; implemented as jumps; memory not cleared. Avoid deep recursion (EVM stack limit 1024).
External: this.g(8) or c.g(2) — message call. All arguments copied to memory. Cannot use this in constructor.
Specify value and gas: feed.info{value: 10, gas: 800}(). The parentheses perform the call; without them the options are lost. Callee must be payable for value.
Calls to non-existing contracts: compiler uses extcodesize to check; exception if no code. Exception skipped when return data is decoded (ABI decoder catches it).
Return data: Low-level call returns (bool success, bytes memory data). Use abi.decode(data, (T)) to decode.
Revert and Assert
require(condition, "message")orrequire(condition)— revert with optional message or custom error.revert()/revert("message")/revert CustomError(args)— abort and revert.assert(condition)— for invariants; should never fail. UsesPanicerror in 0.8+.
Use custom errors instead of string messages for gas efficiency.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/control-structures.html
-->
Layout of a Solidity Source File
Source files contain contract definitions, imports, pragma/using-for directives, and top-level struct/enum/function/error/constant definitions.
SPDX License Identifier
Use machine-readable SPDX identifier at top of file:
// SPDX-License-Identifier: MITUse UNLICENSED for proprietary code (no usage allowed). Compiler includes the string in bytecode metadata; it does not validate against SPDX list.
Pragmas
Version: Reject incompatible compiler versions.
pragma solidity ^0.5.2; // >=0.5.2 and <0.6.0
pragma solidity >=0.8.0 <0.9.0;Same syntax as npm semver. Pragma does not change compiler version; it only checks compatibility.
ABI coder: Prefer v2 (default since 0.8.0). v1 is deprecated.
pragma abicoder v2;Experimental: e.g. pragma experimental SMTChecker; — SMTChecker is now enabled via compiler options, not pragma.
Pragmas are file-local; importing a file does not apply its pragmas to the importer.
Import
import "filename"; // pollutes namespace, not recommended
import * as symbolName from "filename"; // symbolName.symbol
import "filename" as symbolName; // same as above
import {symbol1 as alias, symbol2} from "filename";Import paths are resolved via compiler VFS (Standard JSON or import callback). Command-line compiler maps paths to filesystem; Remix can use HTTP/IPFS/NPM.
Comments
- Single-line:
// - Multi-line:
/* ... */ - NatSpec:
///or/** ... */above functions/statements (see style guide)
<!-- Source references:
- https://docs.soliditylang.org/en/latest/layout-of-source-files.html
-->
Structure of a Contract
Contracts are like classes: state variables (storage or transient), functions, modifiers, events, errors, structs, enums. Contracts can inherit from others. Special kinds: libraries and interfaces.
State Variables
Stored in contract storage or transient storage (cleaned at end of transaction). See data locations for types.
contract SimpleStorage {
uint storedData; // state variable
}Functions
Executable units; can be inside contracts or free (outside). Free functions are implicitly internal; code is inlined into callers.
contract SimpleAuction {
function bid() public payable { }
}
function helper(uint x) pure returns (uint) { return x * 2; }Function Modifiers
Amend function semantics declaratively. No overloading (same name, different params). Can be overridden.
modifier onlySeller() {
require(msg.sender == seller, "Only seller can call this.");
_;
}
function abort() public view onlySeller { }Events
EVM logging interface. Emit from within contracts.
event HighestBidIncreased(address bidder, uint amount);
emit HighestBidIncreased(msg.sender, msg.value);Errors
Custom errors with names and data; cheaper than string reverts. Use in revert statements.
error NotEnoughFunds(uint requested, uint available);
revert NotEnoughFunds(amount, balance);Structs and Enums
Structs group variables; enums define a finite set of constants.
struct Voter { uint weight; bool voted; address delegate; uint vote; }
enum State { Created, Locked, Inactive }<!-- Source references:
- https://docs.soliditylang.org/en/latest/structure-of-a-contract.html
- https://docs.soliditylang.org/en/latest/contracts.html
-->
Types
Solidity is statically typed. No undefined/null; new variables have type-dependent default values. Use revert or return (value, success) for failure handling.
Value Types
- Booleans:
bool(true/false) - Integers:
int/uintin steps of 8 (e.g.int8..int256,uint8..uint256).uint/int= 256 bits. - Address:
address,address payable(hastransfer/send). Literals:0x... - Bytes: fixed-size
bytes1..bytes32, dynamicbytesandstring - Enums: user-defined, finite set
- Function types: internal/external function references
Reference Types
Must specify data location: memory (lifetime of call), storage (contract storage), calldata (read-only, for external params).
- Arrays:
T[],T[k]. Dynamic/storage arrays have.push(),.push(x),.pop().bytesandstringare special arrays. - Structs: user-defined types grouping variables
- Mappings:
mapping(KeyType => ValueType); only in storage; no length, no iteration. Nested mappings possible.
Operators
Arithmetic, comparison, logical, bitwise, shift, ternary. No implicit non-boolean to boolean (e.g. if (1) invalid). See operator precedence table in docs.
Conversions
Implicit: e.g. uint8 to uint256, contract to address. Explicit: e.g. uint8(x), address payable(x). Literals to type: uint8(1).
<!-- Source references:
- https://docs.soliditylang.org/en/latest/types.html
- https://docs.soliditylang.org/en/latest/types/value-types.html
- https://docs.soliditylang.org/en/latest/types/reference-types.html
- https://docs.soliditylang.org/en/latest/types/mapping-types.html
- https://docs.soliditylang.org/en/latest/types/operators.html
- https://docs.soliditylang.org/en/latest/types/conversion.html
-->
Units and Globally Available Variables
Ether Units
Suffixes multiply by power of ten: wei, gwei, ether (1 ether == 1e18 wei). Removed: finney, szabo (0.7.0), years (0.5.0).
Time Units
seconds, minutes, hours, days, weeks — naive conversion (1 days == 24 hours, etc.). Not for calendar math; leap seconds not represented. Apply to literals only, not variables.
Block and Transaction
- block:
basefee,blobbasefee,chainid,coinbase,difficulty/prevrandao,gaslimit,number,timestamp - msg:
data(calldata),sender,sig(bytes4),value - tx:
gasprice,origin(full call chain) - Other:
blockhash(uint blockNumber),blobhash(uint index),gasleft()
msg.sender and msg.value change on every external call. Prefer msg.sender over tx.origin (origin can be spoofed by intermediate contract).
ABI Encoding / Hashing
abi.encode(...),abi.encodePacked(...),abi.encodeWithSelector(selector, ...),abi.encodeWithSignature(sig, ...),abi.encodeCall(f, (args))abi.decode(bytes memory data, (T, ...))keccak256(bytes memory),sha256(bytes memory),ripemd160(bytes memory)ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
Contract / Address
balance,code,codehash— onaddressoraddress payableselfdestruct(payable dest)— destroy contract, send ether to dest (deprecated in EVM; use with care)
Mathematical and Crypto
addmod,mulmod— (x + y) % k with arbitrary precisionblock.prevrandao— beacon chain randomness (EVM >= Paris)
<!-- Source references:
- https://docs.soliditylang.org/en/latest/units-and-global-variables.html
-->
Inline Assembly
Solidity supports inline assembly (Yul dialect) for low-level control. Use when you need opcodes, fine-grained gas control, or library code that expects a specific layout.
Syntax
assembly {
// Yul code
}Assembly block can access local Solidity variables (by name), assign to them, and use Yul constructs. Memory/slot layout must match Solidity’s conventions when interfacing.
Access to Variables
- Local variables: Referenced by name; value/slot copied as needed.
- Storage: Use
.slotand.offsetfor dynamic types; follow layout (e.g. packed slots, dynamic array layout). - Memory: Solidity uses a contiguous area; first free memory pointer at 0x40; arrays/structs laid out in documented order.
Safety
Inline assembly bypasses many Solidity checks. Wrong layout or wrong opcodes can corrupt storage/memory or break invariants. Prefer high-level Solidity; use assembly only when necessary and document assumptions.
Common Patterns
- Low-level calls:
call(g, a, v, in, insize, out, outsize)— return 0 on failure. - Storage:
sload(slot),sstore(slot, value). - Memory:
mload(addr),mstore(addr, value). - Hashing: Use precompile or inline keccak256 where available.
See Yul documentation for opcodes and EVM semantics.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/assembly.html
- https://docs.soliditylang.org/en/latest/yul.html
-->
Contracts (Details)
Contracts hold persistent state and functions. Calling another contract performs an EVM call and switches context (caller state inaccessible). No cron; something must call the contract.
Creating Contracts
new ContractName(args) — creation via contract type. Get address of created contract. Constructor runs once. Use salt for CREATE2: new ContractName{salt: salt}(args).
Visibility and Getters
- public: external interface + automatic getter for state variables
- external: only external calls (can be more gas-efficient for large arrays)
- internal: contract and derived
- private: only current contract (still visible on-chain)
Function Modifiers
Modifiers wrap function body; _ is replaced by function body. Apply in declaration: function f() public view onlyOwner { }. Overloading not allowed; overriding is.
Functions
- view: no state change; pure: no state read/write
- payable: can receive ether
- Overloading: same name, different parameters
- Free functions: defined outside contract; implicit internal; inlined into callers
Parameters and return variables declared like locals; return variables can be assigned and used by name.
Events and Errors
Events: Declare with event Name(type indexed a, type b);. Indexed params (up to 3) are searchable. Emit with emit Name(a, b);.
Errors: error Name(params); then revert Name(args);. Cheaper than string messages; can encode data for front-end.
Inheritance
contract B is A, C { }. Multiple inheritance; linearization (C3). super.f() for overridden function. virtual/override for overrides. Constructors: pass args up with A(arg) in constructor list.
Abstract Contracts and Interfaces
- Abstract: at least one function without body; cannot be instantiated
- Interface: no storage, no constructor, no non-external functions; all functions external
Libraries
No state (or only constant/immutable), no inheritance, no ether balance. Deployed once and linked; using Lib for Type attaches library functions to type. library L { function f(Type x) external { } } — use x.f() or L.f(x).
Using For
using Lib for Type; or using Lib for *; — attach library functions to type(s) for the rest of the file.
Transient Storage
EIP-1153: storage that is cleared at end of transaction. Use for same-transaction context (e.g. reentrancy guards) without persistent storage cost.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/contracts.html
- https://docs.soliditylang.org/en/latest/contracts/creating-contracts.html
- https://docs.soliditylang.org/en/latest/contracts/visibility-and-getters.html
- https://docs.soliditylang.org/en/latest/contracts/functions.html
- https://docs.soliditylang.org/en/latest/contracts/events.html
- https://docs.soliditylang.org/en/latest/contracts/errors.html
- https://docs.soliditylang.org/en/latest/contracts/inheritance.html
- https://docs.soliditylang.org/en/latest/contracts/interfaces.html
- https://docs.soliditylang.org/en/latest/contracts/libraries.html
- https://docs.soliditylang.org/en/latest/contracts/using-for.html
-->
Events
Events abstract the EVM logging facility. Arguments are stored in the transaction log associated with the contract address. Logs are not readable from contracts; off-chain clients subscribe via RPC (e.g. subscribe("logs")).
Declaration and emit
Define events at file level or inside contracts (including interfaces and libraries). Emit with emit EventName(args).
event Deposit(address indexed from, bytes32 indexed id, uint value);
function deposit(bytes32 id) public payable {
emit Deposit(msg.sender, id, msg.value);
}Indexed and topics
- Up to three parameters can be
indexed. They go into the log topics (32-byte each). Non-indexed parameters are ABI-encoded in the data part. - Topics are used for filtering (e.g. filter by address or indexed value). Reference types as indexed store the keccak256 hash of the value, not the value itself.
- Anonymous events: no signature topic, so you cannot filter by event name—only by contract address. Benefit: cheaper, and you can have four indexed parameters.
Selector
- `event.selector`: For non-anonymous events,
bytes32equal tokeccak256of the event signature (used as first topic).
Interpretation
Log type is not stored; to decode you must know the event type, which parameters are indexed, and whether it is anonymous. Anonymous events can be used to mimic other event signatures (documentation warns about "faking").
ABI / client usage
Event signature and indexed args appear in the topics array; non-indexed args are in data. Clients use the ABI to decode; method key is the canonical event signature.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/contracts.html#events
- https://docs.soliditylang.org/en/latest/abi-spec.html#events
-->
Inheritance
Contracts use contract B is A, C { }. A single deployed contract contains code from all bases; internal calls to base functions use JUMP. State variable shadowing is an error.
Virtual and override
- Base functions that can be overridden must be marked
virtual. Overriding functions useoverride. For multiple bases defining the same function, list them:override(Base1, Base2). - Visibility: overriding can change
externaltopubliconly. Mutability can be made stricter:nonpayable→vieworpure,view→pure.payablecannot be changed. - Private functions cannot be
virtual. Interface functions are implicitlyvirtual; overriding them does not requireoverride(except when defined in multiple bases). - Public state variables can override external functions if the getter matches; state variables cannot be overridden.
Super and qualified calls
super.f()calls the next base in the linearized hierarchy (not “parent” in the literal sense). Use when you want the chain of overrides (e.g. Final → Base2 → Base1 → Base).ContractName.f()calls a specific contract’s implementation. Use when you want to skip part of the chain.
C3 linearization
Base order matters. List bases from “most base-like” to “most derived”. The linearization fixes a unique order for constructor execution and super resolution. Conflicting orders (e.g. C is A, X when A is X) can make linearization impossible and cause a compile error.
Constructors
- Single optional constructor per contract with the
constructorkeyword. No overloading. - Base constructors run in linearized order. Pass arguments in the inheritance list:
is Base(7)or in the derived constructor:Base(y * y)in the modifier position. Must provide args for all bases or mark the contract abstract. - Abstract contracts can have constructors with internal-only parameters (e.g. storage pointers); concrete derived contracts must supply them.
Modifier overriding
Modifiers can be virtual and override; this is deprecated and scheduled for removal.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/contracts.html#inheritance
- https://docs.soliditylang.org/en/latest/contracts/inheritance.html
-->
Interfaces
Interfaces are declared with the interface keyword. They cannot have state variables, constructors, or implemented functions. All declared functions must be external. They cannot declare modifiers. They can inherit only from other interfaces.
Restrictions
- No state variables, no constructor, no function bodies.
- No modifiers. All functions are implicitly
virtual. - Inheritance only from interfaces (not contracts). Multiple interface inheritance is allowed.
Interfaces are designed to align with the Contract ABI: conversion between ABI and interface should be lossless.
Types inside interfaces
Interfaces can define structs and enums. Other contracts access them as InterfaceName.StructName or InterfaceName.EnumName.
Overriding interface functions
When a contract implements an interface, overriding functions do not require the override keyword unless the function comes from multiple bases. To allow further overrides, mark the implementing function virtual.
Example
interface Token {
enum TokenType { Fungible, NonFungible }
struct Coin { string obverse; string reverse; }
function transfer(address recipient, uint amount) external;
}
contract MyToken is Token {
function transfer(address recipient, uint amount) external override {
// ...
}
}<!-- Source references:
- https://docs.soliditylang.org/en/latest/contracts.html#interfaces
- https://docs.soliditylang.org/en/latest/contracts/interfaces.html
-->
Libraries
Libraries are deployed once; their code runs in the caller’s context via DELEGATECALL. No state variables, no inheritance, cannot receive ether or be destroyed. Internal library functions are inlined at compile time (JUMP); public/external library functions are real external calls (DELEGATECALL).
Using a library
- External style:
Lib.f(args)orx.f()withusing Lib for Type;— results in DELEGATECALL unless the function is internal (see below). - Internal functions: If a library only has internal (or private) functions, calls are inlined into the contract; no separate deployment needed. Internal functions can take memory types by reference.
Storage references
Library functions can take storage reference parameters; only the storage address is passed. Idiomatic to name the first such parameter self. The calling contract’s storage is modified. Example: function insert(Data storage self, uint value) public returns (bool).
Linking and placeholders
Deployed bytecode contains placeholders (hash of fully qualified library name) where the library address must be inserted. Replace via compiler (library addresses) or linker. Do not deploy bytecode with unresolved placeholders.
Call protection
If library code runs via CALL instead of DELEGATECALL (e.g. direct call to library address), it reverts unless the function is view or pure. The compiler injects a check comparing address(this) at runtime to the deployment-time address.
Selectors and ABI
External library function selectors use an internal naming schema (not the same as contract ABI). Storage pointer types use identifiers like mapping(K => V) storage and are encoded as uint256 (storage slot). Use Lib.f.selector to get the selector.
Address
Get library address with address(LibraryName).
<!-- Source references:
- https://docs.soliditylang.org/en/latest/contracts.html#libraries
-->
NatSpec Format
Ethereum Natural Language Specification Format: structured comments for functions, contracts, events, and errors. Use /// or /** ... */. Annotate all public/external interfaces for tooling and end-user messages.
Tags
| Tag | Context | Purpose |
|---|---|---|
@title | contract, interface, library, struct, enum | Short title |
@author | contract, interface, library, struct, enum | Author name |
@notice | function, state var, event, struct, enum, error | End-user explanation (shown at interaction time) |
@dev | function, state var, event, struct, enum, error | Developer details |
@param | function, event, error | Parameter description (name must follow) |
@return | function, public state var | Return value(s); use multiple for multiple returns |
@inheritdoc | function, enum | Copy missing tags from base (e.g. @inheritdoc BaseContract) |
@custom:<name> | anywhere | Application-defined (e.g. analysis tools, SMTChecker) |
If no tags are used, the compiler treats the comment as @notice. Custom tags: @custom: followed by lowercase letters or hyphens (cannot start with hyphen).
Example
/// @title A simulator for trees
/// @author Larry A. Gardner
/// @notice You can use this contract for only the most basic simulation
/// @dev All function calls are currently implemented without side effects
/// @custom:experimental This is an experimental contract.
contract Tree {
/// @notice Calculate tree age in years, rounded up, for live trees
/// @param rings The number of rings from dendrochronological sample
/// @return Age in years, rounded up for partial years
/// @return Name of the tree
function age(uint256 rings) external virtual pure returns (uint256, string memory) {
return (rings + 1, "tree");
}
}Output
Generate machine-readable docs:
solc --userdoc --devdoc ex1.sol- User doc (kind
"user"):noticefor contract and methods; for end-user clients. - Dev doc (kind
"dev"):title,author,details,params,returns, custom tags; for developers.
Method keys are canonical function/event signatures (as in ABI), not just names.
Inheritance
Functions without NatSpec inherit base documentation unless: parameter names differ, there are multiple bases, or @inheritdoc ContractName specifies the source.
Dynamic expressions
Client software may substitute parameter values in @notice text, e.g. ` a ` replaced with the actual argument when presenting to the user.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/natspec-format.html
-->
Transient Storage
EIP-1153 adds a transaction-scoped data location: transient. Values are cleared at the end of the transaction. Gas cost is lower than persistent storage. Requires EVM version cancun or newer.
Declaration
Use transient for state variables (value types only in current Solidity). Cannot initialize in place (value would be cleared at end of creation transaction). No constant or immutable with transient. Transient and persistent storage use separate address space; layout of one does not affect the other.
bool transient locked;
modifier nonReentrant {
require(!locked, "Reentrancy attempt");
locked = true;
_;
locked = false;
}Reentrancy locks
Transient storage is well-suited to reentrancy guards: set at entry, clear at exit. Composable within the same transaction (unlike a guard that stays set). Clear the guard at end of call to allow composed transactions.
Composability caveats
Within one transaction, multiple calls to the same contract share the same transient store. If you use transient storage to carry context between calls in the same transaction, later calls in the same tx see that context; calls in a different transaction do not. This can break composability (e.g. batching) if callers assume persistent semantics. Prefer clearing transient state at the end of each call when possible. See EIP-1153 security considerations.
DELEGATECALL and STATICCALL
- With DELEGATECALL/CALLCODE, the caller owns transient storage. Libraries cannot take transient refs in parameters; use inline assembly in libraries to access transient storage.
- With CALL/STATICCALL, the callee owns it. TSTORE in STATICCALL reverts; TLOAD is allowed in STATICCALL.
Reverts
Reverting a frame reverts all transient writes from that frame and inner calls. try/catch can prevent revert from bubbling and preserve caller’s transient state.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/contracts.html#transient-storage
- https://docs.soliditylang.org/en/latest/contracts/transient-storage.html
- https://eips.ethereum.org/EIPS/eip-1153
-->
Visibility and Getters
Function visibility
- external: Part of contract interface; callable via message. Cannot be called internally as
f()— usethis.f(). Can be more gas-efficient for large calldata (arguments not copied to memory). - public: Callable internally and externally. Generates an external entry in the ABI.
- internal: Contract and derived contracts only; not in ABI. Can take internal types (e.g. storage refs, mappings).
- private: Like internal but not visible in derived contracts.
State variables: public (getter generated), internal (default), private (not in derived). Private/internal only hide from other contracts; data is still on-chain.
Getter functions
For public state variables the compiler generates getters:
- Name = variable name, no arguments (except for mappings/arrays below). External visibility.
- Internally,
xreads storage;this.x()calls the getter. - Arrays: getter takes index and returns one element (e.g.
myArray(i)). No getter that returns the whole array; add a function if needed. - Mappings: getter takes key(s) and returns value. Nested mappings: one argument per key.
- Structs in mappings: getter returns only types that can be selected (value types,
bytes, etc.); mappings and dynamic arrays inside the struct are omitted from the getter signature.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/contracts.html#visibility-and-getters
- https://docs.soliditylang.org/en/latest/contracts/visibility-and-getters.html
-->
Yul
Yul is an intermediate language used in the Solidity compiler and in inline assembly. It is EVM-targeted and supports other backends (e.g. Ewasm).
When to Use
- Inline assembly in Solidity (see
features-assembly) for low-level logic. - Standalone Yul for very gas-optimized or fixed layout code (e.g. custom creation code).
Concepts
- Blocks:
{ }with statements. - Variables: Declared with
let x := valueor assigned; typed only by usage. - Literals: Decimal or hex (e.g.
0x20). - Functions:
function f(a, b) -> x, y { ... }; return via variable names. - Control flow:
if cond { },for { } cond { } { },switch value case n { } default { }.
EVM Opcodes
Yul exposes EVM opcodes as builtins: e.g. add, sload, sstore, mload, mstore, call, delegatecall, create, create2. Use returndatasize, returndatacopy after external calls when decoding return data.
Object / Subobject
Standalone Yul can be structured as objects with code and data subobjects; Solidity inline assembly is a single block. For full Yul object format and deployment, see compiler docs.
<!-- Source references:
- https://docs.soliditylang.org/en/latest/yul.html
-->