|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# This script checks if any Solidity contract/library in the `src/` folder |
| 4 | +# exceeds the EIP-170 contract runtime size limit (24,576 bytes) |
| 5 | +# and the EIP-3860 init code size limit (49,152 bytes). |
| 6 | +# Intended for use in CI (e.g., GitHub Actions) with Foundry. |
| 7 | +# Exits 1 and prints the list of exceeding contracts if violations are found. |
| 8 | +# NOTE: This script requires Bash (not sh or dash) due to use of mapfile and [[ ... ]]. |
| 9 | + |
| 10 | +set -euo pipefail |
| 11 | + |
| 12 | +command -v jq >/dev/null 2>&1 || { echo >&2 "jq is required but not installed."; exit 1; } |
| 13 | +command -v forge >/dev/null 2>&1 || { echo >&2 "forge is required but not installed."; exit 1; } |
| 14 | + |
| 15 | +# Gather contract and library names from src/ |
| 16 | +# Only matches [A-Za-z0-9_] in contract/library names (no special characters) |
| 17 | +if [[ -d src/ ]]; then |
| 18 | + mapfile -t contracts < <(grep -rE '^(contract|library) ' src/ 2>/dev/null | sed -E 's/.*(contract|library) ([A-Za-z0-9_]+).*/\2/') |
| 19 | +else |
| 20 | + contracts=() |
| 21 | +fi |
| 22 | + |
| 23 | +# Exit early if none found |
| 24 | +if [[ ${#contracts[@]} -eq 0 ]]; then |
| 25 | + echo "No contracts or libraries found in src/." |
| 26 | + exit 0 |
| 27 | +fi |
| 28 | + |
| 29 | +# Build the contracts, get size info as JSON (ignore non-zero exit to always parse output) |
| 30 | +forge clean || true |
| 31 | +forge build --sizes --json | jq . > contract_sizes.json || true |
| 32 | + |
| 33 | +# Validate JSON output |
| 34 | +if ! jq empty contract_sizes.json 2>/dev/null; then |
| 35 | + echo "forge build did not return valid JSON. Output:" |
| 36 | + cat contract_sizes.json |
| 37 | + exit 1 |
| 38 | +fi |
| 39 | + |
| 40 | +if jq -e '. == {}' contract_sizes.json >/dev/null; then |
| 41 | + echo "forge did not find any contracts. forge build:" |
| 42 | + # This usually means build failure |
| 43 | + forge build |
| 44 | + exit 1 |
| 45 | +fi |
| 46 | + |
| 47 | +json=$(cat contract_sizes.json) |
| 48 | + |
| 49 | +# Filter JSON: keep only contracts/libraries from src/ |
| 50 | +json=$(echo "$json" | jq --argjson keys "$(printf '%s\n' "${contracts[@]}" | jq -R . | jq -s .)" ' |
| 51 | + to_entries |
| 52 | + | map(select(.key as $k | $keys | index($k))) |
| 53 | + | from_entries |
| 54 | +') |
| 55 | + |
| 56 | +# Find all that violate the EIP-170 runtime size limit (24,576 bytes) |
| 57 | +exceeding_runtime=$(echo "$json" | jq -r ' |
| 58 | + to_entries |
| 59 | + | map(select(.value.runtime_size > 24576)) |
| 60 | + | .[] |
| 61 | + | "\(.key): \(.value.runtime_size) bytes (runtime size)"' |
| 62 | +) |
| 63 | + |
| 64 | +# Find all that violate the EIP-3860 init code size limit (49,152 bytes) |
| 65 | +exceeding_initcode=$(echo "$json" | jq -r ' |
| 66 | + to_entries |
| 67 | + | map(select(.value.init_size > 49152)) |
| 68 | + | .[] |
| 69 | + | "\(.key): \(.value.init_size) bytes (init code size)"' |
| 70 | +) |
| 71 | + |
| 72 | +# Initialize status |
| 73 | +status=0 |
| 74 | + |
| 75 | +if [[ -n "$exceeding_runtime" ]]; then |
| 76 | + echo "ERROR: The following contracts exceed EIP-170 runtime size (24,576 bytes):" |
| 77 | + echo "$exceeding_runtime" |
| 78 | + status=1 |
| 79 | +fi |
| 80 | + |
| 81 | +if [[ -n "$exceeding_initcode" ]]; then |
| 82 | + echo "ERROR: The following contracts exceed EIP-3860 init code size (49,152 bytes):" |
| 83 | + echo "$exceeding_initcode" |
| 84 | + status=1 |
| 85 | +fi |
| 86 | + |
| 87 | +if [[ $status -eq 0 ]]; then |
| 88 | + echo "All contracts are within the EIP-170 runtime and EIP-3860 init code size limits." |
| 89 | +fi |
| 90 | + |
| 91 | +# Clean up temporary file |
| 92 | +rm -f contract_sizes.json |
| 93 | + |
| 94 | +# Exit with appropriate status |
| 95 | +exit $status |
| 96 | + |
0 commit comments