{
  "slug": "solidity-smart-contracts",
  "title": "Solidity Smart Contract Development",
  "domain_slug": "blockchain",
  "subdomain_slug": null,
  "difficulty": "intermediate",
  "tags": [
    "solidity",
    "smart-contracts",
    "ethereum",
    "OpenZeppelin",
    "gas-optimization",
    "ERC-20",
    "ERC-721",
    "Hardhat"
  ],
  "is_free": false,
  "token_count": 2685,
  "uncompressed_estimate": 3087,
  "savings_pct": 13.0,
  "rosetta": "[DECODE] SC=smart contract|OZ=OpenZeppelin|RE=reentrancy|GO=gas optimization|EVM=Ethereum Virtual Machine|HH=Hardhat|TX=transaction|EOA=externally owned account|ABI=application binary interface|UUPS=Universal Upgradeable Proxy Standard|TP=transparent proxy|CEI=checks-effects-interactions|AC=access control|FL=flash loan|MEV=maximal extractable value|SLT=storage layout|WEI=smallest unit of ether|CAL=delegatecall|NM=NatSpec comment|UR=upgradeable|MR=modifier|DS=data structure",
  "content_compressed": "# Solidity SC Development\n\n## Solidity Language Fundamentals\n\nSolidity is a statically-typed, contract-oriented language targeting the EVM. Every SC is deployed at an address and contains state variables (stored on-chain), functions (logic), events (indexed logs), and MRs (reusable preconditions).\n\nValue types: `uint256` (most common, default for integers \u2014 always use explicitly sized types), `int256`, `address` (20 bytes, use `address payable` for ETH transfers), `bool`, `bytes32` (cheaper than `string` for fixed-length data). Reference types: arrays, mappings, structs. Mappings cannot be iterated \u2014 if you need iteration, maintain a parallel array.\n\nVisibility: `public` (external + internal, auto-generates getter), `external` (only callable from outside \u2014 cheaper than public for large calldata), `internal` (this contract + derived), `private` (this contract only \u2014 NOT truly private, data is readable on-chain).\n\nState mutability: `view` (reads state, no gas when called externally), `pure` (no state access), `payable` (can receive ETH). Unmarked functions can read and write state.\n\nKey patterns:\n```solidity\n// MR pattern \u2014 reusable preconditions\nmodifier onlyOwner() {\n    require(msg.sender == owner, \"Not owner\");\n    _;\n}\n\n// Event emission \u2014 always emit after state changes\nevent Transfer(address indexed from, address indexed to, uint256 value);\n\n// Error handling \u2014 custom errors save gas vs require strings\nerror InsufficientBalance(uint256 requested, uint256 available);\nif (balance < amount) revert InsufficientBalance(amount, balance);\n```\n\nCustom errors (Solidity 0.8.4+) save significant gas compared to `require` with string messages. A `require` with a 32-character string costs ~500 more gas than a custom error `revert`.\n\n## OZ Patterns & Libraries\n\nOZ provides audited, battle-tested SC components. Always inherit rather than writing from scratch.\n\n**AC patterns**: `Ownable` for single-owner (simple but centralized). `AccessControl` for role-based (define roles like MINTER_ROLE, PAUSER_ROLE). `AccessControlDefaultAdminRules` adds two-step admin transfer with delay \u2014 use this for production.\n\n**Token standards**: `ERC20` (fungible tokens \u2014 include `ERC20Permit` for gasless approvals via EIP-2612), `ERC721` (NFTs \u2014 use `ERC721Enumerable` only if on-chain enumeration needed, it's expensive), `ERC1155` (multi-token \u2014 single contract for fungible + non-fungible, batch transfers save gas).\n\n**Security utilities**: `ReentrancyGuard` (add `nonReentrant` MR to all external functions that change state or transfer value), `Pausable` (emergency stop pattern \u2014 include in every production SC), `Address.sendValue` (safe ETH transfer avoiding the 2300 gas stipend issue).\n\n**Governance**: `Governor` + `TimelockController` for DAO governance. `Governor` handles proposal creation and voting. `TimelockController` enforces delay between vote passage and execution. Always use timelock for treasury operations.\n\nImport convention: `import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";` \u2014 pin OZ version in package.json. Never use `@latest`.\n\n## GO Techniques\n\nGas is the execution cost on EVM. Every operation has a fixed gas cost. Optimization reduces TX cost for users.\n\n**SLT optimization**: storage is the most expensive operation. `SSTORE` (write) costs 20,000 gas for new slot, 5,000 for update. `SLOAD` (read) costs 2,100 gas (cold) or 100 gas (warm). Pack variables into single 256-bit slots:\n```solidity\n// Bad: 3 storage slots (60,000 gas to initialize)\nuint256 amount;    // slot 0\nuint128 timestamp; // slot 1\nbool active;       // slot 2\n\n// Good: 2 storage slots (40,000 gas to initialize)\nuint128 timestamp; // slot 0 (16 bytes)\nbool active;       // slot 0 (1 byte, packed)\nuint256 amount;    // slot 1\n```\n\n**Memory vs calldata**: use `calldata` for external function parameters that aren't modified \u2014 avoids copying to memory. Use `memory` only when you need to modify the data.\n\n**Loop optimization**: cache array length (`uint256 len = arr.length`), use `unchecked { ++i }` for loop counters (safe in Solidity 0.8+ since counter can't realistically overflow), avoid storage reads inside loops (cache to memory variable).\n\n**Short-circuiting**: in `require(a && b)`, put the cheaper check first. If `a` fails, `b` is never evaluated.\n\n**Constants and immutables**: `constant` for compile-time values (costs 0 gas to read \u2014 inlined into bytecode). `immutable` for constructor-set values (much cheaper than regular storage reads).\n\n**Mapping vs array**: mappings have O(1) lookup with no length tracking overhead. Use mappings as default DS. Only use arrays when you need enumeration or ordering.\n\n## RE Prevention & Security\n\nRE is the most dangerous SC vulnerability. It occurs when an external call re-enters the calling contract before state updates complete.\n\nThe CEI pattern is the primary defense:\n```solidity\nfunction withdraw(uint256 amount) external nonReentrant {\n    // CHECKS \u2014 validate inputs and conditions\n    require(balances[msg.sender] >= amount, \"Insufficient\");\n    \n    // EFFECTS \u2014 update state BEFORE external call\n    balances[msg.sender] -= amount;\n    \n    // INTERACTIONS \u2014 external call LAST\n    (bool success, ) = msg.sender.call{value: amount}(\"\");\n    require(success, \"Transfer failed\");\n}\n```\n\nAlways combine CEI with OZ `ReentrancyGuard` (`nonReentrant` MR) as defense in depth. Cross-contract RE (read-only RE) occurs when contract A calls contract B, which calls back into a view function on A that reads stale state. Guard against this with mutex locks or by ensuring view functions reflect pending state changes.\n\nFL attack prevention: validate that critical price feeds or balance ratios haven't been manipulated within the same TX. Use time-weighted average prices (TWAPs) instead of spot prices. FL attacks exploit atomic composability \u2014 anything that reads instantaneous on-chain state is vulnerable.\n\nMEV awareness: TX ordering is controlled by block builders. Front-running (seeing your TX and inserting one before it) and sandwich attacks (buying before your trade, selling after) are common. Mitigation: use commit-reveal schemes, private mempools (Flashbots Protect), or slippage limits.\n\n## Proxy Patterns for UR SCs\n\nSCs are immutable once deployed. Proxy patterns enable upgradability by separating logic from storage.\n\n**TP (OZ TransparentUpgradeableProxy)**: admin calls go to proxy (upgrade functions), user calls are delegated to implementation via CAL. Simple but has gas overhead \u2014 every call checks if caller is admin. ProxyAdmin contract manages upgrades. Use for established projects with infrequent upgrades.\n\n**UUPS (EIP-1822)**: upgrade logic lives in the implementation contract, not the proxy. Lighter proxy (cheaper deployment and per-call gas). The implementation must include `_authorizeUpgrade` function with AC. Risk: if you deploy an implementation without upgrade logic, it's permanently frozen.\n\nSLT compatibility is critical for UR SCs: never change the order or type of existing state variables in upgrades. Only append new variables at the end. Never remove variables \u2014 add unused spacers if needed. OZ provides `@openzeppelin/contracts-upgradeable` with initializers instead of constructors.\n\n**Initializer pattern**: UR SCs cannot use constructors (constructor runs in implementation context, not proxy). Use `initialize` function with OZ `initializer` MR that ensures it runs exactly once:\n```solidity\nfunction initialize(address admin) public initializer {\n    __Ownable_init(admin);\n    __ERC20_init(\"Token\", \"TKN\");\n    __UUPSUpgradeable_init();\n}\n```\n\nStorage gaps: in base contracts of UR systems, reserve storage slots for future variables: `uint256[50] private __gap;` Each upgrade can use gap slots for new variables without breaking derived contract SLT.\n\n## ERC Standards Implementation\n\n**ERC-20 essentials**: implement `transfer`, `approve`, `transferFrom`, `balanceOf`, `totalSupply`, `allowance`. Add `ERC20Permit` for gasless approvals (users sign a message, anyone can submit the TX). Consider `ERC20Votes` if governance is planned \u2014 tracks historical balances for snapshot-based voting.\n\n**ERC-721 (NFTs)**: `tokenURI` returns metadata JSON (typically IPFS URI). Use `_safeMint` (checks receiver can handle NFTs) not `_mint`. `ERC721URIStorage` for per-token URIs, `ERC721Royalty` (EIP-2981) for creator royalties. Batch minting: ERC721A from Azuki saves ~50% gas for bulk mints by lazy-initializing ownership data.\n\n**ERC-1155 (multi-token)**: single contract manages multiple token types. `balanceOf(address, id)` replaces separate contracts. Batch operations (`safeBatchTransferFrom`) save gas. `uri(id)` returns metadata \u2014 use `{id}` substitution pattern per spec. Ideal for gaming (items, currencies, land in one contract).\n\n## HH Testing & Deployment\n\nHH is the standard SC development environment. Project structure: `contracts/`, `test/`, `scripts/`, `hardhat.config.ts`.\n\nTesting essentials:\n```javascript\nconst { expect } = require(\"chai\");\nconst { ethers } = require(\"hardhat\");\nconst { loadFixture } = require(\"@nomicfoundation/hardhat-network-helpers\");\n\nasync function deployFixture() {\n    const [owner, user] = await ethers.getSigners();\n    const Token = await ethers.getContractFactory(\"Token\");\n    const token = await Token.deploy();\n    return { token, owner, user };\n}\n\nit(\"should revert on insufficient balance\", async () => {\n    const { token, user } = await loadFixture(deployFixture);\n    await expect(token.connect(user).transfer(owner, 100))\n        .to.be.revertedWithCustomError(token, \"InsufficientBalance\");\n});\n```\n\nUse `loadFixture` for test isolation \u2014 snapshots EVM state and reverts between tests (faster than redeployment). Test RE explicitly: create an attacker contract that calls back during `receive()`. Test AC: verify that unauthorized callers get reverted with correct error. Test edge cases: zero amounts, max uint256, address(0).\n\nGas reporting: enable `hardhat-gas-reporter` plugin. Compare gas costs before and after GO changes. Set gas price assumptions in config for cost estimation.\n\nDeployment: use HH Ignition (declarative deployment module) or custom scripts. Always verify on block explorer: `npx hardhat verify --network mainnet CONTRACT_ADDRESS constructor_args`. Deploy to testnet first (Sepolia), run full test suite against testnet deployment, then mainnet. Use multi-sig (Safe) as owner for mainnet deployments \u2014 never EOA.\n\n## Security Audit Preparation\n\nPre-audit checklist: complete NM documentation for every public and external function. Run Slither (static analysis) and fix all high/medium findings before engaging auditors. Run Mythril (symbolic execution) for deeper vulnerability detection. Achieve 100% line coverage and 90%+ branch coverage in tests. Document all design decisions and known tradeoffs in a separate document for auditors.\n\nCommon vulnerability classes beyond RE:\n- **Integer overflow/underflow**: Solidity 0.8+ has built-in overflow checks. But `unchecked` blocks bypass this \u2014 only use for provably safe arithmetic (loop counters, known-bounded calculations).\n- **Access control**: every state-changing function must have explicit AC. Missing AC on `initialize` in UR SCs is a critical vulnerability \u2014 anyone can call it first.\n- **Oracle manipulation**: price oracles using spot prices from DEXs are vulnerable to FL manipulation. Always use TWAPs (Uniswap V3 observation, Chainlink feeds). Chainlink feeds can go stale \u2014 check `updatedAt` timestamp and revert if data is older than the heartbeat.\n- **Denial of service**: unbounded loops over user-controlled arrays can exceed block gas limit. Implement pagination or limit array sizes. `transfer` and `send` forward only 2300 gas \u2014 if the recipient has a complex `receive` function, the TX fails. Use `call` with explicit gas limits.\n- **Front-running**: any TX that depends on ordering can be front-run. Use commit-reveal for auctions, Flashbots for sensitive TXs. Slippage limits on DEX trades protect against sandwich attacks.\n- **Signature replay**: signatures used for meta-TXs must include nonce and chain ID to prevent replay across TXs or chains. EIP-712 provides typed structured data hashing that includes domain separator with chain ID and contract address.\n\nGas estimation: use HH gas reporter to benchmark every function. Compare against block gas limit (30M on Ethereum mainnet). Functions approaching 50% of block gas limit are risky \u2014 may fail during network congestion. Batch operations should have configurable batch sizes to stay within gas limits.\n\n## DeFi SC Patterns\n\nAMM (Automated Market Maker): constant product formula `x * y = k`. Adding liquidity: deposit both tokens in current ratio, receive LP tokens. Removing liquidity: burn LP tokens, receive proportional share. Impermanent loss occurs when token prices diverge \u2014 provide examples: 2x price change = 5.7% IL, 5x = 25.5% IL.\n\nLending protocol patterns: supply assets to earn yield, borrow against collateral. Health factor = (collateral value * liquidation threshold) / borrow value. When health factor drops below 1.0, position is liquidatable. Liquidation bonus (5-15%) incentivizes liquidators. Interest rate models: typically utilization-based \u2014 low utilization = low rates (encourage borrowing), high utilization = high rates (encourage repayment and deposits).\n\nVault patterns (ERC-4626): standardized tokenized vault interface. Deposit underlying token, receive share tokens. `convertToAssets` and `convertToShares` handle the exchange rate. `maxDeposit`, `maxWithdraw` enforce limits. Implements hooks for yield strategy integration. Always round in favor of the vault (against the user) to prevent rounding exploits on deposit/withdraw.\n\nStaking patterns: users deposit tokens, earn rewards over time. Track rewards per share using the \"masterchef\" pattern: `accRewardPerShare += newRewards / totalStaked`. User pending rewards = `(userAmount * accRewardPerShare) - userRewardDebt`. Update `rewardDebt` on every deposit/withdraw. This avoids iterating over all stakers \u2014 O(1) per operation.\n\n## Deployment & Operations\n\nMulti-chain deployment: use `CREATE2` for deterministic addresses across chains (same address on Ethereum, Arbitrum, Base, etc.). Deploy factory contract first, then deploy through factory. HH Ignition supports multi-chain deployment workflows. Verify contracts on each chain's block explorer.\n\nMonitoring post-deployment: use Tenderly or OpenZeppelin Defender for TX monitoring. Set alerts for: large token transfers, AC role changes, proxy upgrades, unusual gas consumption patterns, and contract balance changes. Monitor governance proposals and timelock queued TXs.\n\nEmergency procedures: every production SC should have a circuit breaker (OZ Pausable). Define clear criteria for when to pause (exploit detected, oracle failure, governance attack). Multi-sig (Safe) should control pause function \u2014 require 2-of-3 minimum. Document the emergency response runbook: who can pause, who must be notified, what's the assessment process, and who authorizes unpause."
}