CMTAT supports two layers of on-chain document management:
- Terms — the primary tokenization terms stored directly in the token contract via
ExtraInformationModule. - Additional documents — arbitrary ERC-1643 documents, managed through one of two patterns:
- Native storage (
DocumentERC1643Module) — documents are stored directly in the token contract (available in all shipped deployment variants). - External engine delegation (
DocumentEngineModule) — documents are managed by a separateDocumentEnginecontract (not integrated in any shipped deployment variant; available as an optional module).
- Native storage (
ERC-1643 — two versions, both supported. CMTAT implements both ERC-1643 variants:
- the original ERC-1643, a 2018 draft published only as a GitHub issue — never a merged EIP;
- its current rework, the draft proposal "Document Management for Security Tokens" (ethereum/ERCs PR #1754), still open/draft, which modernizes the Solidity syntax, broadens scope beyond security tokens to ERC-721/ERC-1155, and adds backwards-compatibility, testing, and security sections.
Both share the same core document model (
getDocument/getAllDocuments/setDocument/removeDocument, withbytes32document names), which is what CMTAT'sIERC1643implements.
The tokenization terms are a single CMTATTerms struct stored in the token contract. They can be read by anyone and set by an address with EXTRA_INFORMATION_ROLE.
interface ICMTATBase {
struct CMTATTerms {
string name;
IERC1643.Document doc;
}
event Terms(CMTATTerms newTerm);
function terms() external view returns (CMTATTerms memory);
function setTerms(IERC1643CMTAT.DocumentInfo calldata terms_) external;
}A Document contains:
uri— a URI pointing to the document (e.g., IPFS link or HTTPS URL)documentHash— hash of the document contents for integrity verificationlastModified— block timestamp of the last on-chain update (set automatically)
DocumentERC1643Module implements the full ERC-1643 interface directly inside the token contract. Documents are stored in ERC-7201 namespaced storage (slot 0x24fbb1cf6345ced60d5278ef6f68f4f7576fd9068704b4c8f1eec8f0bbd8a200) using:
mapping(bytes32 => Document) _documents;
mapping(bytes32 => uint256) _documentKey; // 1-based index for O(1) removal
bytes32[] _documentNames;All four ERC-1643 operations are available on the token contract directly — no external engine contract is required:
function getDocument(bytes32 name) external view returns (string memory uri, bytes32 documentHash, uint256 lastModified);
function getAllDocuments() external view returns (bytes32[] memory);
function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; // requires DOCUMENT_ROLE
function removeDocument(bytes32 name) external; // requires DOCUMENT_ROLEremoveDocument uses a swap-and-pop pattern to keep the _documentNames array compact without gaps.
DocumentERC1643Module is included in all shipped CMTAT deployment variants except Light through the inheritance chain CMTATBaseDocument (level 1) → CMTATBaseAccessControl (level 2). The Light variant (CMTATStandaloneLight, CMTATUpgradeableLight) is based on CMTATBaseCore, which does not extend CMTATBaseDocument and therefore has no ERC-1643 document support. The concrete access-control hook _authorizeDocumentManagement() enforces DOCUMENT_ROLE in CMTATBaseAccessControl.
DocumentERC1643Module |
DocumentEngineModule |
|
|---|---|---|
| Storage location | On-chain in token contract | External engine contract |
| Deployment complexity | None (built-in) | Requires separate engine deployment |
| Token contract size | Adds storage overhead | Keeps token contract lean |
| Multi-token sharing | One registry per token | One engine can serve many tokens |
| Shipped in CMTAT variants | Yes (all standard variants) | No |
DocumentEngineModule is an alternative to the native storage pattern. Instead of storing documents in the token contract, it delegates all four ERC-1643 operations to a separate DocumentEngine contract that implements ERC-1643:
interface IERC1643 {
struct Document {
string uri;
bytes32 documentHash;
uint256 lastModified;
}
function getDocument(bytes32 name) external view returns (string memory uri, bytes32 documentHash, uint256 lastModified);
function getAllDocuments() external view returns (bytes32[] memory);
function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external;
function removeDocument(bytes32 name) external;
}Note: CMTAT uses bytes32 for document identifiers, aligned with the original EIP proposal. Only the CMTAT tokenization terms path (IERC1643CMTAT.DocumentInfo) still uses string name.
The engine is completely free to implement its own storage and management logic. CMTAT forwards both read (getDocument, getAllDocuments) and write (setDocument, removeDocument) calls to the engine. Write operations and engine configuration require DOCUMENT_ENGINE_ROLE.
An address with DOCUMENT_ENGINE_ROLE can update the engine:
function setDocumentEngine(address documentEngine_) external;- Reduces CMTAT contract size (which is near the maximum EVM limit).
- Allows one engine to serve multiple token contracts simultaneously.
| CMTAT version | DocumentEngine |
|---|---|
| CMTAT v3.0.0 | Under development |
| CMTAT v2.5.0 (unaudited) | DocumentEngine v0.3.0 |
