Releases: CMTA/CMTAT-Factory
Releases · CMTA/CMTAT-Factory
Release list
v0.5.0
0.5.0 - 2026/08/26
Summary
The release that makes the factory's access control pluggable, and doubles the deployable surface as a result.
- Ten deployable factories instead of five. Each of the five families (UUPS, Transparent, Beacon, and the two CMTAT Light variants) now ships in two access-control flavours: the existing role-based
CMTAT_*_FACTORY, and a new single-ownerCMTAT_*_FACTORY_Ownable2Step. They share identical deployment logic and differ only in who may calldeployCMTAT. The choice is made at deployment and cannot be changed at a deployed address. deployCMTATis gated by a hook, not a hard-wired role.CMTATFactoryRootno longer inheritsAccessControl; it declaresonlyCMTATDeployerand a bodyless_authorizeDeployCMTAT(), which each deployment answers with a single modifier.CMTAT_DEPLOYER_ROLEmoved to the layer that enforces it, so anOwnable2Stepfactory never publishes a role it does not check.- A dependency-free integration interface.
ICMTATFactorydeclares the registry and salt surface every factory shares and imports nothing, so an indexer can compile against it without the CMTAT submodule or OpenZeppelin. isCustomSaltUsed(bytes32)closes a gap where the address predictors kept answering for a one-time-use salt that had already been consumed.contracts/reorganised so a file's path says what it is:interfaces/,modules/{core,proxy,deployment,access}/,libraries/, and the deployable directories.- Dependencies moved forward: CMTAT
v3.3.0-rc1->v3.3.0-rc3, OpenZeppelin5.6.1->5.7.0, solc0.8.34->0.8.36.
Compatibility: the five existing factories keep their public constructor signatures and their ABI shape. Two things do change for integrators — type(ICMTATFactory).interfaceId (the interface dropped its access-control member) and the deployed CMTAT implementation bytecode (the vendored submodule moved).
Verification: 117 tests pass (42 at the start of the release), Slither and Aderyn report nothing to fix, and the style checker reports 0 violations across all 8 checks.
Added
ICMTATFactory(contracts/interfaces/ICMTATFactory.sol): the deployment-registry and salt surface every factory exposes identically -CMTATProxyAddress,cmtatsList,cmtatCounterId,useCustomSalt,nextDeploymentSalt,isCustomSaltUsedand theCMTATDeployedevent (no access-control member: that is policy, and policy is chosen per deployment). It has no imports, so an indexer or integrating contract can compile against it without the CMTAT submodule or OpenZeppelin; previously the only way to call a factory was to import the concrete contract and its whole dependency graph. It is inherited (so the compiler enforces the match, not a comment) and advertised through ERC-165supportsInterface. The deployment entrypoints are deliberately not declared: their shapes differ across the family, their arguments are CMTAT types, anddeployCMTATreturns the concrete proxy type rather thanaddress. Costs +26 bytes per factory and leaves the ABI shape unchanged. Finding J-3 of the v0.5.0 code-quality review; covered bytest/FactoryInterface.test.js.isCustomSaltUsed(bytes32 salt)on every factory: whether a custom salt has already been consumed, sodeployCMTAT(...)with it would revertCMTAT_Factory_SaltAlreadyUsed. PreviouslycustomSaltUsedwasinternal, and the address predictors keep answering for a consumed salt (the CREATE2 address is still correct, it just can no longer be reached) - so an integrator had no on-chain way to tell a live prediction from a dead one short of sending a transaction and watching it revert. Returnsfalsein counter mode, which never records a salt. Finding H-1 of the v0.5.0 code-quality review; covered bytest/CustomSalt.test.js.
Changed
- Bumped the factory version constant to
0.5.0(ContractVersion.sol) and synced every mirror (package.json,package-lock.json, theversion()test,README.md,doc/README.md,AGENTS.md/CLAUDE.md). - Reordered functions and modifier keywords across the contracts to follow the Solidity style guide (constructor / external / public / internal / private,
viewandpurelast; visibility before mutability beforevirtual/overridebefore custom modifiers). Member moves only, no logic change. .gitignore: ignore LibreOffice lock files (.~lock.*#).- Reorganised
contracts/so a file's path says what it is.contracts/libraries/had grown to hold 13 abstract contracts and a single actuallibrary; the abstract contracts moved tocontracts/modules/, grouped by capability -core/(registry, salt logic, CREATE2, version),proxy/(proxy-mechanism bases),deployment/(per-family entrypoints) andaccess/(who may deploy) - leavinglibraries/holding onlyFactoryErrors. Two interfaces that were declared inside the module implementing them were extracted:IERC8303fromContractVersion.solandIERC173fromCMTATFactoryOwnable2Step.sol, both now undercontracts/interfaces/. The layout follows the upstream CMTAT convention. Behaviour-preserving: the moved files differ only in their import lines, every relative import was recomputed and verified to resolve, and all 117 tests pass. Finding J-1 of the v0.5.0 code-quality review. - Extracted the CREATE2 address prediction into
_computeCreate2Address(bytecode, salt)onCMTATFactoryRoot, replacing three byte-identical copies ofCreate2.computeAddress(...)in the beacon base, the transparent base and the UUPS factory, and removing the three now-unusedCreate2imports. It sits beside_deployAndRegisterProxy- the function it must mirror - and takes the same arguments in the same order, so the deploy/predict invariant has one documented home. Behaviour is unchanged (predicted addresses depend only on deployer, salt and init code, none of which moved);computedProxyAddressgas is identical at 35,280, and deployed bytecode grows by 7 bytes per factory (12 for the beacon one). Finding D-2 of the v0.5.0 code-quality review. _deployAndRegisterProxycachescmtatCounterIdin a local instead of loading the slot twice (once for the event, once for the increment): 114 gas per deployment, measured. Finding B-1 of the v0.5.0 code-quality review.- Marked every
internalfunctionvirtual(18 additions across 8 files), so the internal surface is now 20/20 and matches the public one, which was already 13/13. Previously the rule was applied inconsistently - insideCMTATFactoryRoot,_deployAndRegisterProxysat between twovirtualsiblings without the keyword - which left the deployment funnel and the_checkProxyAdminOwnervalidation hook impossible to override without forking the base contract. The runtime bytecode is byte-identical for all five factories (verified with the solc metadata trailer stripped), so this costs nothing to deploy or run. Finding E-1 of the v0.5.0 code-quality review; the two highest-consequence hooks are covered bytest/VirtualOverride.test.js.
Dependencies
- Updated the Hardhat Solidity compiler from
0.8.34to0.8.36(hardhat.config.js), matching the compiler the pinned CMTAT submodule builds with. Source pragmas stay^0.8.20; the EVM target staysprague. - Updated the pinned CMTAT submodule from
v3.3.0-rc1tov3.3.0-rc3. - Updated OpenZeppelin Contracts and Contracts-Upgradeable from
5.6.1to5.7.0. - Relaxed the
npm run check:ozguard (scripts/check-oz-version.js) from an exact-range match to a same-major floor: it still fails when the factory's OpenZeppelin is older than, or on a different major to, the version the pinned CMTAT submodule declares - the duplicateInitializablebreakage the guard was written for - but a newer OZ within the same major is now reported as a warning instead of an error. This is what CMTATv3.3.0-rc3needs, since it still pins OZ5.6.1exactly while the factory runs5.7.0(compiles clean, full suite passing).
Documentation
- Split the README in two: the root
README.mdis now a short overview (factory table, key features, common API, quick start, documentation index, security), and the full specification moved todoc/README.mdwith all relative links rewritten for its new location. - Completed NatSpec coverage across
contracts/: every contract, interface, library, struct, event, state variable, constant and function now carries a/** */block, with one@paramper argument and one@returnper return value. Comment-only change, verified by the style checker and by a full compile. - Fixed the stale
ContractVersionversion shown in the library-contracts table (was"0.3.0"). - Added the versioned specification PDF (
doc/specification/CMTATFactorySpecificationV0.4.0.pdf) and its cover page sources (coverpage.odg,coverpage.pdf). - Disclosed the use of AI coding assistants (Claude Code, Codex) in both READMEs.
- Added a PlantUML diagram directory (
doc/schema/plantuml/) holding both the.pumlsources and their renders. Added anOverviewdiagram (deployer -> factory -> CREATE2 proxy -> CMTAT implementation, with the factory/proxy/implementation matrix) to the top of both READMEs, and replaced the drawio export of the beacon factory withbeacon-factory.png. The redrawn beacon diagram corrects a stale label: the implementation behindCMTAT_BEACON_FACTORYisCMTATStandardUpgradeable, notCMTATUpgradeable, and it now also shows that the beacon is created once in the factory constructor. Removed the supersededdoc/schema/drawio/factory-BeaconFactory.drawio.png(thefactory.drawiosource is kept - it still backs the transparent-factory diag...
v0.4.0
The fix commits for the Nethermind AuditAgent findings (NM-1, NM-2) are slated for this release.
Security
- Reviewed the Nethermind AuditAgent v0.3.0 report and added per-finding triage. NM-1 (Low): the
counter-derived next-address helpers (computedNextProxyAddress/nextDeploymentSalt) are front-runnable
across authorized deployers, because in counter mode the effective CREATE2 salt is the shared
keccak256(cmtatCounterId). NM-2 (Info): that same counter-derived salt can be reused under reentrant proxy
initialization, sinceCreate2.deployruns before++cmtatCounterId. See
doc/audits/v0.3.0/audit_agent_report-feedback.md. - NM-1 documentation clarification. Added a
WARNINGNatSpec block tonextDeploymentSalt()and to
computedNextProxyAddress(...)on all five factories, plus a warning callout in the README "Salt behavior"
section, stating that in counter mode (useCustomSalt == false) a predicted address is only valid until the next
deployment by any authorized deployer, and that the safer mode is custom salts (useCustomSalt == true) with a
unique, caller-chosen, one-time-use salt. Documentation-only; no behavior change. - NM-2 reentrancy guard. Each concrete factory inherits OpenZeppelin
ReentrancyGuard(co-located with usage)
and marks its publicdeployCMTAT(...)entrypointnonReentrant. This prevents a
reentrantdeployCMTAT(...)— triggered during a proxy's constructor/initializer, before++cmtatCounterId— from
observing the samecmtatCounterIdand reusing the auto-derived salt, so every automatic deployment keeps a
distinct counter and salt. The guard is declared first in the modifier list (nonReentrant onlyRole(...)) per
the Aderyn best-practice check.
Changed
- Bumped the factory version constant to
0.4.0(ContractVersion.sol) and synced every mirror (package.json,
theversion()test, README,AGENTS.md/CLAUDE.md).
Documentation
- Added the Nethermind AuditAgent v0.3.0 report (
doc/audits/v0.3.0/audit_agent_report_v0.3.0.pdf) and its
per-finding triage feedback, and recorded both indoc/audits/AUDIT_OVERVIEW.md
(neither finding exploitable; both hardened in this release — NM-1 docs warning, NM-2 reentrancy guard). - Added
doc/script/convert_links_for_pdf.sh, a helper that rewrites relative Markdown links to GitHub URLs for
PDF generation while preserving image and external links. - Added versioned Slither (0.11.5) and Aderyn (0.6.5) static-analysis reports for v0.4.0 under
doc/audits/v0.4.0/
with per-finding triage feedback (both tools: nothing to fix). The Aderyn set matches v0.3.0 (1 High false
positive + 4 by-design/environment Lows); Slither's factory-scoped result stays 0.
Testing
- Added
test/UUPS/ReentrancyGuard.test.jsandcontracts/mocks/ReentrancyDeployMock.sol: a maliciouslogic
mock re-entersdeployCMTATfrom the proxy initializer through a role-holding attacker. The armed case reverts
and registers nothing (thenonReentrantguard fires); a disarmed control deployment succeeds — isolating the
guard as the cause (NM-2). Suite: 42 passing.
v0.3.0
Added
- CMTAT Light factories
CMTAT_LIGHT_TP_FACTORYandCMTAT_LIGHT_BEACON_FACTORY(contracts/light/) deploying the lighterCMTATUpgradeableLightimplementation through the smallerCMTAT_LIGHT_ARGUMENTinitializer struct. - ERC-8303 factory version support:
version()exposed throughIERC8303/ContractVersion, with matching ERC-165supportsInterface. - Rich deployment event
CMTATDeployed(address indexed proxy, address indexed deployer, uint256 indexed id, bytes32 salt), emitted on every deployment. - Deployment-salt helpers for address prediction:
nextDeploymentSalt()andcomputedNextProxyAddress(...)on every factory. npm run check:ozscript (also run in CI) that fails the build if the installed OpenZeppelin version diverges from the version required by the pinned CMTAT submodule.
Changed
- Reorganised factory contracts into
contracts/standard/andcontracts/light/folders. - Shared transparent and beacon deployment logic into new base contracts
CMTATTransparentFactoryBaseandCMTATBeaconFactoryBase. - Removed redundant beacon access-control inheritance and made factory-error imports explicit.
- Removed the redundant
cmtatsmapping —CMTATProxyAddress(id)now readscmtatsList(with a bounds guard returning the zero address for unknown ids), saving one storage write per deployment. - Assigned
useCustomSaltunconditionally in the constructor and removed a no-opbytes32(...)cast innextDeploymentSalt(). - Set the
package.jsonversionfield to0.3.0.
Removed
- Removed the legacy
CMTAT(address indexed, uint256)event, superseded byCMTATDeployed(which carries the same proxy address and id plus thedeployerandsalt).
Fixed
- Validate the transparent proxy admin owner is non-zero (
CMTAT_Factory_AddressZeroNotAllowedForProxyAdminOwner). - Fix proxy creation bytecode assembly for the Light factories.
Dependencies
- Update Solidity (Hardhat compiler) to v0.8.34.
- Update CMTAT to v3.3.0-rc1.
- Update OpenZeppelin (Contracts & Contracts-Upgradeable) to v5.6.1 — required by CMTAT v3.3.0-rc1 and resolves a duplicate
Initializabledeclaration that broke compilation under v5.4.0.
Documentation
- Major README overhaul: document all five factories including the Light variants, the ERC-8303
version(), deployment events, and the salt / address-prediction API; correct theEngineABI encoding, the TransparentdeployCMTATreturn type, and contract paths; complete the library-contracts section and regenerate the Surya diagrams. - Added README sections: a generated table of contents,
CREATEvsCREATE2deterministic deployment, a Transparent/UUPS/Beacon proxy comparison table, and a CMTAT Standard vs Light overview. - Added
@returnNatSpec tocomputedProxyAddress(Transparent/UUPS), fixed thebeaconImplementationcasing, and linked ERC-8303 to its draft PR (not yet published as an EIP page). - Added versioned Slither and Aderyn static-analysis reports for v0.3.0 with per-finding triage feedback, and a consolidated
doc/audits/AUDIT_OVERVIEW.md(both tools: nothing to fix).
Testing
- Added tests for the proxy registry (the emitted
CMTATDeployedaddress,cmtatsList(id), andCMTATProxyAddress(id)all agree, in both counter-salt and custom-salt modes; unknown ids return the zero address), theuseCustomSaltgetter (true/false), a uniformversion()across all five factories, andsupportsInterfacefor the inherited ERC-165 and AccessControl interface ids (exercising thesuperchain inContractVersion).