Skip to content

Commit

Permalink
feat: check interface differences in CI
Browse files Browse the repository at this point in the history
Adds a new check script to contracts-bedrock that verifies that
interfaces being added to the repository accurately represent
the interface of the contract that the interface was generated
for.
  • Loading branch information
smartcontracts committed Aug 30, 2024
1 parent 7373ce7 commit a898242
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,11 @@ jobs:
command: |
just snapshots-check || echo "export SNAPSHOTS_STATUS=1" >> "$BASH_ENV"
working_directory: packages/contracts-bedrock
- run:
name: interfaces
command: |
just interfaces-check || echo "export INTERFACES_STATUS=1" >> "$BASH_ENV"
working_directory: packages/contracts-bedrock
- run:
name: size check
command: |
Expand Down Expand Up @@ -650,6 +655,10 @@ jobs:
echo "Snapshots check failed, see job output for details."
FAILED=1
fi
if [[ "$INTERFACES_STATUS" -ne 0 ]]; then
echo "Interface check failed, see job output for details."
FAILED=1
fi
if [[ "$SIZE_CHECK" -ne 0 ]]; then
echo "Contract(s) exceed size limit, see job output for details."
FAILED=1
Expand Down
3 changes: 3 additions & 0 deletions packages/contracts-bedrock/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ snapshots-no-build: snapshots-abi-storage kontrol-summary-fp kontrol-summary
snapshots-check:
./scripts/checks/check-snapshots.sh

interfaces-check:
./scripts/checks/check-interfaces.sh

semver-lock:
forge script scripts/autogen/SemverLock.s.sol

Expand Down
109 changes: 109 additions & 0 deletions packages/contracts-bedrock/scripts/checks/check-interfaces.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
set -euo pipefail

# This script checks for ABI consistency between interfaces and their corresponding contracts.
# It compares the ABIs of interfaces (files starting with 'I') with their implementation contracts,
# excluding constructors and certain predefined files. The script reports any differences found
# and exits with an error if inconsistencies are detected.

# Grab the directory of the contracts-bedrock package
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
CONTRACTS_BASE=$(dirname "$(dirname "$SCRIPT_DIR")")

# Define the files to exclude (patterns can be used)
EXCLUDE_FILES=(
# External dependencies
"IMulticall3"
"IERC20"
"IERC721"
"IERC721Enumerable"
"IERC721Metadata"
"IERC721Upgradeable"
"IERC721Receiver"
"IERC1271"
"IERC165"
"IVotes"
"IBeacon"
"IProxyCreationCallback"
"IAutomate"
"IGelato1Balance"

# EAS
"IEAS"
"ISchemaResolver"
"ISchemaRegistry"

# TODO: Interfaces that need to be fixed
"IDisputeGameFactory"
"IPreimageOracle"
"IOptimismMintableERC721"
"IFaultDisputeGame"
"IInitializable"
"IAddressManager"
"IDelayedWETH"
"IWETH"
"IAnchorStateRegistry"
"ICrossL2Inbox"
"IL2ToL2CrossDomainMessenger"
)

# Find all JSON files in the forge-artifacts folder
JSON_FILES=$(find "$CONTRACTS_BASE/forge-artifacts" -type f -name "*.json")

# Initialize a flag to track if any differences are detected
differences_detected=false

# Iterate over all JSON files
for interface_file in $JSON_FILES; do
# Extract contract kind and name
contract_kind=$(jq -r '.ast.nodes[] | select(.nodeType == "ContractDefinition") | .contractKind' < "$interface_file")
contract_name=$(jq -r '.ast.nodes[] | select(.nodeType == "ContractDefinition") | .name' < "$interface_file")

# Skip excluded files
skip=false
for exclude in "${EXCLUDE_FILES[@]}"; do
if [[ "$interface_file" == *"$exclude"* ]]; then
skip=true
break
fi
done

# Continue to the next file if it should be skipped
if [ "$skip" = true ]; then
continue
fi

# If contract kind is "interface" and name starts with "I"
if [[ "$contract_kind" == "interface" && "$contract_name" == I* ]]; then
# Construct the corresponding contract name by removing the leading "I"
contract_basename=${contract_name:1}
corresponding_contract_file="$CONTRACTS_BASE/forge-artifacts/$contract_basename.sol/$contract_basename.json"

# Check if the corresponding contract file exists
if [ -f "$corresponding_contract_file" ]; then
# Extract and compare ABIs excluding constructors
interface_abi=$(jq '[.abi[] | select(.type != "constructor")]' < "$interface_file")
contract_abi=$(jq '[.abi[] | select(.type != "constructor")]' < "$corresponding_contract_file")

# Use jq to compare the ABIs
diff_result=$(diff -u <(echo "$interface_abi" | jq -S .) <(echo "$contract_abi" | jq -S .))

if [ -n "$diff_result" ]; then
echo "Differences found in ABI between $contract_name and $contract_basename:"
echo "$diff_result"
differences_detected=true
fi
fi
fi
done

# Fail the script if any differences were detected
if [ "$differences_detected" = true ]; then
echo "Differences detected in ABI comparisons. Please review the differences above."
echo "If the interface is an external dependency or should otherwise be excluded from this"
echo "check, add the interface name to the EXCLUDE_FILES list in the script. This will prevent"
echo "the script from comparing it against a corresponding contract."
exit 1
else
exit 0
fi

0 comments on commit a898242

Please sign in to comment.