Web38 min read

Solidity & Smart Contracts

Write secure, gas-efficient Solidity contracts using OpenZeppelin, Hardhat testing, and common patterns like Ownable, ERC-20, and ERC-721.

solidityethereumsmart-contractsweb3

Solidity is the primary language for Ethereum smart contracts. Contracts are immutable once deployed and handle real financial value-security and gas efficiency are non-negotiable. Every vulnerability is a potential exploit.

Security first. Use audited OpenZeppelin contracts over rolling your own. Test extensively with Hardhat before deploying. Understand gas costs. Follow checks-effects-interactions pattern religiously to prevent reentrancy.

Workflow

  1. Set up Hardhat development environmentHardhat provides local Ethereum node, testing with ethers.js/chai, and deployment scripts all in one.
  2. Write contracts with OpenZeppelin base classesOpenZeppelin contracts are battle-tested, audited, and community-reviewed-building on them eliminates reinventing proven patterns.
  3. Apply checks-effects-interactions patternReentrancy attacks drain contracts by re-entering before state updates. The DAO hack ($60M) exploited this.
  4. Optimize for gasGas costs real money. Inefficient contracts can become unusable when gas prices spike.
  5. Test thoroughly with Hardhat and ChaiBugs in deployed contracts can't be patched-they can only be mitigated. Comprehensive tests are the last line of defense before audit.

Output Format

Solidity contracts with OpenZeppelin base, Hardhat test suite at 100% branch coverage, gas benchmarks, deployment scripts, Slither report.

Examples

ERC-20 token: `contract MyToken is ERC20, Ownable { constructor() ERC20("MyToken", "MTK") Ownable(msg.sender) { _mint(msg.sender, 1000000 * 10**18); } function mint(address to, uint256 amount) external onlyOwner { _mint(to, amount); } }`
Checks-effects-interactions: `function withdraw(uint256 amount) external nonReentrant { require(balances[msg.sender] >= amount, "Insufficient"); balances[msg.sender] -= amount; (bool ok, ) = msg.sender.call{value: amount}(""); require(ok, "Transfer failed"); }`
Gas optimization: pack variables: `uint128 price; uint128 quantity;` → one storage slot vs two separate uint256s

Edge Cases & Gotchas

  • Integer overflow: Solidity 0.8+ throws on overflow by default; use unchecked{} only when you've mathematically proven no overflow is possible
  • Block.timestamp manipulation: miners can adjust by ~15 seconds; don't use it for precise timing or as a randomness source
  • Proxy patterns for upgradeable contracts: use OpenZeppelin TransparentUpgradeableProxy; avoid storage collision between proxy and implementation

When to Break These Rules

  • For non-financial contracts (NFT metadata, governance voting), audit requirements can be relaxed but testing and event emission remain essential.

Reference Files

  • OpenZeppelin Contracts: https://docs.openzeppelin.com/contracts/
  • Hardhat: https://hardhat.org/docs
  • SWC Registry (vulnerabilities): https://swcregistry.io/
  • Slither static analyzer: https://github.com/crytic/slither

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.