Releases: CMTA/DocumentEngine
Release list
v0.4.0
Targets CMTAT v3.3.0-rc3 — see the compatibility matrix for which CMTAT release each version of this engine is built against.
Versioning note.
getDocumentchanges shape relative tov0.3.0, which the convention above classifies as a MAJOR bump.MINORis used because the project is still in its0.xline, where a1.0.0would wrongly signal a stable, audited release. Treat this release as breaking for any consumer decodinggetDocument.
Changed
-
Dependencies
- Upgrade CMTAT
v2.5.0-rc0→v3.3.0-rc3(lib/CMTAT→658672f190d56d3f61663a7d6d51962b8980df70). Development passed throughv3.3.0-rc1andv3.3.0-rc2. rc1 is not compatible with the code as shipped here, because it declares neither the ERC-1643 errors nor the flatgetDocumentreturn (see below); rc2 and rc3 are interchangeable for this engine — between them, the whole document surface (draft-IERC1643.sol,IDocumentEngine.sol,DocumentEngineModule.sol,DocumentERC1643Module.sol) changed only its pragma,^0.8.20→^0.8.24. Both are therefore listed as the supported range, verified by building and running the full suite against each (74/74 on both). Nothing below rc2 works:v3.0.0/v3.1.0/v3.2.0return aDocumentstruct and declare no interface errors, and they predate CMTAT's token-sideDocumentEngineModuleentirely. - Upgrade OpenZeppelin Contracts (and Contracts Upgradeable)
v5.0.2→v5.7.0.v5.7.0deprecatesEnumerableSet.at()in favour ofpos()(the old name clashes with a keyword scheduled for Solidity);at()remains as a forwarding alias, and this engine has no call sites either way. The only exposure is inherited —AccessControlEnumerable.getRoleMemberswitched topos()internally, with no change to its signature, selector or behaviour. Verified:DocumentEngine's runtime code is byte-identical acrossv5.6.1andv5.7.0(8436 bytes; only the CBOR metadata trailer moves, because the source text ofAccessControlEnumerable.solchanged), andDocumentEngineOwnable's bytecode is unchanged including metadata. - Add CMTA/RuleEngine
v3.0.0-rc5as a submodule (binding-pattern reference; see Why not reuse RuleEngine's compliance module? — itsERC3643ComplianceExtendedModuleis not reused) foundry.locknow records every submodule by tag; all five entries had gone stale sincev0.3.0.
- Upgrade CMTAT
-
Toolchain: bump Solidity
0.8.26→0.8.34andevm_versioncancun→pragueto match CMTAT v3 (CMTAT usesrequire(cond, CustomError()), which needs solc ≥ 0.8.27) -
Code-quality review (
doc/audits/tools/v0.4.0/claude/CLAUDE_ANALYSIS.md) — 14 findings, none a vulnerability. Six implemented:- Gas,
_removeDocumentName: the_documentNames[subject]mapping slot was re-hashed on every loop iteration; cached as a storage pointer. Measured −2200 gas on a 20-entry full scan. - Gas,
_removeDocument: the wholeDocument(URI included) was copied to memory to be read three times; now read through a storage pointer. A further −645 gas. Combined, removal is −2845 gas (−3.3 %) worst case. The emit must stay ahead of thedelete— verified by mutating the order and confirmingtestRemoveDocumentEmitsForSubjectEventfails. Side effect: Slither'sincorrect-equality(Medium) andtimestamp(Low) stopped firing on the unchangeddoc.lastModified == 0, taking it from 4 results to 2. Not a fix — both were already false positives and the detector merely loses the taint through a storage pointer. hasRoleNatSpec: documented that a role is unrevokable from the default admin —revokeRolesucceeds, emitsRoleRevokedand dropsgetRoleMemberCount, yet the admin keeps the access. Not a privilege issue (an admin can re-grant itself anything) but the call misreports. Pinned by the newtestRevokingRoleFromDefaultAdminDoesNotRemoveAccess.DocumentEngineInvariant: the error-location comment misattributedNotBoundToken(address)toITokenBinding; it is declared byTokenBindingModule.- Documentation pointers removed from contract comments. Three comments referenced
doc/ERCSpecification…; documentation moves but deployed source does not, and this repo had already renamed that file once (ERC-1643-proposition.md→erc-draft_multi_document_management.md), leaving a dangling README link behind. Someone reading verified source on an explorer has the comment and not the file. All three pointers are gone and each comment is now shorter, not longer — theIERC1643MultiDocumentheader dropped from 10 lines to 9 by replacing an enumeration that gestured at the draft's rationale with the one operative fact:subjectneed not be a token. - All 12
internalfunctions are nowvirtual(_setDocument,_removeDocument,_removeDocumentName,_getDocument,_setTokenBinding,_checkTokenBound, and the ERC-2771 context trio in both deployments), resolving an inconsistency whereTokenBindingModuleexposed its public surface for override whileDocumentEngineBaseexposed nothing but its two abstract hooks. A deployment can now override the document write/read paths and the binding check, matching what CMTAT's equivalent module allows. Runtime cost is zero: the executable bytecode of both deployments is byte-identical before and after (7457 / 6111 bytes, metadata trailer excluded). Guarded byOverridingDocumentEngine+testInternalHooksAreVirtualAndOverridesAreReached— removingvirtualfrom any of the three overridden hooks fails the build (Error (4334): Trying to override non-virtual function).
Notable non-changes, recorded so they are not re-raised:
unchecked { ++i }buys 0 gas on solc 0.8.34 (measured);string calldataon the adminsetDocumentis 49 gas worse thanmemory(measured); and the duplicated ERC-2771 context overrides cannot be extracted into a shared module — C3 linearization forces each deployment to re-state them, proven by compiler error. - Gas,
-
Style pass across
src/andscript/— behaviour-preserving. Brought the sources in line with the Solidity style guide: functions reordered by visibility group (external → public → internal,view/purelast within each), so the_authorize*hooks and the ERC-2771 context overrides now follow the public API instead of preceding it; every brace-less global import replaced by a named one (which required adding the previously implicitContextandAccessControlimports, since a named import no longer re-exports a dependency's own imports); and NatSpec completed with a@paramper argument and a@returnper return value. No signature, visibility, body or storage layout changed — verified by an unchanged per-contract function set, a cleanforge build, and 72/72 tests passing. -
Source pragma raised
^0.8.20→^0.8.24acrosssrc/,script/andtest/. This is a correction, not a new restriction:^0.8.20had become an over-promise, advertising a range the sources could not actually compile in. OpenZeppelin'sAccessControlEnumerable.solandEnumerableSet.solare^0.8.24, and CMTATv3.3.0-rc3moveddraft-IERC1643.solto^0.8.24as well, so every contract insrc/now transitively requires it —forge build --use 0.8.23fails to resolve a compiler.0.8.24is the realsrc/floor; the full project including the CMTAT-importing tests needs0.8.27, becauserequire(cond, CustomError())is restricted to the via-ir pipeline before then. Deployed bytecode is unaffected — the pinned compiler is still0.8.34. -
IERC1643(CMTAT v3) breaking changes-
getDocumentkeeps returning(string uri, bytes32 documentHash, uint256 lastModified)— the flat ERC-1643 ABI — on both overloads,getDocument(bytes32)andgetDocument(address subject, bytes32). CMTATv3.3.0-rc1briefly replaced this with aDocumentstruct andv3.3.0-rc2reverted it; this engine follows rc2/rc3, so relative tov0.3.0the external shape is unchanged.The distinction is worth recording because it is invisible to interface detection: return types are not part of a function signature, so both shapes share the same selectors and the same
type(IERC1643).interfaceId(0xecfecec8). A consumer built from the specification ABI decodes a struct return as garbage without reverting —uribecomes binary junk,documentHashbecomes0x…60, andlastModifiedbecomes the real hash as auint256.getDocumentis now covered bytestGetDocumentReturnsFlatErc1643Abi, which inspects the returndata directly since ERC-165 structurally cannot. -
The
Documentstruct and theDocumentUpdated/DocumentRemovedevents are now provided byIERC1643; the duplicate local declarations were removed fromDocumentEngineInvariant. The struct is retained internally for storage only. -
ERC1643InvalidName()/ERC1643MissingDocument()are likewise declared byIERC1643as of CMTATv3.3.0-rc2and are not re-declared here. The multi-subject draft requires a contract implementing both interfaces to obtain each error exactly once ("MUST NOT declare them twice"), and re-declaring is a compile error. Selectors, and hence revert data, are unchanged. The same principle was applied to every other error:MultiDocumentInvalidSubject()moved toIERC1643MultiDocumentandTokenBindingInvalidToken()is declared onITokenBinding, so an ABI generated from an interface carries its errors.DocumentEngineInvariantnow holds onlyInvalidInputLengthandAdminWithAddressZeroNotAllowed, which no interface defines. -
Im...
-
v0.3.0
- Add ERC-2771 support
The release does not include the .git folder, which does not allow installing dependencies.
The easiest solution is to clone the project and perform a git checkout on the version tag
git clone https://github.com/CMTA/DocumentEngine
git checkout v0.3.0
forge install
forge build
v0.2.0
- Add the constant VERSION
- Add batch functionsto manage documents for one target contract
- Improve documentation
The release does not include the .git folder, which does not allow installing dependencies.
The easiest solution is to clone the project and perform a git checkout on the version tag
git clone https://github.com/CMTA/DocumentEngine
git checkout v0.2.0
forge install
forge build