Introduction
We express our gratitude to the Europeum team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.
Europeum operates and scales Europe’s sovereign digital trust infrastructure, enabling secure, transparent, and interoperable services across the public and private sectors.
Document | |
|---|---|
| Name | Smart Contract Code Review and Security Analysis Report for Europeum |
| Audited By | Seher Saylik |
| Approved By | Ivan Bondar |
| Website | https://europeum.eu/→ |
| Changelog | 28/07/2026 - Preliminary Report |
| 04/08/2026 - Retest V1 | |
| 05/08/2026- Retest V2 | |
| 14/08/2026 - Retest V3 | |
| 17/08/2026 - Final Commit | |
| Platform | Private Chain |
| Language | Solidity |
| Tags | Proxy, Upgradable, Centralization |
| Methodology | https://docs.hacken.io/methodologies/smart-contracts→ |
Document
- Name
- Smart Contract Code Review and Security Analysis Report for Europeum
- Audited By
- Seher Saylik
- Approved By
- Ivan Bondar
- Website
- https://europeum.eu/→
- Changelog
- 28/07/2026 - Preliminary Report
- 04/08/2026 - Retest V1
- 05/08/2026- Retest V2
- 14/08/2026 - Retest V3
- 17/08/2026 - Final Commit
- Platform
- Private Chain
- Language
- Solidity
- Tags
- Proxy, Upgradable, Centralization
Review Scope | |
|---|---|
| Repository | https://gitlab.com/europeum/public/core-services/-/tree/main/contracts/contracts-registry/contracts→ |
| Commit | 89bb63b |
| Retest V1 | a64e313 |
| Retest V2 | 048a030 |
| Retest V3 | d1535ba |
| Final Commit | 708ec7f |
Review Scope
- Commit
- 89bb63b
- Retest V1
- a64e313
- Retest V2
- 048a030
- Retest V3
- d1535ba
- Final Commit
- 708ec7f
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 documented.
A description is provided, but it is outdated and references missing components, roles, functions, and events.
Code quality
The contracts generally reuse OpenZeppelin and shared project libraries.
The development environment, compilation, and testing scripts are configured.
Test coverage
Code coverage of the project is 94.21% (state coverage).
Deployment and basic multi-user interactions are covered.
Negative cases are substantially covered.
However, adversarial beacon updates, malformed initialization data, constructor-time behavior, CREATE2 address drift, and pagination edge cases are not thoroughly covered.
System Overview
Europeum EBSI Contract Factory is a deployment system that enables authorized users and contracts to deploy upgradeable beacon proxies from governance-approved templates.
ProxyTemplateRegistry stores templates identified by name and version. Each template includes a beacon address, repository and audit references, integrity metadata, an initializer selector, and an active status. Templates can be registered, updated, or deprecated.
ProxyFactory deploys proxies from active templates using regular contract creation or CREATE2. It prepares initialization calldata, records each deployment, and groups deployed proxies by DID. Authorization depends on governance roles, external policies, DID ownership, or an existing active proxy record.
Pagination provides shared pagination logic for template and deployment queries.
Privileged Roles
DEFAULT_ADMIN_ROLE can grant and revoke roles, including EBSI_GOVERNOR.
EBSI_GOVERNOR in ProxyFactory can:
Change the DID and policy registries.
Deploy proxies without policy, DID-controller, or active-proxy checks.
Activate or deactivate registered deployments via
setDeploymentActive.
EBSI_GOVERNOR in ProxyTemplateRegistry can:
Change the policy registry.
Add, update, and deprecate templates without policy authorization.
Holders of the
TCR:manageTemplatespolicy can manage templates.Holders of the
TCR:deployProxypolicy can deploy proxies when they control the supplied DID or are registered as an active proxy.
Potential Risks
Contracts calling deployProxy() become the proxy’s beacon-level upgrade owner and must implement properly authorized functions to call upgradeProxyToVersion() and transferProxyOwnership() on the beacon; otherwise, the proxy’s upgrade and ownership-transfer rights will be inaccessible.
Because EBSI_GOVERNOR bypasses didRegistry.checkController, it can deploy a proxy using any supplied DID without proving control, after which the factory stores, indexes, and emits that DID as the proxy’s deployment attribution.
EBSI_GOVERNOR can immediately replace the factory’s DID and policy registries with any non-zero address, potentially changing authorization behavior or preventing legitimate deployments if the role is compromised.
ProxyFactory and ProxyTemplateRegistry are deployed behind upgradeable beacon proxies, allowing the relevant beacon owner to replace their logic for all instances; if this authority is compromised or misused, authorization rules, template records, and deployment behavior could be changed without protection from any timelock or approval mechanism in the scoped contracts.
Because deprecation only blocks one template name and version, the same beacon can be registered again with a different name or version and used for new proxy deployments.
Implementation initializers execute with msg.sender set to the factory, in both deployment paths — via delegatecall from the proxy in deployProxy and via functionCall in deployProxyWithSalt. Any implementation that derives privileges from msg.sender inside initialize (granting an admin role, setting an owner) will assign them to ProxyFactory rather than to the deployer. Privileged addresses must be passed explicitly in initData.
Pagination helpers used by getDeployedContracts and getContractsByTemplate can return prev = 0 for an empty list when page >= 2, and a very large out-of-range page can overflow the cursor calculation and revert with panic 0x11 instead of returning an empty page.
A registered proxy can deploy with an empty deployerDID and reuse its parent’s stored DID without a new didRegistry.checkController check. If that DID’s controller is later revoked or the parent proxy is compromised, the factory still allows further deployments under the old DID until an EBSI_GOVERNOR calls setDeploymentActive. Until then, unauthorized or obsolete callers can keep creating proxies attributed to a DID they no longer control. The team stated that this is intentional.
functionCall is a call from the factory into the proxy. Any implementation that treats msg.sender as owner/admin during initialize assigns that role to ProxyFactory, not the deployer. Privileged addresses must come from initData / the separate owner arg.
Every factory deployment creates a VersionedBeaconProxy, which calls registerProxy on template.beaconAddress in its constructor. addTemplate only checks standard IBeacon.implementation(), so a plain OpenZeppelin UpgradeableBeacon can be registered even though it has no registerProxy. Any later deployProxy / deployProxyWithSalt for that template then reverts in the proxy constructor, so the template appears active but no proxy can be deployed from it. Therefore, Normal OZ UpgradeableBeacon is not supported in this code.
Findings
Code ― | Title | Status | Severity | |
|---|---|---|---|---|
| F-2026-1824 | Template Integrity Metadata Is Never Enforced and Deployments Follow the Beacon's Live Implementation | mitigated | Medium | |
| F-2026-1896 | Deactivation Does Not Cascade to Child Proxies | mitigated | Medium | |
| F-2026-1871 | Deployed Proxies Cannot Be Deactivated | fixed | Low | |
| F-2026-1871 | Hardcoded Proxy Ownership Can Assign Upgrade Authority to the Calling Contract | fixed | Low | |
| F-2026-1825 | Stale Template Initializer Selector Can Break Proxy Deployment | accepted | Low | |
| F-2026-1824 | deployProxy Executes initialize() Before Proxy Code Is Deployed | fixed | Low | |
| F-2026-1896 | CREATE2 Address Prediction Is Bound to msg.sender Instead of an Explicit Deployer | fixed | Low | |
| F-2026-1871 | Pagination Helper Returns prev and next in Reversed Order | fixed | Observation | |
| F-2026-1870 | Non-Deterministic CREATE2 Address Due to Live Beacon State in Salt Derivation | fixed | Observation | |
| F-2026-1868 | IProxyFactory Does Not Fully Represent the Factory’s External API | 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/-/tree/main/contracts/contracts-registry/contracts→ |
| Commit | 89bb63b74e88c7e3b1602f4801d682e634b6a521 |
| Retest V1 | a64e3139c3a139da5bc3ff81c4d25fb35212c7f2 |
| Retest V2 | 048a0308e34fa43f19402a7efd69064475fa2310 |
| Retest V3 | d1535bacb0ee03fc987339c58324b34c3db14687 |
| Final Commit | 708ec7f98d45e3709024e950c6ac83b48fc5a9d2 |
| Whitepaper | N/A |
| Requirements | NatSpec |
| Technical Requirements | N/A |
Scope Details
- Commit
- 89bb63b74e88c7e3b1602f4801d682e634b6a521
- Retest V1
- a64e3139c3a139da5bc3ff81c4d25fb35212c7f2
- Retest V2
- 048a0308e34fa43f19402a7efd69064475fa2310
- Retest V3
- d1535bacb0ee03fc987339c58324b34c3db14687
- Final Commit
- 708ec7f98d45e3709024e950c6ac83b48fc5a9d2
- Whitepaper
- N/A
- Requirements
- NatSpec
- Technical Requirements
- N/A
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.