Skip to content
Merged
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
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
- package-ecosystem: npm
directory: /
schedule:
interval: monthly
groups:
dev:
dependency-type: development
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
# The lowest version `engines` allows, the active LTS, and the newest line.
node: ["22.13.0", "24", "26"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: ${{ matrix.node }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm test
- run: pnpm build
# The package must keep loading from CommonJS.
- run: node -e "require('./dist/index.js')"
51 changes: 51 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Publish

# Run by hand: `gh workflow run publish.yml -f dist-tag=alpha`. npm trusts
# this workflow through trusted publishing (OIDC), so no npm token is stored,
# and the package gets a provenance attestation linking it to this repository
# and commit. It only stages the release: nothing is public until a maintainer
# runs `npm stage approve <stage-id>` with 2FA.
on:
workflow_dispatch:
inputs:
dist-tag:
description: npm dist-tag
required: true
type: choice
options: [alpha, latest]

permissions:
contents: read

jobs:
publish:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: npm
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
# The newest Node 24, for its npm: `npm stage` needs npm 11.15.0+ (trusted
# publishing alone needs 11.5.1+). No registry-url: trusted publishing needs no .npmrc.
node-version: "24"
check-latest: true
- run: pnpm install --frozen-lockfile
- name: Check npm supports staged publishing
run: |
npm --version
# Compares versions part by part as numbers, so 11.9 sorts before 11.15.
node -e 'if (process.argv[1].localeCompare("11.15.0", "en", { numeric: true }) < 0) {
console.error(`npm ${process.argv[1]} has no "npm stage"; it needs 11.15.0 or newer.`);
process.exit(1);
}' "$(npm --version)"
# prepublishOnly runs lint and tests; prepack builds. Prints the stage id.
- run: npm stage publish --tag "$DIST_TAG"
env:
DIST_TAG: ${{ inputs.dist-tag }}
35 changes: 35 additions & 0 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Scorecard

on:
push:
branches: [main]
schedule:
# Mondays 16:00 UTC: 10:00 CST (11:00 during daylight time; cron is UTC only).
- cron: "0 16 * * 1"
workflow_dispatch:

permissions: read-all

jobs:
analysis:
runs-on: ubuntu-latest
permissions:
security-events: write
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ossf/scorecard-action@fa7117db83c52d787ae5a89f350999701505d78e # v2.5.0
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: scorecard
path: results.sarif
retention-days: 5
- uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
sarif_file: results.sarif
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"trailingComma": "all",
"printWidth": 100,
"tabWidth": 2,
"endOfLine": "lf"
"endOfLine": "lf",
"proseWrap": "never"
}
25 changes: 16 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
# Changelog

## 0.2.0-alpha.1

- CLI: `slotdiff <old> <new>`, where each side is a contract in your build info or an address (a proxy, read as what it runs now, or an implementation; needs `--rpc` and `ETHERSCAN_API_KEY`). Checks the storage layout, the ERC-7201 slots and OpenZeppelin's safety rules for `<new>`, with the proxy kind taken from `<old>`'s implementation, so a UUPS implementation missing `upgradeToAndCall` fails. Exits 0 when every check passed, 1 when one failed, 2 when something could not be checked. `ETHERSCAN_API_KEY` is sent only to Etherscan, never to `--explorer-url`.
- `findBuildInfo`: reads Foundry, Hardhat 2 and Hardhat 3 build info. The build is chosen by content: only one whose target and imports still match the files on disk, so the stale files Foundry keeps after each edit are never used. Its `input` holds only what the target imports.
- ERC-7201 namespaces are compared by the slot the code really uses, not only the annotation: a namespace whose slot moves fails, whatever the annotations say, and a corrected annotation over an unchanged slot passes (`SourceAnalysis.namespaces`, `checkNamespaces`).
- `analyzeSource`: `layoutFromSource` plus OpenZeppelin's safety check from one compile, as the CLI runs it; `safety({ kind })` takes the proxy kind being upgraded.
- Explorer errors no longer repeat fetch's error text, which can quote the request URL and with it the API key.
- CLI colors and a banner on an interactive terminal only (on runs, `--help` and `--version`); in a pipe or CI the output stays plain and `--version` prints just the version. `NO_COLOR` and `FORCE_COLOR` are honored. Built on Node's `util.styleText`, so no new dependency.
- **Breaking:** Node 22.13+ (was 22.12+), for `util.styleText`'s terminal detection.
- Releases are published from GitHub Actions through npm trusted publishing, staged for a maintainer's 2FA approval, with a provenance attestation linking each version to its commit.
- `SECURITY.md`: how to report a vulnerability privately, and what slotdiff touches (network, processes, files, environment). The README and `--help` now say plainly that a pass is no guarantee and that slotdiff comes without warranty.

## 0.1.0-alpha.1

First release.

- `resolveProxyLayout` / `resolveLayout`: the storage layout of the code a proxy (ERC-1967 or
beacon) runs now, rebuilt from verified standard-json source on any Etherscan-compatible explorer
and proven against the deployed code.
- `resolveProxyLayout` / `resolveLayout`: the storage layout of the code a proxy (ERC-1967 or beacon) runs now, rebuilt from verified standard-json source on any Etherscan-compatible explorer and proven against the deployed code.
- `proveLocalBuild`: a layout record from your own build, only when it is the deployed build.
- `diffLayouts`: OpenZeppelin's storage comparison, including ERC-7201 namespaced storage.
- `LayoutRecord`s are plain JSON that slotdiff never writes. Load them back with `verifyRecord`
(checked against the chain id and deployed code) or `parseRecord` (offline).
- A match proves a layout only when the build embeds solc's metadata hash. Builds with
`bytecodeHash: "none"` or `appendCBOR: false` are reported as `code-only` and refused.
- `getSolc`: native solc from a cache (Hardhat's included) or a checksum-verified download.
Accepts release versions such as `0.8.24`, as Foundry's build info records them.
- `LayoutRecord`s are plain JSON that slotdiff never writes. Load them back with `verifyRecord` (checked against the chain id and deployed code) or `parseRecord` (offline).
- A match proves a layout only when the build embeds solc's metadata hash. Builds with `bytecodeHash: "none"` or `appendCBOR: false` are reported as `code-only` and refused.
- `getSolc`: native solc from a cache (Hardhat's included) or a checksum-verified download. Accepts release versions such as `0.8.24`, as Foundry's build info records them.
- `layoutFromSource` accepts build-info input from Foundry and Hardhat.
- Loads from ESM and CommonJS. Node 22.12+.
Loading
Loading