Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions .github/workflows/check-upstream-abigen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: Check Upstream Abigen Updates

on:
pull_request:
branches:
- main
- "releases/**"
workflow_dispatch:

jobs:
check-upstream:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4

- name: Check latest go-ethereum release
id: upstream
run: |
LATEST=$(curl -s https://api.github.com/repos/ethereum/go-ethereum/releases/latest | jq -r .tag_name)
echo "latest=$LATEST" >> "$GITHUB_OUTPUT"
echo "Latest go-ethereum: $LATEST"

- name: Get current fork version
id: current
run: |
CURRENT=$(grep "Upstream Version:" cmd/generate-bindings/bindings/abigen/FORK_METADATA.md | cut -d: -f2 | tr -d ' ')
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "Current fork version: $CURRENT"

- name: Compare versions
id: compare
run: |
CURRENT="${{ steps.current.outputs.current }}"
LATEST="${{ steps.upstream.outputs.latest }}"

# Extract major.minor version (e.g., "1.16" from "v1.16.0")
CURRENT_MAJOR_MINOR=$(echo "$CURRENT" | sed 's/^v//' | cut -d. -f1,2)
LATEST_MAJOR_MINOR=$(echo "$LATEST" | sed 's/^v//' | cut -d. -f1,2)

echo "Current major.minor: $CURRENT_MAJOR_MINOR"
echo "Latest major.minor: $LATEST_MAJOR_MINOR"

if [ "$CURRENT_MAJOR_MINOR" != "$LATEST_MAJOR_MINOR" ]; then
echo "outdated=true" >> "$GITHUB_OUTPUT"
echo "::warning::Fork has a major version difference. Current: $CURRENT, Latest: $LATEST"
else
echo "outdated=false" >> "$GITHUB_OUTPUT"
echo "Fork is on the same major.minor version ($CURRENT_MAJOR_MINOR)"
fi

- name: Check for recent security-related commits
id: security
run: |
CURRENT="${{ steps.current.outputs.current }}"
echo "Checking for security-related commits since $CURRENT..."

# Search for security-related keywords in commit messages
SECURITY_COMMITS=$(curl -s "https://api.github.com/repos/ethereum/go-ethereum/commits?sha=master&per_page=100" | \
jq -r '[.[] | select(.commit.message | test("security|vulnerability|CVE|exploit"; "i")) | "- \(.commit.message | split("\n")[0]) ([link](\(.html_url)))"] | join("\n")' || echo "")

if [ -n "$SECURITY_COMMITS" ]; then
echo "has_security=true" >> "$GITHUB_OUTPUT"
# Save to file to handle multiline
echo "$SECURITY_COMMITS" > /tmp/security_commits.txt
else
echo "has_security=false" >> "$GITHUB_OUTPUT"
fi

- name: Comment on PR - Outdated
if: steps.compare.outputs.outdated == 'true'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const current = '${{ steps.current.outputs.current }}';
const latest = '${{ steps.upstream.outputs.latest }}';
const hasSecurity = '${{ steps.security.outputs.has_security }}' === 'true';

let securitySection = '';
if (hasSecurity) {
try {
const commits = fs.readFileSync('/tmp/security_commits.txt', 'utf8');
securitySection = `

### ⚠️ Potential Security-Related Commits Detected

${commits}
`;
} catch (e) {
// File might not exist
}
}

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## ⚠️ Abigen Fork Check - Update Available

The forked abigen package is **outdated** and may be missing important updates.

| Version | Value |
|---------|-------|
| **Current Fork** | \`${current}\` |
| **Latest Upstream** | \`${latest}\` |

### Action Required

1. Review [abigen changes in upstream](https://github.com/ethereum/go-ethereum/commits/${latest}/accounts/abi/bind) (only the \`accounts/abi/bind\` directory matters)
2. Compare with our fork in \`cmd/generate-bindings/bindings/abigen/\`
3. If relevant changes exist, sync them and update \`FORK_METADATA.md\`
4. If no abigen changes, just update the version in \`FORK_METADATA.md\` to \`${latest}\`
${securitySection}
### Files to Review

- \`cmd/generate-bindings/bindings/abigen/bind.go\`
- \`cmd/generate-bindings/bindings/abigen/bindv2.go\`
- \`cmd/generate-bindings/bindings/abigen/template.go\`

---
⚠️ **Note to PR author**: This is not something you need to fix. The Platform Expansion team is responsible for maintaining the abigen fork.

cc @smartcontractkit/bix-framework`
});
36 changes: 36 additions & 0 deletions cmd/generate-bindings/bindings/abigen/FORK_METADATA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Abigen Fork Metadata

## Upstream Information

- Source Repository: https://github.com/ethereum/go-ethereum
- Original Package: accounts/abi/bind
- Fork Date: 2025-06-18
- Upstream Version: v1.16.0
- Upstream Commit: 4997a248ab4acdb40383f1e1a5d3813a634370a6

## Modifications

1. Custom Template Support (bindv2.go:300)
- Description: Added `templateContent` parameter to `BindV2()` function signature
- Reason: Enable CRE-specific binding generation with custom templates

2. isDynTopicType Function (bindv2.go:401-408)
- Description: Added template function for event topic type checking
- Registered `isDynTopicType` in the template function map
- Reason: Distinguish hashed versus unhashed indexed event fields for dynamic types (tuples, strings, bytes, slices, arrays)

3. sanitizeStructNames Function (bindv2.go:383-395)
- Reason: Generate cleaner, less verbose struct names in bindings
- Description: Added function to remove contract name prefixes from struct names

4. Copyright Header Addition (bindv2.go:17-18)
- Description: Added SmartContract ChainLink Limited SEZC copyright notice
- Reason: Proper attribution for modifications

## Sync History

- 2025-06-18: Initial fork from v1.16.0

## Security Patches Applied

None yet.
11 changes: 11 additions & 0 deletions cmd/generate-bindings/generate-bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,17 @@ func (h *handler) processAbiDirectory(inputs Inputs) error {
return fmt.Errorf("no .abi files found in directory: %s", inputs.AbiPath)
}

packageNames := make(map[string]bool)
for _, abiFile := range files {
contractName := filepath.Base(abiFile)
contractName = contractName[:len(contractName)-4]
packageName := contractNameToPackage(contractName)
if _, exists := packageNames[packageName]; exists {
return fmt.Errorf("package name collision: multiple contracts would generate the same package name '%s' (contracts are converted to snake_case for package names). Please rename one of your contract files to avoid this conflict", packageName)
}
packageNames[packageName] = true
}

// Process each ABI file
for _, abiFile := range files {
// Extract contract name from filename (remove .abi extension)
Expand Down
41 changes: 41 additions & 0 deletions cmd/generate-bindings/generate-bindings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,47 @@ func TestProcessAbiDirectory_NoAbiFiles(t *testing.T) {
assert.Contains(t, err.Error(), "no .abi files found")
}

func TestProcessAbiDirectory_PackageNameCollision(t *testing.T) {
tempDir, err := os.MkdirTemp("", "generate-bindings-test")
require.NoError(t, err)
defer os.RemoveAll(tempDir)

abiDir := filepath.Join(tempDir, "abi")
outDir := filepath.Join(tempDir, "generated")

err = os.MkdirAll(abiDir, 0755)
require.NoError(t, err)

abiContent := `[{"type":"function","name":"test","inputs":[],"outputs":[]}]`

// "TestContract" -> "test_contract"
// "test_contract" -> "test_contract"
err = os.WriteFile(filepath.Join(abiDir, "TestContract.abi"), []byte(abiContent), 0600)
require.NoError(t, err)
err = os.WriteFile(filepath.Join(abiDir, "test_contract.abi"), []byte(abiContent), 0600)
require.NoError(t, err)

logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
runtimeCtx := &runtime.Context{
Logger: &logger,
}
handler := newHandler(runtimeCtx)

inputs := Inputs{
ProjectRoot: tempDir,
ChainFamily: "evm",
Language: "go",
AbiPath: abiDir,
PkgName: "bindings",
OutPath: outDir,
}

err = handler.processAbiDirectory(inputs)
fmt.Println(err.Error())
require.Error(t, err)
require.Equal(t, err.Error(), "package name collision: multiple contracts would generate the same package name 'test_contract' (contracts are converted to snake_case for package names). Please rename one of your contract files to avoid this conflict")
}

func TestProcessAbiDirectory_NonExistentDirectory(t *testing.T) {
logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
runtimeCtx := &runtime.Context{
Expand Down
Loading