Skip to content

Commit 94073a5

Browse files
ci: add Solidity contract size checks (#203)
1 parent c33b93d commit 94073a5

File tree

3 files changed

+134
-1
lines changed

3 files changed

+134
-1
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Solidity Contract Size Check
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
branches: ["main"]
8+
9+
jobs:
10+
check_contract_size:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
with:
17+
submodules: recursive
18+
19+
- name: Install Foundry
20+
uses: foundry-rs/foundry-toolchain@v1
21+
with:
22+
version: v1.2.3
23+
24+
- name: Install Dependencies
25+
run: forge install
26+
27+
- name: Install jq
28+
run: sudo apt-get install -y jq
29+
30+
- name: Check contract size
31+
run: make contract-size-check

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,10 @@ extract-abis:
4949
@find out -type f -name '*.json' | while read file; do \
5050
name=$$(basename "$${file%.*}"); \
5151
jq '.abi' "$${file}" > "abi/$${name}.abi.json"; \
52-
done
52+
done
53+
54+
# Contract size check
55+
.PHONY: contract-size-check
56+
contract-size-check:
57+
@echo "Checking contract sizes..."
58+
bash tools/check-contract-size.sh

tools/check-contract-size.sh

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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

Comments
 (0)