Skip to content

Commit 804da6c

Browse files
committed
feat: scripts/bytecode-diff-no-metadata.sh
1 parent 6009501 commit 804da6c

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Bytecode Comparison Tool (Metadata-Stripped)
4+
#
5+
# Compares functional bytecode between two contract artifact directories,
6+
# excluding metadata hashes to focus on actual code differences.
7+
#
8+
# This is an enhanced version of bytecode-diff.sh that strips Solidity
9+
# metadata hashes before comparison, allowing you to identify functional
10+
# differences vs compilation environment differences.
11+
#
12+
# Usage: ./bytecode-diff-no-metadata.sh <dir1> <dir2>
13+
# Example: ./bytecode-diff-no-metadata.sh /path/to/old/artifacts /path/to/new/artifacts
14+
#
15+
# Metadata Pattern Stripped: a264697066735822[64 hex chars]64736f6c63[6 hex chars]
16+
# This represents: "ipfs" + IPFS hash + "solc" + Solidity version
17+
#
18+
19+
set -euo pipefail
20+
21+
if [ "$#" -ne 2 ]; then
22+
echo "Usage: $0 <artifacts-dir-1> <artifacts-dir-2>"
23+
echo "This script compares bytecode excluding metadata hashes"
24+
echo "Metadata hashes are embedded by Solidity and don't affect contract functionality"
25+
exit 1
26+
fi
27+
28+
DIR1="$1"
29+
DIR2="$2"
30+
31+
TMPDIR=$(mktemp -d)
32+
33+
# Function to extract bytecode and strip metadata hash
34+
strip_metadata() {
35+
local file="$1"
36+
local out="$2"
37+
38+
# Extract bytecode
39+
local bytecode=$(jq -r '.bytecode' "$file")
40+
41+
# Remove 0x prefix if present
42+
bytecode=${bytecode#0x}
43+
44+
# Strip metadata hash - Solidity metadata follows pattern:
45+
# a264697066735822<32-byte-hash>64736f6c63<version>
46+
# Where:
47+
# - a264697066735822 = "ipfs" in hex + length prefix
48+
# - 64736f6c63 = "solc" in hex
49+
# We'll remove everything from the last occurrence of a264697066735822 to the end
50+
51+
# Use sed to remove the metadata pattern from the end
52+
# This removes everything from a264697066735822 (ipfs marker) to the end
53+
bytecode=$(echo "$bytecode" | sed 's/a264697066735822.*$//')
54+
55+
# Output in chunks of 64 characters for easier diffing
56+
echo "$bytecode" | fold -w 64 > "$out"
57+
}
58+
59+
echo "🔍 Comparing bytecode (excluding metadata hashes) for repository contracts..."
60+
echo "DIR1: $DIR1"
61+
echo "DIR2: $DIR2"
62+
echo
63+
64+
# Create lists of contracts in each directory
65+
contracts1="$TMPDIR/contracts1.txt"
66+
contracts2="$TMPDIR/contracts2.txt"
67+
68+
find "$DIR1/contracts" -type f -name '*.json' ! -name '*dbg.json' ! -name 'I*.json' 2>/dev/null | while read -r file; do
69+
rel_path="${file#$DIR1/contracts/}"
70+
echo "$rel_path"
71+
done | sort > "$contracts1"
72+
73+
find "$DIR2/contracts" -type f -name '*.json' ! -name '*dbg.json' ! -name 'I*.json' 2>/dev/null | while read -r file; do
74+
rel_path="${file#$DIR2/contracts/}"
75+
echo "$rel_path"
76+
done | sort > "$contracts2"
77+
78+
# Find common contracts
79+
common_contracts="$TMPDIR/common.txt"
80+
comm -12 "$contracts1" "$contracts2" > "$common_contracts"
81+
82+
common_count=$(wc -l < "$common_contracts")
83+
echo "📊 Found $common_count common contracts to compare"
84+
echo
85+
86+
if [ "$common_count" -eq 0 ]; then
87+
echo "❌ No common contracts found!"
88+
exit 1
89+
fi
90+
91+
# Compare bytecode for common contracts
92+
diff_count=0
93+
same_count=0
94+
no_bytecode_count=0
95+
96+
# Store results for summary
97+
same_contracts="$TMPDIR/same.txt"
98+
diff_contracts="$TMPDIR/different.txt"
99+
touch "$same_contracts" "$diff_contracts"
100+
101+
echo "Processing contracts..."
102+
103+
while read -r contract; do
104+
file1="$DIR1/contracts/$contract"
105+
file2="$DIR2/contracts/$contract"
106+
107+
# Extract and strip metadata
108+
tmp1="$TMPDIR/1"
109+
tmp2="$TMPDIR/2"
110+
111+
strip_metadata "$file1" "$tmp1"
112+
strip_metadata "$file2" "$tmp2"
113+
114+
# Skip if no bytecode (interfaces, abstract contracts)
115+
if [ ! -s "$tmp1" ] || [ "$(wc -c < "$tmp1")" -le 3 ]; then
116+
no_bytecode_count=$((no_bytecode_count + 1))
117+
continue
118+
fi
119+
120+
contract_name=$(jq -r '.contractName // "Unknown"' "$file1" 2>/dev/null || echo "Unknown")
121+
122+
if ! diff -q "$tmp1" "$tmp2" > /dev/null; then
123+
diff_count=$((diff_count + 1))
124+
echo "$contract ($contract_name)" >> "$diff_contracts"
125+
echo "🧨 $contract"
126+
else
127+
same_count=$((same_count + 1))
128+
echo "$contract ($contract_name)" >> "$same_contracts"
129+
echo "$contract"
130+
fi
131+
done < "$common_contracts"
132+
133+
echo
134+
echo "📋 SUMMARY LISTS:"
135+
echo
136+
echo "✅ FUNCTIONALLY IDENTICAL ($same_count contracts):"
137+
if [ -s "$same_contracts" ]; then
138+
cat "$same_contracts" | sed 's/^/ - /'
139+
else
140+
echo " (none)"
141+
fi
142+
143+
echo
144+
echo "🧨 FUNCTIONAL DIFFERENCES ($diff_count contracts):"
145+
if [ -s "$diff_contracts" ]; then
146+
cat "$diff_contracts" | sed 's/^/ - /'
147+
else
148+
echo " (none)"
149+
fi
150+
151+
echo
152+
echo "📊 Final Summary:"
153+
echo " Total contracts compared: $((same_count + diff_count))"
154+
echo " No bytecode (interfaces/abstract): $no_bytecode_count"
155+
echo " Functionally identical: $same_count"
156+
echo " Functional differences: $diff_count"
157+
158+
if [ "$diff_count" -eq 0 ]; then
159+
echo
160+
echo "🎉 SUCCESS: All contracts are functionally identical!"
161+
echo " The previous differences were only in metadata hashes."
162+
else
163+
echo
164+
echo "⚠️ ATTENTION: Found $diff_count contracts with functional differences!"
165+
echo " These contracts have actual code changes that affect functionality."
166+
fi
167+
168+
rm -rf "$TMPDIR"

0 commit comments

Comments
 (0)