Introduction
We express our gratitude to the Europeum team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.
Track and Trace is an EBSI document track-and-trace registry. Invited DID controllers create documents by hash through a commit–reveal flow, manage CREATE/DELEGATE/WRITE access, and append timestamped events under DID and policy checks.
Document | |
|---|---|
| Name | Smart Contract Code Review and Security Analysis Report for Europeum |
| Audited By | Khrystyna Tkachuk |
| Approved By | Ivan Bondar |
| Website | https://europeum.eu/→ |
| Changelog | 07/08/2026 - Preliminary Report |
| 12/08/2026 - Final Report | |
| 12/08/2026 - Updated Final Report | |
| Platform | Private Chain |
| Language | Solidity |
| Tags | Upgradable |
| Methodology | https://docs.hacken.io/methodologies/smart-contracts→ |
Document
- Name
- Smart Contract Code Review and Security Analysis Report for Europeum
- Audited By
- Khrystyna Tkachuk
- Approved By
- Ivan Bondar
- Website
- https://europeum.eu/→
- Changelog
- 07/08/2026 - Preliminary Report
- 12/08/2026 - Final Report
- 12/08/2026 - Updated Final Report
- Platform
- Private Chain
- Language
- Solidity
- Tags
- Upgradable
Review Scope | |
|---|---|
| Repository | https://gitlab.com/europeum/public/core-services/→ |
| Commit | db871ad |
| Final Commit | 85f94b2 |
| Updated Final Commit | d1535ba |
Review Scope
- Commit
- db871ad
- Final Commit
- 85f94b2
- Updated Final Commit
- d1535ba
Audit Summary
The system users should acknowledge all the risks summed up in the risks section of the report
Documentation quality
Functional requirements are partially missed.
Technical description is provided.
NatSpec is sufficient.
Code quality
The code leverages OpenZeppelin 5.6.1 and shared in-house primitives, and follows the established beacon-proxy upgradeable pattern.
The codebase is well-structured and clearly organized.
The development environment is configured.
Test coverage
Code coverage of the project is 97.56 % (statement coverage).
Deployment and basic user interactions are covered with 64 passing tests.
Negative cases coverage is present.
System Overview
TrackAndTrace is an OpenZeppelin upgradeable contract (AccessControlUpgradeable, ImplementationInitSelector) that stores documents keyed by bytes32 hash, with metadata, creator DID, timestamps (Source.Block or Source.External), per-subject access grants, an append-only event log, and a DocumentStatus lifecycle (None, Active, Deleted). Document creation uses a commit–reveal pattern: callers register a commitment via commitCreateDocument, wait CREATE_DOCUMENT_COMMITMENT_MATURITY blocks, then call createDocument with matching arguments and salt. Controllers of invited did:ebsi accounts may create documents; creators and delegated accounts call grantAccess / revokeAccess; creators and writers call writeEvent. Authorization uses external IDidRegistry.checkController for DID_EBSI and public-key-derived addresses for DID_KEY. Document-creation eligibility is gated by invitedDidEbsiAccounts, maintained via authoriseDid under the TNT:authoriseDid policy on IPolicyRegistry. removeDocument tombstones a document as DocumentStatus.Deleted after detaching subject indexes, permanently blocking hash reuse. DEFAULT_ADMIN_ROLE may update policiesRegistry and didRegistry through setPoliciesRegistry and setDidRegistry. Paginated getters expose documents, events, and access lists. Event string fields are bounded by MAX_METADATA_LENGTH.
TrackAndTraceLib supplies DID-key address derivation, assembly-based bytes equality, and EIP-712-style createDocument commitment hashing used by authorization, access revocation, and the commit–reveal path. No token or DeFi standards are implemented; the design is a permissioned on-chain registry for EBSI identity-bound document trails.
Files in Scope
TrackAndTrace.sol: Upgradeable registry for document lifecycle and access.
TrackAndTraceLib.sol: Shared helpers for DID-key handling and bytes comparison.
Privileged roles
TrackAndTracesol
DEFAULT_ADMIN_ROLE (inherited from AccessControlUpgradeable): Admin role for AccessControl role management and registry address updates.
Can call
grantRoleto grant roles.Can call
revokeRoleto revoke roles.Can call
setRoleAdminto change the admin role for a role.Can call
setPoliciesRegistryto replacepoliciesRegistry.Can call
setDidRegistryto replacedidRegistry.
TNT:authoriseDid policy holder: Can whitelist or blacklist DID EBSI accounts for document creation (also requires DID controller of
senderDid).Can call
authoriseDidto setinvitedDidEbsiAccounts. Blacklisting blocks only newcreateDocumentcalls; existing documents, grants, andwriteEventrights remain.
Invited DID EBSI creator: Whitelisted DID EBSI account whose controller can create documents after a matured commitment.
Can call
commitCreateDocumentto register a create commitment.Can call
createDocumentto create a document with a block or external timestamp once the matching commitment has matured.
Document creator: Controller of the document creator DID; manages document lifecycle and permissions.
Can call
removeDocumentto tombstone a document (DocumentStatus.Deleted), detach subject indexes, and remove the hash fromdocumentsMapped.Can call
grantAccessto grant DELEGATE or WRITE permissions (CREATOR is not grantable).Can call
revokeAccessto revoke permissions previously granted by the creator.Can call
writeEventto append events with a block or external timestamp.
Delegate: Account with DELEGATE permission whose controller can manage WRITE grants.
Can call
grantAccessto grant WRITE permission.Can call
revokeAccessto revoke WRITE permissions previously granted by the delegate.
Writer: Account with WRITE permission whose controller can append document events.
Can call
writeEventto append events with a block or external timestamp.
Potential Risks
Dependency on External Registries and Bootstrap Logic: TrackAndTrace depends on out-of-scope IPolicyRegistry.checkPolicy for authoriseDid and migrationRemoveDocument, IDidRegistry.checkController for _authorize on DID_EBSI accounts, and ImplementationInitSelector plus Pagination from @ebsiint-sc/bootstrap for initialization and list queries. Core paths including _authorize, createDocument, grantAccess, revokeAccess, writeEvent, and authoriseDid treat live didRegistry and policiesRegistry addresses set in initialize (and policiesRegistry again in initializeV2) as authoritative. Misconfiguration, unavailability, or incorrect responses from those registries can halt legitimate operations or admit unauthorized document creation, access changes, or event writes.
Admin Control of Registry Addresses: Holders of DEFAULT_ADMIN_ROLE may call setPoliciesRegistry and setDidRegistry at any time, subject only to a non-zero address check. A substituted policy registry can redefine which addresses satisfy "TNT:authoriseDid". A substituted DID registry can redefine controller checks used by _authorize for creation, grants, revokes, and event writes across all documents.
Control Risks from Invited DID Whitelist: authoriseDid sets invitedDidEbsiAccounts after policiesRegistry.checkPolicy("TNT:authoriseDid", msg.sender) and _authorize of senderDid. _getAccountAccess grants SCOPE.TNT_CREATE from that mapping when documentHash is zero, which is the gate checked by both createDocument overloads. Holders of the "TNT:authoriseDid" policy therefore control which DID EBSI accounts may create documents, without on-chain bounds on how many accounts are whitelisted or how often the mapping is flipped. Blacklisting does not revoke existing documents, grants, or writeEvent rights.
Absence of Timelock Mechanisms for Critical Operations: Privilege-changing calls in TrackAndTrace take effect in the same transaction. No on-chain review window, cooling period, or multi-party approval gate exists inside this contract before those changes become enforceable.
Forced Execution Without Subject Consent: When ACCESS_ENUM.DELEGATE is revoked, _revokeAccess recursively revokes ACCESS_ENUM.WRITE for every account in the subject’s children array (bounded by MAX_DELEGATED_CHILDREN) without signatures from those children. Separately, a document creator’s controller may call removeDocument, which marks the document DocumentStatus.Deleted, clears subject index entries, and removes the hash from documentsMapped, ending Active access and further writes for all invitees without their consent while retaining nested storage as a permanent tombstone.
Flexibility and Risk in Contract Upgrades: The project's contracts are upgradable, allowing the administrator to update the contract logic at any time. While this provides flexibility in addressing issues and evolving the project, it also introduces risks if upgrade processes are not properly managed or secured, potentially allowing for unauthorized changes that could compromise the project's integrity and security.
Absence of Upgrade Window Constraints: The contract suite allows for immediate upgrades without a mandatory review or waiting period, increasing the risk of rapid deployment of malicious or flawed code, potentially compromising the system's integrity and user assets.
Upgrade Path Dependency: Initialization assumes a sequenced path where initialize sets DEFAULT_ADMIN_ROLE, policiesRegistry, and didRegistry, and initializeV2 uses reinitializer(2) only to replace policiesRegistry. didRegistry has no corresponding reinitializer. Incompatible storage layout changes relative to Document, Access_Struct, accessBySubject, and documentsMapped, or an incomplete upgrade sequence, can leave policy pointers or access indexes inconsistent with the new logic.
Unverified External Timestamp Proofs: Overloaded createDocument and writeEvent accept caller-supplied timestamp and timestampProof, persist them through _createDocument / _writeEvent with Source.External, and perform no on-chain validation of the proof against a signature, registry, or other verifier. Only timestamp != 0 and the usual DID-controller plus scope checks apply (plus a matured create commitment for document creation). Consumers that treat those fields as attested chronology may rely on values that TrackAndTrace does not cryptographically authenticate.
Unbounded Event Log Growth: _writeEvent appends to eventHashes without a per-document maximum count. String fields are capped at MAX_METADATA_LENGTH, but any account with WRITE access can still grow nested event storage indefinitely through many bounded writes. That growth increases gas costs for pagination over large event lists and leaves permanent storage on tombstoned documents.
Unbounded Invitee Iteration on Document Removal: removeDocument calls _detachDocumentSubjects, which iterates doc.allInvited with no upper bound. Subject count is otherwise uncapped by grantAccess (only DELEGATE children are limited by MAX_DELEGATED_CHILDREN). A document with a sufficiently large invitee list can push removeDocument past the block gas limit, preventing the creator from tombstoning the document and detaching subject indexes.
Ungated, Never-Cleared Create Commitments: commitCreateDocument accepts any non-zero commitment from any caller with no invite or role check, and writes permanently to createDocumentCommitments. Successful createDocument reveals validate maturity via _checkCreateDocumentCommitment but do not delete the commitment slot. Unused or spent commitments therefore accumulate indefinitely, enabling storage griefing by arbitrary accounts while invite eligibility is enforced only at reveal time.
Findings
Code ― | Title | Status | Severity | |
|---|---|---|---|---|
| F-2026-1877 | Incomplete Mapping Cleanup in removeDocument Restores Document Permissions and Events on Hash Reuse | fixed | Critical | |
| F-2026-1886 | Ungated initializeV2 Permanently Redirects policiesRegistry | fixed | High | |
| F-2026-1877 | Missing accessBySubjectIndex Update in removeDocument Causes Permanent Removal DoS | fixed | High | |
| F-2026-1887 | Sentinel Collision in _getAccountAccess Enables Whitelist Document Permission Takeover of Zero-Hash Documents | fixed | Medium | |
| F-2026-1886 | Incorrect Unchecked subjectAccountType Permanently Locks writeEvent | fixed | Medium | |
| F-2026-1886 | Ungated CREATOR Permission in grantAccess Enables Subject Type Poisoning | fixed | Medium | |
| F-2026-1887 | Duplicate children Push Corrupts DELEGATE Cascade Indexes | fixed | Low | |
| F-2026-1877 | Unbounded origin, externalHash, and eventHashes Enable Storage Griefing | mitigated | Low | |
| F-2026-1887 | Dead and Unused Interface Surface | fixed | Observation | |
| F-2026-1887 | AccessRevoked Event Omits Permission | fixed | Observation |
Appendix 1. Definitions
Severities
When auditing smart contracts, Hacken is using a risk-based approach that considers Likelihood, Impact, Exploitability and Complexity metrics to evaluate findings and score severities.
Reference on how risk scoring is done is available through the repository in our Github organization:
Severity | Description |
|---|---|
Critical | Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation. |
High | High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation. |
Medium | Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category. |
Low | Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution. |
Severity
- Critical
Description
- Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.
Severity
- High
Description
- High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.
Severity
- Medium
Description
- Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.
Severity
- Low
Description
- Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution.
Potential Risks
The "Potential Risks" section identifies issues that are not direct security vulnerabilities but could still affect the project’s performance, reliability, or user trust. These risks arise from design choices, architectural decisions, or operational practices that, while not immediately exploitable, may lead to problems under certain conditions. Additionally, potential risks can impact the quality of the audit itself, as they may involve external factors or components beyond the scope of the audit, leading to incomplete assessments or oversight of key areas. This section aims to provide a broader perspective on factors that could affect the project's long-term security, functionality, and the comprehensiveness of the audit findings.
Appendix 2. Scope
The scope of the project includes the following smart contracts from the provided repository:
Scope Details | |
|---|---|
| Repository | https://gitlab.com/europeum/public/core-services/→ |
| Commit | db871ad07ff4dd62e89dceeb08cacc4bbb50103f |
| Final Commit | 85f94b2c705dfde80d25c56f62ef54e39db84b27 |
| Updated Final Commit | d1535bacb0ee03fc987339c58324b34c3db14687 |
| Whitepaper | - |
| Requirements | README.md; NatSpec |
| Technical Requirements | README.md; NatSpec |
Scope Details
- Commit
- db871ad07ff4dd62e89dceeb08cacc4bbb50103f
- Final Commit
- 85f94b2c705dfde80d25c56f62ef54e39db84b27
- Updated Final Commit
- d1535bacb0ee03fc987339c58324b34c3db14687
- Whitepaper
- -
- Requirements
- README.md; NatSpec
- Technical Requirements
- README.md; NatSpec
Assets in Scope
Appendix 3. Additional Valuables
Additional Recommendations
The smart contracts in the scope of this audit could benefit from the introduction of automatic emergency actions for critical activities, such as unauthorized operations like ownership changes or proxy upgrades, as well as unexpected fund manipulations, including large withdrawals or minting events. Adding such mechanisms would enable the protocol to react automatically to unusual activity, ensuring that the contract remains secure and functions as intended.
To improve functionality, these emergency actions could be designed to trigger under specific conditions, such as:
Detecting changes to ownership or critical permissions.
Monitoring large or unexpected transactions and minting events.
Pausing operations when irregularities are identified.
These enhancements would provide an added layer of security, making the contract more robust and better equipped to handle unexpected situations while maintaining smooth operations.
Frameworks and Methodologies
This security assessment was conducted in alignment with recognised penetration testing standards, methodologies and guidelines, including the NIST SP 800-115 – Technical Guide to Information Security Testing and Assessment →, and the Penetration Testing Execution Standard (PTES) →, These assets provide a structured foundation for planning, executing, and documenting technical evaluations such as vulnerability assessments, exploitation activities, and security code reviews. Hacken’s internal penetration testing methodology extends these principles to Web2 and Web3 environments to ensure consistency, repeatability, and verifiable outcomes.