Agent skill

solidity-guide

Solidity/Ethereum guardrails, patterns, and best practices for AI-assisted development. Use when working with Solidity files (.sol), or when the user mentions Solidity/Ethereum/smart contracts. Provides gas optimization patterns, reentrancy prevention, upgradeable contract guidelines, and testing standards specific to this project's coding standards.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/solidity-guide

Metadata

Additional technical details for this skill

author
samuel
version
1.0
category
language
language
solidity
extensions
.sol

SKILL.md

Solidity Guide

Applies to: Solidity 0.8.20+, Ethereum, EVM-compatible chains, DeFi, NFTs

Core Principles

  1. Security First: Every function is a potential attack surface. Assume adversarial callers.
  2. Gas Efficiency: On-chain computation is expensive. Optimize storage access and minimize state changes.
  3. Explicit Over Implicit: Use explicit visibility, explicit types, and named return values.
  4. Immutability by Default: Prefer immutable and constant for values that do not change after deployment.
  5. Standards Compliance: Use OpenZeppelin for ERC standards. Do not roll your own token logic.

Guardrails

Compiler Version

  • ALWAYS specify pragma version range: pragma solidity ^0.8.20;
  • Do NOT use floating pragmas in production (e.g., >=0.8.0). Pin to a minor range.
  • Enable the optimizer with at least 200 runs for production deployments.

Security

  • NEVER use tx.origin for authorization. Use msg.sender.
  • NEVER use selfdestruct in new contracts (deprecated in Dencun).
  • ALWAYS use ReentrancyGuard from OpenZeppelin for functions that make external calls.
  • ALWAYS validate external inputs: check zero addresses, bounds, and array lengths.
  • Use SafeERC20 for token transfers (handles non-standard return values).
  • Emit events for every state-changing operation (required for off-chain indexing).

Gas Optimization

  • Pack storage variables: group uint128, uint64, bool into single 256-bit slots.
  • Use calldata instead of memory for read-only external function parameters.
  • Use custom errors instead of require strings (saves ~50 bytes per error site).
  • Cache storage reads in local variables when accessed more than once.
  • Use unchecked blocks for arithmetic that provably cannot overflow.
  • Prefer mapping over array for large datasets (O(1) vs O(n) lookups).

Access Control

  • Use OpenZeppelin AccessControl or Ownable2Step (not plain Ownable).
  • Separate admin roles: deployer, upgrader, pauser, minter. No single god key.
  • Consider a timelock for sensitive admin operations (TimelockController).

Upgradability

  • Use UUPS proxy pattern (preferred) or Transparent proxy when upgradability is required.
  • NEVER change storage layout ordering in upgraded implementations.
  • Use @openzeppelin/contracts-upgradeable with initializer instead of constructors.
  • ALWAYS include a storage gap: uint256[50] private __gap;
  • Disable initializers in the constructor: _disableInitializers();

Key Patterns

Checks-Effects-Interactions (CEI)

Prevents reentrancy by ordering operations: validate, update state, then call externally.

solidity
function withdraw(uint256 amount) external nonReentrant {
    // 1. CHECKS
    if (amount == 0) revert ZeroAmount();
    if (balances[msg.sender] < amount) revert InsufficientBalance();

    // 2. EFFECTS: update state BEFORE external calls
    balances[msg.sender] -= amount;

    // 3. INTERACTIONS: external calls last
    (bool success, ) = msg.sender.call{value: amount}("");
    if (!success) revert TransferFailed();

    emit Withdrawn(msg.sender, amount);
}

Pull Over Push

Let users withdraw funds instead of pushing payments to them.

solidity
mapping(address => uint256) public pendingWithdrawals;

function claimPayment() external nonReentrant {
    uint256 amount = pendingWithdrawals[msg.sender];
    if (amount == 0) revert NothingToClaim();
    pendingWithdrawals[msg.sender] = 0;

    (bool success, ) = msg.sender.call{value: amount}("");
    if (!success) revert TransferFailed();
    emit PaymentClaimed(msg.sender, amount);
}

Guard Checks with Custom Errors

Custom errors are cheaper than require strings and support typed parameters.

solidity
error Unauthorized(address caller);
error InsufficientBalance(uint256 requested, uint256 available);
error ZeroAddress();

function transfer(address to, uint256 amount) external {
    if (to == address(0)) revert ZeroAddress();
    if (balances[msg.sender] < amount) {
        revert InsufficientBalance(amount, balances[msg.sender]);
    }
    balances[msg.sender] -= amount;
    balances[to] += amount;
    emit Transfer(msg.sender, to, amount);
}

UUPS Proxy Pattern

solidity
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract MyProtocol is UUPSUpgradeable, OwnableUpgradeable {
    uint256 public protocolFee;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() { _disableInitializers(); }

    function initialize(address owner, uint256 fee) external initializer {
        __Ownable_init(owner);
        __UUPSUpgradeable_init();
        protocolFee = fee;
    }

    function _authorizeUpgrade(address) internal override onlyOwner {}

    uint256[49] private __gap;
}

Security Checklist

  • Reentrancy: Apply nonReentrant to external-call functions. Follow CEI even with the guard. Watch for cross-function reentrancy on shared state.
  • Integer Safety: Solidity 0.8+ has overflow checks. Use unchecked only when provably safe.
  • Front-Running: Use commit-reveal for auctions/voting. Add deadline params to swaps. Use block.timestamp, not block.number.
  • Access Control: Never rely on tx.origin. Use Pausable for emergency stops.

Testing

Foundry (forge) -- Recommended

solidity
// test/Vault.t.sol
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {Vault} from "../src/Vault.sol";

contract VaultTest is Test {
    Vault public vault;
    address public alice = makeAddr("alice");

    function setUp() public {
        vault = new Vault();
        vm.deal(alice, 10 ether);
    }

    function test_deposit_updates_balance() public {
        vm.prank(alice);
        vault.deposit{value: 1 ether}();
        assertEq(vault.balances(alice), 1 ether);
    }

    function test_withdraw_reverts_on_insufficient_balance() public {
        vm.prank(alice);
        vm.expectRevert(Vault.InsufficientBalance.selector);
        vault.withdraw(1 ether);
    }

    // Fuzz testing: forge generates random inputs automatically
    function testFuzz_deposit_and_withdraw(uint96 amount) public {
        vm.assume(amount > 0 && amount <= 10 ether);
        vm.startPrank(alice);
        vault.deposit{value: amount}();
        vault.withdraw(amount);
        vm.stopPrank();
        assertEq(vault.balances(alice), 0);
    }
}

Hardhat (alternative)

Use npx hardhat test with Chai matchers and ethers.js for JS/TS projects.

Tooling

bash
forge build && forge test         # Compile and test (Foundry)
forge test -vvvv                  # Verbose with stack traces
forge test --fuzz-runs 10000      # Extended fuzz testing
forge coverage                    # Coverage report
forge fmt                         # Format Solidity files
forge snapshot                    # Gas snapshot for benchmarking
slither .                         # Static analysis (Slither)
myth analyze src/Contract.sol     # Symbolic execution (Mythril)
npx hardhat compile && npx hardhat test  # Hardhat alternative

Pre-Deployment Checklist

  • forge test passes, Slither reports zero high/medium findings
  • Gas snapshot compared (forge snapshot --diff)
  • Storage layout verified for upgradeable contracts
  • Deploy script tested on fork: forge script --fork-url $RPC_URL
  • Admin keys secured (multisig, timelock)

References

  • references/patterns.md -- ERC20, proxy upgrades, gas optimization examples

External References

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results