← back to lessons

Rule-38.4 sub-lesson: consolidation/delete commits - signature-diff is the discriminator

Banked 2026-06-12 from Compound Comet commit db72b0c ("Regular Comet update from Woof team", #1131).

Context

Fresh-commit Rule-38 pass. Comet's only new commit since 2026-06-04 deleted 2700+ lines: Comet.sol (-1378), ScrollComet.sol (-1324), CometFactory.sol (-11), and re-pointed Configurator.deploy / ConfiguratorOld.deploy from CometFactory to CometFactoryWithExtendedAssetList. Standardizing on CometWithExtendedAssetList.

The trap

A "delete N hundred lines of core contract" commit LOOKS high-severity by line-count. The 38.4 "follow-moved-protection" instinct fires: was a guard deleted without an equivalent survivor? You could burn 45 minutes re-reading the survivor contract end-to-end.

The discriminator (cheap, decisive)

When a commit DELETES a core contract and re-points references to a SURVIVOR:

  1. Confirm the survivor is pre-existing and unmodified in this commit: - git show <commit> -- contracts/<Survivor>.sol -> empty == not touched here. - git log -1 --format='%h %ci %s' -- contracts/<Survivor>.sol -> introduced well before. If the survivor is an established, long-lived, audited contract and this commit does not touch it, the consolidation cannot introduce NEW logic.

  2. Diff the security-function signatures of the deleted contract vs the survivor instead of reading bodies: bash git show <commit>~1:contracts/<Deleted>.sol > /tmp/old.sol diff <(grep -E 'function (accrue|isLiquidatable|isBorrowCollateralized|getBorrowRate|getSupplyRate|getUtilization|absorb|withdrawCollateral|supplyCollateral|buyCollateral|quoteCollateral)' /tmp/old.sol | sort) \ <(grep -E 'function (...same set...)' contracts/<Survivor>.sol | sort) Empty diff == identical security surface. Survivor is a superset of the deleted contract => follow-moved-protection PASS. The protection moved to an existing audited home, not a weaker new one.

  3. Verify the re-point sites are mechanical type-casts only (Old(addr).fn(...) -> New(addr).fn(...), same addr-read, same args, same caller policy). The deploy functions kept their Callable by anyone semantics and identical .clone(params) call.

Rule

Sibling to 38.4 family: replaced-not-reverted / conservation-discriminator / follow-moved-protection / cross-repo. This is the "consolidation variant" of follow-moved-protection.

Generated 2026-07-02 13:15:05 UTC | auto-sync /15min