chore(uniswapx-sdk): add ABI provenance manifest + deterministic refresh tooling - #649
chore(uniswapx-sdk): add ABI provenance manifest + deterministic refresh tooling#649claude[bot] wants to merge 2 commits into
Conversation
…esh tooling The files in abis/ are compiled contract artifacts hand-copied from upstream (mainly Uniswap/UniswapX, plus Uniswap/permit2 and a few third-party ABIs) and turned into the src/contracts ethers bindings by typechain at build time. Nothing recorded which upstream commit each artifact came from and nothing re-derives them, so they can silently drift out of sync with the deployed contracts. This is the same class of problem as the liquidity-launcher stale-bytecode incident (#648), with a smaller blast radius (the embedded bytecode only deploys test mocks). - abis/PROVENANCE.md: records the upstream repo, source path, contract, and solc version of every artifact, and which are script-refreshed vs hand-maintained. - scripts/abis.manifest.json: machine-readable version of the same, driving the script. - scripts/regenerate-abis.ts: one-command deterministic refresh of the UniswapX/permit2 artifacts from local checkouts (forge build -> copy mapped out/ artifacts -> typechain), fails loudly and writes nothing partial, with FORGE_BIN/SKIP_* escape hatches. - package.json: regenerate:abis script alias. - README: "Maintaining the contract ABIs" section documenting the drift risk and refresh. No runtime behavior change: no abis/*.json or src/contracts files are modified, and the abis are build-time inputs not shipped in the published package.
|
● Reviewed · against Note Approved. Adds an ABI provenance manifest, a AssessmentBuild-time maintainer tooling with no runtime surface. All subprocess calls use Iteration history · 2 reviews2026-07-17 03:02 UTC · ✅ approved · 0 findings ·
|
| skippedRepos.push(`${key} (set ${spec.envVar} to refresh its ${artifacts.length} artifact(s))`) | ||
| continue | ||
| } | ||
| const commit = process.env[`${key.toUpperCase()}_COMMIT`] |
There was a problem hiding this comment.
🔵 info · correctness — the commit env var is derived from the manifest key (${key.toUpperCase()}_COMMIT) while the repo path comes from spec.envVar. These match today only because uniswapx/permit2 happen to align with the *_REPO prefixes. A future entry whose key differs from its envVar prefix would silently ignore the documented <REPO>_COMMIT pin. Derive both from spec.envVar (swap the _REPO suffix) to keep them in lockstep.
There was a problem hiding this comment.
Note
✅ Approved — see full review in the sticky comment ↑
Graphite Automations"Request reviewers once CI passes on sdks monorepo" took an action on this PR • (07/17/26)2 reviewers were added to this PR based on Siyu Jiang (See-You John)'s automation. |
Derive both the repo-path var and the per-entry <REPO>_COMMIT var from a single source (spec.envVar) so they cannot drift for a future manifest entry whose key differs from its envVar prefix. Behavior is unchanged for the current uniswapx/permit2 entries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hkw9wpfbpaigd258uUmkYr
Requested by Bruno Nunes · Slack thread
PR Scope
chore(uniswapx-sdk):— internal tooling/docs change, no runtime behavior change and no release (empty changeset included). Noabis/*.jsonorsrc/contractsfiles are modified.Description
The JSON files in
sdks/uniswapx-sdk/abisare compiled contract artifacts (and a few hand-authored ABIs) copied from upstream contract repos — mainly Uniswap/UniswapX, plus Uniswap/permit2 and a handful of third-party/interface ABIs. Thetypechainbuild step turns them into the ethers bindings committed undersrc/contracts, which are then compiled intodist.abis/itself is a build-time input only (not in"files", so never shipped directly).Before: the ABIs were hand-copied with no record of which upstream commit each came from and no refresh path. Investigating this package confirmed the smell:
0.8.16→0.8.30, come from at least two repos, and mix Foundry and Hardhat artifact formats — clear evidence of piecemeal manual copying over time. Their in-repo git history is a single squashed migration commit, so the source commits are unrecoverable.v2-sdk/v3-sdkuse (recompute a hash from a published@uniswap/*-corenpm artifact and assert equality), because no UniswapX contract artifact is published to npm to diff against (verified:@uniswap/uniswapx,@uniswap/uniswapx-contracts, etc. all 404).This is the same mechanism as the liquidity-launcher stale-bytecode incident fixed in #648 — a hand-copied upstream artifact pinned to nothing, with no refresh path. The blast radius here is smaller (the embedded bytecode only deploys mocks in integration tests; the ABIs drive typed encode/decode rather than a CREATE2 prediction holding user funds), but the treatment is the same: record provenance + make refresh a single deterministic command.
After:
abis/PROVENANCE.md— records, for every artifact, its upstream repo, source path, contract, solc version, and whether it is script-refreshed or hand-maintained; explains the drift risk and links the chore(liquidity-launcher-sdk): add deterministic lock-bytecode regeneration tooling #648 incident. Also documents a pre-existing orphan (see Follow Ups).scripts/abis.manifest.json— machine-readable version of the same, driving the script (15scriptedfoundry artifacts from UniswapX/permit2; 7scripted: falsethird-party/interface/hand-authored ABIs left untouched).scripts/regenerate-abis.ts— a deterministic, one-command regenerator. Given local checkouts (UNISWAPX_REPO/PERMIT2_REPO, optional*_COMMITpins), it checks out the commit, initializes submodules, runsforge build, copies the mappedout/<Contract>.sol/<Contract>.jsonartifacts intoabis/, regeneratessrc/contractsvia typechain, and records the resolved commit back into the manifest andPROVENANCE.md. It validates every artifact before writing anything, fails loudly and writes nothing partial ifforgeis missing / a build fails / an artifact is absent, and exposes escape hatches (FORGE_BIN,SKIP_CHECKOUT,SKIP_SUBMODULES,SKIP_BUILD). It uses only Node builtins — no new dependency.package.json—regenerate:abisscript alias.README.md— a "Maintaining the contract ABIs" section: what the ABIs are, why they silently go stale (with chore(liquidity-launcher-sdk): add deterministic lock-bytecode regeneration tooling #648 as motivation), and the exact refresh command.No ABI bytes and no generated bindings change in this PR; the tooling is for future refreshes.
How Has This Been Tested?
SKIP_BUILDmode against synthesizedout/trees reconstructed from the current artifacts: the script validated and refreshed all 15 scripted artifacts, then regeneratedsrc/contractsvia typechain with zero diff — typechain is idempotent against the committed ABIs. The only artifact diffs were formatting-only (verified:abiandbytecode.objectbyte-identical toHEADfor all 15); those rewrites were then reverted so this PR leavesabis/untouched.out/artifact, and a deployable contract with empty bytecode.bun x tsc -p tsconfig.base.json --noEmit— clean (after building the@uniswap/permit2-sdkworkspace dep).bun run lint— clean.bun test src/— 328 pass / 0 fail.Are there any breaking changes?
No. No public API, types, ABIs, or generated bindings change;
distoutput is unaffected (only new tooling/docs and a README/package.jsonedit).(Optional) Follow Ups
Not in this PR:
out/*.json). Once that exists, this SDK could adopt the same real drift-checkv2-sdk/v3-sdkalready use — recompute a hash from the published artifact and assert equality, so a changed upstream ABI fails CI onbun install. This manifest + script is the interim, deterministic bridge. Worth filing an issue on Uniswap/UniswapX to publish those artifacts.src/contractscontainsDutchOrderReactor.tsandDutchLimitOrderReactor.ts(plus their factories) that have no backing ABI inabis/— leftovers from ABIs removed without a clean regenerate (typechain does not delete). A cleanrm -rf src/contracts && typechainreproduces all 47 other typings byte-for-byte but drops these 4, andEventWatcher.tsstill imports theFillEventtype from../contracts/DutchOrderReactor, so removing them requires a small code change. Left as a follow-up; the refresh script is additive (like the build's owntypechain) and does not touch them.abis/formatting is inconsistent (12 of 22 files don't round-trip through a single JSON pretty-print), a side effect of piecemeal hand-copying. The firstforge-backed run of the new script will normalize this — formatting-only, no semantic change — and is best done as its own reviewed refresh commit.Generated by Claude Code