Introduction
We express our gratitude to the BlockRidge team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.
BlockRidge is a comprehensive RWA ecosystem that provides compliant tokenization services, creating scalable and equitable access to financing opportunities for asset owners. It enables a broad range of users to invest in various asset classes through a compliant and streamlined interface.
Document | |
|---|---|
| Name | Smart Contract Code Review and Security Analysis Report for BlockRidge |
| Audited By | Ivan Bondar, Viktor Lavrenenko |
| Approved By | Przemyslaw Swiatowiec |
| Website | https://blockridge.com/→ |
| Changelog | 10/12/2024 - Preliminary Report |
| 24/02/2025 - Final Report | |
| Platform | Base |
| Language | Solidity |
| Tags | Real World Assets (RWA); Storage; Factory; Proxy; Upgradable; Centralization |
| Methodology | https://hackenio.cc/sc_methodology→ |
Document
- Name
- Smart Contract Code Review and Security Analysis Report for BlockRidge
- Audited By
- Ivan Bondar, Viktor Lavrenenko
- Approved By
- Przemyslaw Swiatowiec
- Website
- https://blockridge.com/→
- Changelog
- 10/12/2024 - Preliminary Report
- 24/02/2025 - Final Report
- Platform
- Base
- Language
- Solidity
- Tags
- Real World Assets (RWA); Storage; Factory; Proxy; Upgradable; Centralization
- Methodology
- https://hackenio.cc/sc_methodology→
Review Scope | |
|---|---|
| Repository | https://github.com/valuit-official/valuit-smart-contracts→ |
| Commit | 4a8e151 |
| Retest Commit | eb1bb6f |
Review Scope
- Commit
- 4a8e151
- Retest Commit
- eb1bb6f
Audit Summary
The system users should acknowledge all the risks summed up in the risks section of the report
Documentation quality
Functional requirements are complete.
Project purpose is described.
Use cases are provided.
Project features are described.
Interactions are described.
Technical requirements are provided.
Run instructions are provided.
Technical specification is sufficient.
The NatSpec documentation is sufficient.
Authorization and roles are provided.
Code quality
The development environment is configured.
Test coverage
Code coverage of the project is 56.32% (branch coverage).
Deployment and basic user interactions are covered with tests.
Interactions by several users are tested.
Not all branches are covered with tests.
System Overview
BlockRidge is a Real-World Asset (RWA) ecosystem designed to provide compliant tokenization services, facilitating financing opportunities for asset owners and offering investors access to a variety of asset classes through a regulatory-compliant interface. The platform's modular and scalable architecture comprises interconnected components that ensure compliance and flexibility across all operations.
At the core of BlockRidge is a compliance framework that enforces regulatory requirements by allowing dynamic configuration of rules tailored to specific jurisdictions or asset types. The platform also utilizes blockchain standards for identity management, maintaining a registry of verified identities and handling claims and attestations from trusted issuers, thereby ensuring that only compliant and verified users can participate in transactions.
BlockRidge offers escrow services for handling token deposits and settlements between users, supporting multiple stablecoins and administrative fee management. This provides flexible transaction mechanisms while maintaining compliance in financial operations. Additionally, the system enables the creation and management of funds, allowing asset owners to deploy new investment opportunities with specific parameters such as property type, valuation, and projected yield. It manages investment limits and financial metrics, supporting the compliant distribution of dividends to investors.
The tokenization module is responsible for creating and managing tokenized representations of real-world assets. By implementing compliance standards and handling token management, the platform ensures regulatory-compliant asset tokenization. BlockRidge also incorporates upgradeability mechanisms that allow contracts to be updated without changing their addresses, ensuring longevity and adaptability while maintaining continuous operation.
To ensure that only authorized entities can perform sensitive operations, the platform enforces role-based access controls. Key roles include owners, who control modules and perform administrative functions; agents, who execute operations such as settlements, identity verification, and token management; and compliance officers, who manage compliance modules and enforce regulatory adherence.
The modular design of BlockRidge promotes integration with other systems and adherence to protocols such as ERC-734, ERC-735, and ERC-3643. This ensures compatibility with various wallets, identity providers, and compliance services, fostering a versatile and interconnected ecosystem.
Files in Scope
compliance/modular:
modules:
AbstractModule.sol: Defines the base functionality for compliance modules, handling the binding and unbinding of compliance contracts. It includes modifiers to restrict function calls to bound compliance contracts only, establishing a secure link between the module and Modular Compliance.
AbstractModuleUpgradeable.sol: An upgradeable version of
AbstractModule, implementing OpenZeppelin's upgradeability features. This contract is structured to allow compliance modules to be upgraded without disrupting the compliance binding status, which is maintained in a custom storage structure.CountryAllowModule.sol: A compliance module that manages country-specific allowances, enabling or disabling token manipulation permissions for users based on their country. It facilitates adding or removing countries from the allowed list, ensuring jurisdictional compliance.
CountryRestrictModule: A compliance module that manages country-specific restrictions, enabling or disabling token manipulation permissions for users based on their country.
HoldTimeModule.sol: Compliance module that enforces a minimum hold period for tokens. Users cannot transfer tokens until the hold time has passed, ensuring adherence to time-based restrictions.
SupplyLimitModule.sol: Compliance module that restricts the total supply of tokens that can be minted for a given compliance contract, preventing excessive token issuance.
ModuleProxy.sol: A proxy contract based on the ERC1967 standard, used to manage upgradeable modules within the Modular Compliance architecture. It facilitates upgrading module implementations while preserving their state.
MaxBalanceModule.sol: Compliance module that restricts the maximum token balance each user can hold. It also allows pre-setting balances to ensure compliant behavior across accounts and manages identity-based balances for transfers and compliance checks.
IModule.sol: Interface defining the standard for all compliance modules. It includes functions for binding and unbinding compliance contracts, performing transfer, mint, and burn actions, and conducting compliance checks.
ModularCompliance.sol: The main contract for Modular Compliance, implementing
IModularComplianceand inheritingOwnableUpgradeableandMCStorage. It allows the owner to bind/unbind tokens, add/remove modules, and manage module interactions while ensuring that token transfers comply with specified rules.MCStorage.sol: Storage contract for the Modular Compliance system, holding the token bound to the compliance contract and the list of bound modules. It includes mappings to track the binding status of modules and a variable to manage the wrapper status.
IModularCompliance.sol: Interface defining the Modular Compliance contract, which manages compliance rules for token transfers by binding tokens and modules, adding/removing modules, and facilitating module interactions. It outlines functions for enforcing compliance and events to track module interactions.
escrow:
EscrowController.sol: The main escrow contract managing token deposits and settlements for compliant users. It supports various stablecoins, applies an admin fee, and enables secure fund transfers for verified investors.
EscrowProxy.sol: A proxy contract for managing the upgradeability of the escrow implementation. It allows ownership control and maintenance mode, facilitating safe upgrades of the escrow contract.
EscrowStorage.sol: Storage contract for the escrow system, holding data such as admin wallet, admin fee, stablecoin mappings, and investor orders. It serves as the data layer for
Escrow.solto separate logic and storage.IEscrowController: Interface for the
EscrowControllercontract.TransferHelper.sol: A utility library for safely interacting with ERC20 tokens and transferring ETH. It provides functions for safe approvals, transfers, and
transferFromoperations, ensuring that token operations either succeed or revert.
factory:
FactoryProxy.sol: A proxy contract for managing the upgradeability of the fund factory implementation. It includes basic authorization controls for ownership, allows the owner to upgrade the implementation, and enables maintenance mode for controlled operations.
FundFactory.sol: The main contract for creating funds and equity configurations. It interacts with proxy contracts to deploy and initialize new fund or equity configuration instances based on stored implementations. It also includes a batch whitelisting function for user identities.
FundFactoryStorage.sol: Storage contract for
FundFactory, holding references to the main factory address, fund and equity configuration implementations, and proxy address. It serves as a data layer, separating storage from the factory logic inFundFactory.sol.TREXFactory.sol: Main contract for deploying the TREX token suite, which includes identity registries, compliance modules, claim topics, and trusted issuer registries. It uses the CREATE2 opcode to predetermine deployment addresses, facilitating standardized deployments across EVM-compatible chains.
ITREXFactory.sol: Interface defining the TREX Factory contract's core functionalities for deploying and managing tokenization suite contracts. It includes data structures for token and claim details and function signatures for deploying tokens and setting up compliant infrastructure.
fund:
EquityConfig.sol: A contract for managing equity configurations of a fund. It initializes parameters like minimum and maximum investment, valuation, projected yield, and Debt-to-Equity ratio. It allows authorized agents to update the fund's valuation.
EquityConfigStorage.sol: Storage contract for
EquityConfig, holding data on fund name, Debt-to-Equity ratio, token address, and valuation metrics. This contract separates data storage from logic, supporting upgradeable architecture.Fund.sol: The main contract for managing fund-related parameters and operations. It initializes with key details like property type, NAV (Net Asset Value), and projected yield. It allows token agents to update NAV, and distribute dividends in stablecoins to investors.
FundStorage.sol: Storage contract for
Fund, holding key data like fund name, CUSIP, NAV, projected yield, and investment limits. This contract is used as a data layer forFund.sol, maintaining the separation of storage from operational logic.IFactory.sol: Interface for a factory contract, providing an external view of the factory owner's address. This is likely used to confirm authorization and ensure that only the owner can access certain functions.
IFund.sol: Interface for the
Fundcontract, providing a function signature for retrieving the Net Asset Value (NAV).ITKN.sol: Interface for a token contract that includes functions to check if an address is an agent, get the token’s name, and retrieve its total supply. This interface is used in the
FundandEquityConfigcontracts to validate agents and access token data.
onchainID:
factory:
IdFactory.sol: Main contract for managing the creation and linkage of on-chain identities (ONCHAINID) for both users and tokens. It allows the deployment of identity contracts using CREATE2 for deterministic addresses, linking and unlinking wallets to identities, and registering token factories authorized to create token identities.
IIdFactory.sol: Interface defining the core functions of the
IdFactory, including creating identities, linking/unlinking wallets, adding/removing token factories, and providing view functions to retrieve linked identities, wallets, and token statuses.
interface:
IClaimIssuer.sol: Interface for a claim issuer, allowing claim revocation and validation.
IERC734.sol: Interface defining ERC-734 standard functions for identity management, including key handling and execution approvals.
IERC735.sol: Interface defining ERC-735 standard functions for claim management within an identity, including claim addition, removal, and retrieval.
IIdentity.sol: Composite interface for both
IERC734(Key Holder) andIERC735(Claim Holder), establishing a comprehensive identity standard.IImplementationAuthority.sol: Interface for managing and retrieving the current implementation of a proxy, with upgrade capabilities.
proxy:
IdentityProxy.sol: Acts as a proxy for the
Identitycontract. It delegates calls to an implementation contract whose address is managed by anImplementationAuthority. This structure ensures that the proxy can dynamically update its implementation, enabling upgradability while maintaining the same address.ImplementationAuthority.sol: Manages the current implementation address for the
Identityproxy. It can be updated only by the owner, ensuring centralized control over the address of the actual implementation contract, which is beneficial for maintaining control over upgrades.
storage:
Storage.sol: Contains the storage layout for the
Identitycontract, including mappings for keys, executions, and claims. This setup enables modular contract structure and compatibility with proxy-based upgradability. The_initializedand_canInteractflags control initialization status and interaction permissions.Structs.sol: Defines data structures used across the identity system.
verifiers:
Verifier.sol: Manages trusted issuers and required claim topics for identity verification, ensuring compliance by validating claims from approved sources.
version:
Version.sol: Provides a simple versioning function that returns the version string of the implementation contract, allowing external parties to check which version of the contract logic is being used.
Identity.sol: Implements an ERC-734 "KeyHolder" and ERC-735 "ClaimHolder" identity contract that supports identity management, including key management for various roles (management, action, claim signer, and encryption). It allows the addition, execution, and management of claims and keys for each identity. This contract is intended to be deployed as an upgradable contract with modular storage, making it suitable for managing complex identity structures with separate, upgradeable logic.
ClaimIssuer.sol: This contract extends the
Identitycontract to provide claim issuance and revocation functionalities. It enables claim validation and revocation based on signatures, allowing the contract to check the validity and status of claims linked to identities.
proxy:
authority:
IAFactory.sol: Manages deployment of new instances of
TREXImplementationAuthority, ensuring they are created by the authorized factory.IIAFactory.sol: Interface defining
IAFactorycontract functions.ITREXImplementationAuthority.sol: Interface defining
TREXImplementationAuthoritycontract functions, including managing contract versions and associated addresses.TREXImplementationAuthority.sol: Manages contract versions and maintains the current set of
TREXContractsimplementations used across the T-REX suite. Allows updating and fetching contract versions.
interface:
IImplementationAuthority.sol: Interface providing a function to retrieve the current implementation address in use.
IProxy.sol: Interface for a proxy contract, allowing the setting and retrieval of the current implementation authority address.
AbstractProxy.sol: Abstract contract providing core proxy functions to delegate calls to an implementation authority.
ClaimTopicsRegistryProxy.sol: Proxy for the Claim Topics Registry, delegating calls to the appropriate logic contract set by the implementation authority.
IdentityRegistryProxy.sol: Proxy for the Identity Registry, initializing with registries and delegating to the implementation logic.
IdentityRegistryStorageProxy.sol: Proxy for storing identity data, initialized with implementation authority and delegates to the storage logic.
ModularComplianceProxy.sol: Proxy for the compliance module, which delegates all calls to the compliance logic contract.
ProxyV1.sol: A proxy contract allowing upgradeability and maintenance control, delegating calls to an implementation authority.
TokenProxy.sol: Proxy contract for token management, initialized with identity registry, compliance module, and token metadata, delegating to token logic.
TrustedIssuersRegistryProxy.sol: Proxy for the Trusted Issuers Registry, initialized with an implementation authority and delegating calls to the trusted issuers logic.
registry:
implementation:
ClaimTopicsRegistry.sol: Provides functionality to manage claim topics that are necessary for identity verification.
IdentityRegistry.sol: Manages user identities and verifies them based on trusted issuers and claim topics.
IdentityRegistryStorage.sol: Manages the storage of identities, including adding, modifying, and removing identities.
TrustedIssuersRegistry.sol: Manages trusted claim issuers and the topics they support.
interface:
IClaimTopicsRegistry.sol: Interface for managing claim topics, allowing only the owner to add or remove claim topics.
IIdentityRegistry.sol: Interface for managing identities within the registry, including functions to register, update, and verify identities.
IIdentityRegistryStorage.sol: Interface defining storage management functions for identities, such as adding, removing, and updating stored identities.
ITrustedIssuersRegistry.sol: Interface for managing trusted claim issuers and associated claim topics, only callable by the contract owner.
storage:
CTRStorage.sol: Provides internal storage for managing claim topics required for identity verification.
IRSStorage.sol: Manages identity-related data, specifically mapping between user addresses and identities (country and identity contract).
IRStorage.sol: Stores references to key registry contracts: Claim Topics Registry, Trusted Issuers Registry, and Identity Registry Storage.
TIRStorage.sol: Contains data structures for managing trusted issuers, including arrays and mappings for claim topics associated with each issuer.
roles:
AgentRole.sol: Implements role-based access for agents and "TA" roles, allowing only the owner to assign and remove these roles.
AgentRoleUpgradeable.sol: Upgradeable contract version of
AgentRole, also enabling role-based access for agents and "TA" roles, managed by the owner.Roles.sol: Library providing internal functions to add, remove, and check roles for specific addresses.
token:
Token.sol: Implements the token logic for minting, burning, transfers, and compliance checks. It integrates identity and compliance modules to enforce regulatory compliance.
TokenStorage.sol: Defines essential variables and mappings to manage ERC20 balances, allowances, and the token's freeze/pause status, along with identity and compliance contract references.
IToken.sol: Interface for the token contract, including functions for token management (freeze, pause, transfer) and linking identity and compliance registries.
VERC20.sol: Upgradeable ERC20 token contract, with basic initialization and restricted mint/burn functions.
wrapper:
Wrapper.sol: Manages the creation and wrapping of ERC3643 tokens into ERC20 tokens, allowing for minting and burning to simulate a bridge between token standards.
WrapperStorage.sol: Provides storage structures for managing token wrappers, tracking mappings between ERC20 and ERC3643 tokens, and logging locked tokens.
Privileged roles
ModularCompliance.sol:
Owner:
Can bind or unbind tokens to the compliance contract.
Can add or remove modules, subject to module binding checks.
Authorized to call module functions and set the wrapper address.
Only Token (modifier):
Ensures that only the token bound to the compliance contract can trigger certain functions, like
transferred,created, anddestroyed, which manage compliance checks for token actions.
AbstractModule.sol:
onlyBoundCompliance (modifier):
Restricts function access to only those compliance contracts that have been explicitly bound to this module.
onlyComplianceCall (modifier):
Allows function calls exclusively from addresses associated with bound compliance contracts.
AbstractModuleUpgradeable.sol:
Owner:
Authorized to upgrade the module's implementation.
onlyBoundCompliance (modifier):
Limits access to functions to bound compliance contracts.
onlyComplianceCall (modifier):
Ensures only bound compliance contracts can invoke specific functions within this module.
CountryAllowModule.sol:
Owner (through
onlyComplianceCallmodifier):Permitted to batch allow or disallow countries, and to individually allow or disallow countries for token manipulation.
onlyComplianceCall (modifier):
Limits function calls for managing country allowances to bound compliance contracts, ensuring that only authorized compliance contracts can enforce these restrictions.
HoldTimeModule.sol:
onlyComplianceCall (modifier):
Ensures that only the compliance contract bound to this module can set the hold time for tokens.
Owner:
Authorized to set or update the hold time for tokens, enforcing transfer restrictions based on the specified period.
SupplyLimitModule.sol:
onlyComplianceCall (modifier):
Limits the ability to set the supply limit to the bound compliance contract, safeguarding against unauthorized adjustments.
Owner:
Permitted to define the maximum supply limit, capping token issuance to prevent oversupply.
MaxBalanceModule.sol:
onlyComplianceCall (modifier):
Allows only the compliance contract bound to this module to set or modify maximum balances, ensuring authorized access.
Owner (only compliance owner can access):
Can set maximum balance limits and pre-set balances for specific identities.
Authorized to complete the preset configuration for a compliance contract, allowing it to bind with the module.
Escrow.sol:
Owner:
Can set the admin fee and admin wallet.
Authorized to rescue any ERC20 tokens from the contract except for pending amounts in stablecoins.
Agent:
Can perform the
settlementfunction, finalizing an order and transferring the amount to themselves and the admin wallet (less the admin fee). Ensures that only authorized agents can complete settlements.
EscrowProxy.sol
Proxy Owner (modifier
onlyProxyOwner):Controls upgradeability by authorizing changes to the escrow implementation.
Can enable or disable maintenance mode, restricting certain operations during maintenance.
Can transfer proxy ownership to a new owner, ensuring controlled access over upgradeability and management functions.
FactoryProxy.sol:
Proxy Owner (modifier
onlyProxyOwner):Controls upgradeability by authorizing updates to the factory implementation.
Can enable or disable maintenance mode, restricting operations during maintenance.
Has the authority to transfer proxy ownership, ensuring controlled management over the factory upgradeability.
FundFactory.sol:
Owner (determined by
masterFactoryowner):Authorized to set fund and equity configuration implementations.
Permitted to create new fund and equity configuration contracts, ensuring only the factory owner can deploy new configurations.
Agent:
Can perform
batchWhitelistfor bulk registration of user identities within an identity registry associated with a token, provided they are registered as an agent within the registry.
TREXFactory.sol:
Owner:
Authorized to deploy a new TREX token suite using
deployTREXSuite, allowing comprehensive setup of identity, compliance, and registry components for a compliant token ecosystem.Can set or update the
implementationAuthorityandidFactory, ensuring that only authorized individuals can control core components related to token implementations and identity management.Has exclusive access to
recoverContractOwnership, which is typically used to transfer ownership of contracts previously owned by the factory, such as the Identity Registry Storage (IRS).
Agent (within Identity Registry and Token context):
The owner can add agents to the Identity Registry and Token, giving agents permission to perform functions related to identity verification and token management as required.
EquityConfig.sol:
Factory:
Initializes the contract with key fund parameters like minimum investment, maximum investment, and valuation metrics.
Token Agent:
Authorized to update the fund's valuation by calling
setValuation, ensuring that only designated agents can alter financial metrics of the fund.
Fund.sol
Factory:
Initializes the fund with key parameters such as NAV and investment limits.
Token Agent:
Can update the NAV using
setNAV, ensuring that only authorized agents can make adjustments to the fund's asset valuation.Authorized to distribute dividends to a list of investors using
shareDividend, allowing controlled and compliant distribution of fund proceeds.
Identity.sol
Management Key Holders:
Management key holders can perform actions such as executing transactions, adding and removing keys, approving executions, and modifying claims within the identity.
Management key holders can also directly interact with the identity contract for actions that affect the identity as a whole, including the execution of internal transactions.
Action Key Holders:
Action key holders can initiate external calls through the
executefunction. These keys have lower permissions than management keys and are intended for actions that do not modify the state of the identity.
Claim Key Holders:
Claim key holders are authorized to add and remove claims on behalf of the identity. They can issue claims to other identities and sign claims to validate user assertions within the
addClaimandremoveClaimfunctions.
Encryption Key Holders:
Encryption key holders manage data encryption, useful for holding claims or sensitive identity data securely. These keys are primarily used in contexts that require identity data to be encrypted before storage or transmission.
ClaimIssuer.sol
Manager (Identity Management Key):
The
onlyManagermodifier restricts claim revocation functions to only be called by accounts with a management key. Managers can revoke claims by their ID or revoke signatures, maintaining control over the status of each claim.Managers can also check the validity of claims through
isClaimValid, ensuring a robust verification process.
Claim Signers (Claim Key):
Claim signers are authorized to verify claims based on their signatures using the
isClaimValidfunction, provided they have valid claim keys.
Verifier.sol:
Owner:
Has authority to add and remove trusted issuers, manage required claim topics, and update claim topics for trusted issuers.
AbstractProxy.sol:
Implementation Authority:
Only the current implementation authority can set a new implementation authority address.
ProxyV1.sol:
Proxy Owner:
Can transfer proxy ownership, toggle maintenance mode, upgrade implementation, and upgrade with initialization call.
IAFactory.sol:
TREX Implementation Authority:
Only the reference implementation authority can deploy a new instance of
TREXImplementationAuthoritythrough this factory.
TREXImplementationAuthority.sol:
Owner:
Can add new T-REX contract versions, set the
TREXFactoryandIAFactoryaddresses, and update the active version used by proxies.
Token Owner:
Only the token's owner can change the implementation authority for all linked contracts, including token proxies, registries, and compliance modules.
ClaimTopicsRegistry.sol:
Owner:
Can add or remove claim topics necessary for identity verification.
IdentityRegistry.sol:
Owner:
Can set the addresses of the Identity Registry Storage, Claim Topics Registry, and Trusted Issuers Registry.
Agent:
Can register, update, and delete identities.
Can update the country of residence for an identity.
IdentityRegistryStorage.sol:
Owner:
Initializes the contract and binds Identity Registries.
Agent:
Can add, modify, and remove identities in storage.
Can modify investor country information.
TrustedIssuersRegistry.sol:
Owner:
Can add, remove, or update trusted issuers and their associated claim topics.
AgentRole.sol / AgentRoleUpgradeable.sol:
Owner:
Can assign and remove both "Agent" and "TA" roles, providing privileged access to designated accounts.
Agent:
Granted permissions to execute functions restricted to the "Agent" role.
TA:
Granted permissions to execute functions restricted to the "TA" role and participate in actions alongside "Agent" role holders.
Token.sol:
Owner:
Authorized to initialize the token, set token information (name, symbol, onchain ID), configure Identity Registry and Compliance contracts, and manage ownership.
Agent:
Permitted to pause/unpause the token, freeze/unfreeze addresses and tokens, recover addresses, and enforce transfers, minting, and burning.
Agents (TAs):
Can perform partial token freezes and unfreezes and are allowed in certain transfer operations that require compliance with token policies.
VERC20.sol:
Owner:
Authorized to mint and burn tokens, restricted to only the owner for controlled token supply management.
Wrapper.sol:
Owner:
Has authority to set the on-chain ID for the wrapper.
Agents (in ERC3643):
Only agents associated with the ERC3643 token can create a wrapped ERC20 token through the
createWrapTokenfunction, ensuring controlled access to the wrapping process.
Potential Risks
Reliance on Off-Chain Systems for Key Settings: The project heavily depends on off-chain components to manage and define critical settings, such as NAV and valuation updates, which are essential for the accurate operation. Since the off-chain infrastructure is not part of the audit, its security, reliability, and integrity cannot be guaranteed. Any issues or vulnerabilities within the off-chain systems could lead to incorrect on-chain behavior, manipulation of key metrics, or operational disruptions.
Single Points of Failure and Control: The project is fully or partially centralized, introducing single points of failure and control. This centralization can lead to vulnerabilities in decision-making and operational processes, making the system more susceptible to targeted attacks or manipulation.
Administrative Key Control Risks: The digital contract architecture relies on administrative keys for critical operations. Centralized control over these keys presents a significant security risk, as compromise or misuse can lead to unauthorized actions or loss of funds.
Dynamic Array Iteration Gas Limit Risks: The project iterates over large dynamic arrays, which leads to excessive gas costs, risking denial of service due to out-of-gas errors, directly impacting contract usability and reliability.
External Calls in Iteration Risks: Making external calls within loops increases the risk of gas exhaustion, potentially leading to failed transactions and reduced contract reliability, especially when processing large datasets.
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.
Single Entity Upgrade Authority: The ecosystem grants a single entity the authority to implement upgrades or changes. This centralization of power risks unilateral decisions that may not align with the community or stakeholders' interests, undermining trust 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.
Frontrunning During The Initialization Of The Implementation Contract: If the deployment and the initialization of the proxy implementation contracts will not be handled within one transaction, it can cause the frontrunning of the initialize functions by the attacker which will lead to unexpected system behavior.
Not a Traditional Escrow: Despite its name, the contract does not escrow (hold) user funds upon deposit. Instead, a user’s stablecoins remain in the user’s wallet until “settlement,” at which time the contract attempts to pull stablecoins via safeTransferFrom. If the user depletes or revokes approval of their stablecoin allowance after calling deposit, the settlement will revert. This design is intentional, but it differs from a typical “deposit and lock” escrow mechanism.
In the EscrowController contract, the redemptionAndBurn function relies on token agents to accurately provide the _principalAmount and _profitAmount. If the agent includes the profit in _principalAmount but sets _profitAmount to zero, the protocol will not apply the redemption fee, resulting in uncollected fees. This places responsibility on agents to report profits correctly, introducing risks of fee evasion, either through negligence or intentional manipulation.
Findings
Code ― | Title | Status | Severity | |
|---|---|---|---|---|
| F-2024-7530 | Lack of Wrapper Status Validation in createWrapToken and toERC20 Functions | fixed | Medium | |
| F-2024-7260 | MaxBalanceModule Restriction Interferes with Wrapping Process in ERC3643 Token System | fixed | Medium | |
| F-2024-7243 | Incorrect Order of Checks in canTransfer Leads to Potential Bypass of HoldTimeModule Restrictions | fixed | Medium | |
| F-2024-7528 | Stale Total Supply Data Leads to Incorrect Fee Calculation | fixed | Medium | |
| F-2024-7527 | Precision Loss in Fee Calculation for toERC20 and toERC3643 Functions Leads to Incorrect or Zero Fees | fixed | Medium | |
| F-2024-7282 | Inadequate Validation in deposit Function Leading to Potential Denial of Service | fixed | Medium | |
| F-2024-7529 | Overwriting Parameters When Creating Funds or Equity Configurations for the Same Token | fixed | Low | |
| F-2024-7285 | Mismanagement of Compliance Preset Status in MaxBalanceModule Allows Circumvention of Max Balance Enforcement | fixed | Low | |
| F-2024-7262 | Lack of Wrapper Disabling Mechanism in ModularCompliance Contract | fixed | Low | |
| F-2024-7244 | Compliance Gap in ERC3643 to ERC20 Wrapping Process | fixed | Low |
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://github.com/valuit-official/valuit-smart-contracts→ |
| Commit | 4a8e151 |
| Retest Commit | eb1bb6f |
| Whitepaper | NA |
| Requirements | https://docs.valuit.com/Tokenization/what-is-valuit→ |
| Technical Requirements | https://docs.valuit.com/Tokenization/what-is-valuit;→ |
Scope Details
- Commit
- 4a8e151
- Retest Commit
- eb1bb6f
- Whitepaper
- NA
- Technical Requirements
- https://docs.valuit.com/Tokenization/what-is-valuit;→
Asset | Type |
|---|---|
| compliance/modular/IModularCompliance.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/MCStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/ModularCompliance.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/modules/AbstractModule.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/modules/AbstractModuleUpgradeable.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/modules/CountryAllowModule.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/modules/CountryRestrictModule.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/modules/HoldTimeModule.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/modules/IModule.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/modules/MaxBalanceModule.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/modules/ModuleProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| compliance/modular/modules/SupplyLimitModule.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| escrow/EscrowController.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| escrow/EscrowControllerProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| escrow/EscrowStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| escrow/IEscrowController.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| escrow/TransferHelper.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| factory/FactoryProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| factory/FundFactory.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| factory/FundFactoryStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| factory/IFundFactory.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| factory/ITREXFactory.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| factory/TREXFactory.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| fund/EquityConfig.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| fund/EquityConfigStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| fund/Fund.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| fund/FundStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| fund/IFactory.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| fund/IFund.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| fund/ITKN.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/ClaimIssuer.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/factory/IdFactory.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/factory/IIdFactory.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/Identity.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/interface/IClaimIssuer.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/interface/IERC734.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/interface/IERC735.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/interface/IIdentity.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/interface/IImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/proxy/IdentityProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/proxy/ImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/storage/Storage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/storage/Structs.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/verifiers/Verifier.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| onchainID/version/Version.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/AbstractProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/authority/IAFactory.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/authority/IIAFactory.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/authority/ITREXImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/authority/TREXImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/ClaimTopicsRegistryProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/IdentityRegistryProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/IdentityRegistryStorageProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/interface/IImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/interface/IProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/ModularComplianceProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/ProxyV1.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/TokenProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| proxy/TrustedIssuersRegistryProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/implementation/ClaimTopicsRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/implementation/IdentityRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/implementation/IdentityRegistryStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/implementation/TrustedIssuersRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/interface/IClaimTopicsRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/interface/IIdentityRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/interface/IIdentityRegistryStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/interface/ITrustedIssuersRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/storage/CTRStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/storage/IRSStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/storage/IRStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| registry/storage/TIRStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| roles/AgentRole.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| roles/AgentRoleUpgradeable.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| roles/Roles.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| token/IToken.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| token/Token.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| token/TokenStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| token/VERC20.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| wrapper/Wrapper.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| wrapper/WrapperProxy.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
| wrapper/WrapperStorage.sol [https://github.com/valuit-official/valuit-smart-contracts] | Smart Contract |
Asset
- compliance/modular/IModularCompliance.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/MCStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/ModularCompliance.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/modules/AbstractModule.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/modules/AbstractModuleUpgradeable.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/modules/CountryAllowModule.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/modules/CountryRestrictModule.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/modules/HoldTimeModule.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/modules/IModule.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/modules/MaxBalanceModule.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/modules/ModuleProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- compliance/modular/modules/SupplyLimitModule.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- escrow/EscrowController.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- escrow/EscrowControllerProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- escrow/EscrowStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- escrow/IEscrowController.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- escrow/TransferHelper.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- factory/FactoryProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- factory/FundFactory.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- factory/FundFactoryStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- factory/IFundFactory.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- factory/ITREXFactory.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- factory/TREXFactory.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- fund/EquityConfig.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- fund/EquityConfigStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- fund/Fund.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- fund/FundStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- fund/IFactory.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- fund/IFund.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- fund/ITKN.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/ClaimIssuer.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/factory/IdFactory.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/factory/IIdFactory.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/Identity.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/interface/IClaimIssuer.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/interface/IERC734.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/interface/IERC735.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/interface/IIdentity.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/interface/IImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/proxy/IdentityProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/proxy/ImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/storage/Storage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/storage/Structs.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/verifiers/Verifier.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- onchainID/version/Version.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/AbstractProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/authority/IAFactory.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/authority/IIAFactory.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/authority/ITREXImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/authority/TREXImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/ClaimTopicsRegistryProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/IdentityRegistryProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/IdentityRegistryStorageProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/interface/IImplementationAuthority.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/interface/IProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/ModularComplianceProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/ProxyV1.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/TokenProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- proxy/TrustedIssuersRegistryProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/implementation/ClaimTopicsRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/implementation/IdentityRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/implementation/IdentityRegistryStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/implementation/TrustedIssuersRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/interface/IClaimTopicsRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/interface/IIdentityRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/interface/IIdentityRegistryStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/interface/ITrustedIssuersRegistry.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/storage/CTRStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/storage/IRSStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/storage/IRStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- registry/storage/TIRStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- roles/AgentRole.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- roles/AgentRoleUpgradeable.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- roles/Roles.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- token/IToken.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- token/Token.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- token/TokenStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- token/VERC20.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- wrapper/Wrapper.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- wrapper/WrapperProxy.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Asset
- wrapper/WrapperStorage.sol [https://github.com/valuit-official/valuit-smart-contracts]
Type
- Smart Contract
Assets in Scope
Appendix 3. Additional Valuables
Verification of System Invariants
During the audit of BlockRidge, Hacken followed its methodology by performing fuzz-testing on the project's main functions. Echidna →, a tool used for fuzz-testing, was employed to check how the protocol behaves under various inputs. Due to the complex and dynamic interactions within the protocol, unexpected edge cases might arise. Therefore, it was important to use fuzz-testing to ensure that several system invariants hold true in all situations.
Fuzz-testing allows the input of many random data points into the system, helping to identify issues that regular testing might miss. A specific Echidna fuzzing suite was prepared for this task, and throughout the assessment, 59 invariants were tested over 5,000,000 runs. This thorough testing ensured that the system works correctly even with unexpected or unusual inputs.
Invariant | Test Result | Run Count |
|---|---|---|
| Stateful Invariants | ||
| [ERC20 Invariants] | ||
| Total supply should change only by means of VERC20::mint() or VERC20::burn() | Passed | 5M |
| User balance must not exceed total supply | Passed | 5M |
| Sum of users balance must not exceed total supply | Passed | 5M |
| Address zero should have zero balance | Passed | 5M |
| Transfers to zero address should not be allowed via VERC20::transfer() and VERC20::transferFrom() functions | Passed | 5M |
| Self transfers should not break accounting via VERC20::transfer() and VERC20::transferFrom() functions | Passed | 5M |
| Transfers for more than available balance should not be allowed via VERC20::transfer() and VERC20::transferFrom() functions | Passed | 5M |
| Zero amount transfers should not break accounting via VERC20::transfer() and VERC20::transferFrom() functions | Passed | 5M |
| Transfers via VERC20::transfer() and VERC20::transferFrom() functions should update accounting correctly | Passed | 5M |
| Approving tokens via VERC20::approve() should set correct allowances | Passed | 5M |
| Transferring tokens via VERC20::transferFrom() should decrease allowance | Passed | 5M |
| Minting tokens via VERC20::mint() should update user balance and total supply | Passed | 5M |
| Burning tokens via VERC20::burn() should update user balance and total supply | Passed | 5M |
| Allowance should be modified correctly via VERC20::increaseAllowance/decreaseAllowance() functions | Passed | 5M |
| [Initialization Invariants] | ||
| Token::initialize() should not revert when it is passed the correct addresses of Identity and Compliance contracts | Passed | 5M |
| Token::initialize() should not revert when it is passed the correct not empty symbol and name parameters | Passed | 5M |
| Token::initialize() should not revert when it is passed the correct decimals value which is not bigger than 18 | Passed | 5M |
| IdentityRegistry::init() should not revert when it is passed the correct trustedIssuersRegistry, claimTopicsRegistry, and _identityStorage addresses | Passed | 5M |
| Identity::initialize() should not revert when it is passed the correct initialManagementKey address | Passed | 5M |
| Fund::init() should not revert when it is passed the correct non-zero token address and data value, which cannot be smaller than 4 bytes | Passed | 5M |
| EquityConfig::init() should not revert when it is passed the correct _data value, which cannot be smaller than 5 bytes | Passed | 5M |
| [Authorization Invariants] | ||
| VERC20::mint() should revert if the caller of the function is not the contract owner | Passed | 5M |
| VERC20::burn() should revert if the caller of the function is not the contract owner | Passed | 5M |
| Token::setName() should revert if the caller of the function is not the contract owner | Passed | 5M |
| Token::setSymbol() should revert if the caller of the function is not the contract owner | Passed | 5M |
| Token::setOnchainID() should revert if the caller of the function is not the contract owner | Passed | 5M |
| Token::setIdentityRegistry() should revert if the caller of the function is not the contract owner | Passed | 5M |
| Token::setCompliance() should revert if the caller of the function is not the contract owner | Passed | 5M |
| Token::mint() should revert if the caller of the function is not related to the agent role | Passed | 5M |
| Token::setAddressFrozen() should revert if the caller of the function is not related to the AgentRole | Passed | 5M |
| Token::freezePartialTokens() should revert if the caller of the function is not related to the AgentRole or TARole | Passed | 5M |
| Token::pause() should revert if the caller of the function is not related to the AgentRole | Passed | 5M |
| Token::unpause() should revert if the caller of the function is not related to the AgentRole | Passed | 5M |
| IdentityRegistry::updateIdentity() should revert if the caller of the function is not related to the AgentRole | Passed | 5M |
| IdentityRegistry::setIdentityRegistryStorage() should revert if the caller of the function is not the contract owner | Passed | 5M |
| IdentityRegistry::setClaimTopicsRegistry() should revert if the caller of the function is not the contract owner | Passed | 5M |
| IdentityRegistry::setTrustedIssuersRegistry() should revert if the caller of the function is not the contract owner | Passed | 5M |
| Fund::setNAV() should always revert if the caller of the function is not the token agent | Passed | 5M |
| Fund::shareDividend() should always revert if the caller of the function is not the token agent | Passed | 5M |
| Fund::setMinInvestment() should always revert if the caller of the function is not the token agent | Passed | 5M |
| Fund::setMaxInvestment() should always revert if the caller of the function is not the token agent | Passed | 5M |
| Wrapper::setOnchainID() should always revert if the caller of the function is not the owner of the masteryFactory contract | Passed | 5M |
| Wrapper::setFundFactory() should always revert if the caller of the function is not the owner of the masteryFactory contract | Passed | 5M |
| Wrapper::setEscrowController() should always revert if the caller of the function is not the owner of the masteryFactory contract | Passed | 5M |
| [Roles Management Invariants] | ||
| Roles::add() should always add the role which was not previously added | Passed | 5M |
| Roles::remove() should always remove the role from the account which has this role | Passed | 5M |
| Roles::has() should always return the existing role of a non address(0) account. | Passed | 5M |
| [ClaimTopicsRegistry Management Invariants] | ||
| ClaimTopicsRegistry::addClaimTopic() should always add a claimTopic to the _claimTopics array | Passed | 5M |
| ClaimTopicsRegistry::addClaimTopic() should always revert if the length of the _claimTopics array exceeds 14 | Passed | 5M |
| ClaimTopicsRegistry::addClaimTopic() should always revert if the claimTopic already exists in the _claimTopics array | Passed | 5M |
| ClaimTopicsRegistry::removeClaimTopic() should always remove the claimTopic from the _claimTopics array | Passed | 5M |
| [IdFactory Management Invariants] | ||
| IdFactory::addTokenFactory() should always add a valid TokenFactory, which is not address(0) to the _tokenFactories mapping | Passed | 5M |
| IdFactory::addTokenFactory() should always revert if the address of the TokenFactory already exists in _tokenFactories mapping | Passed | 5M |
| IdFactory::removeTokenFactory() should always remove a valid existing TokenFactory address from the _tokenFactories mapping | Passed | 5M |
| IdFactory::removeTokenFactory() should always revert if the address of the TokenFactory to be deleted does not exist in the _tokenFactories mapping | Passed | 5M |
| [Fund Management Invariants] | ||
| Fund::shareDividend() should always transfer the stablecoin dividents to the specified addresses | Passed | 5M |
| [Wrapper Management Invariants] | ||
| If the _amount is zero, Wrapper::toERC20() and Wrapper::toERC3643() will always calculate orderValue and taxAmount as 0 | Passed | 5M |
| if the getTokenTotalSupply() is zero, the calculation in Wrapper::toERC20() and Wrapper::toERC3643() will always revert | Passed | 5M |
| If the NAV multiplied by 1018 is smaller than getTokenTotalSupply() value, orderValue, tokenPrice and taxAmount will be caclulated as zero in Wrapper::toERC20() and Wrapper::toERC3643()** functions | Passed | 5M |
Invariant
- Stateful Invariants
Test Result
Run Count
Invariant
- [ERC20 Invariants]
Test Result
Run Count
Invariant
- Total supply should change only by means of VERC20::mint() or VERC20::burn()
Test Result
- Passed
Run Count
- 5M
Invariant
- User balance must not exceed total supply
Test Result
- Passed
Run Count
- 5M
Invariant
- Sum of users balance must not exceed total supply
Test Result
- Passed
Run Count
- 5M
Invariant
- Address zero should have zero balance
Test Result
- Passed
Run Count
- 5M
Invariant
- Transfers to zero address should not be allowed via VERC20::transfer() and VERC20::transferFrom() functions
Test Result
- Passed
Run Count
- 5M
Invariant
- Self transfers should not break accounting via VERC20::transfer() and VERC20::transferFrom() functions
Test Result
- Passed
Run Count
- 5M
Invariant
- Transfers for more than available balance should not be allowed via VERC20::transfer() and VERC20::transferFrom() functions
Test Result
- Passed
Run Count
- 5M
Invariant
- Zero amount transfers should not break accounting via VERC20::transfer() and VERC20::transferFrom() functions
Test Result
- Passed
Run Count
- 5M
Invariant
- Transfers via VERC20::transfer() and VERC20::transferFrom() functions should update accounting correctly
Test Result
- Passed
Run Count
- 5M
Invariant
- Approving tokens via VERC20::approve() should set correct allowances
Test Result
- Passed
Run Count
- 5M
Invariant
- Transferring tokens via VERC20::transferFrom() should decrease allowance
Test Result
- Passed
Run Count
- 5M
Invariant
- Minting tokens via VERC20::mint() should update user balance and total supply
Test Result
- Passed
Run Count
- 5M
Invariant
- Burning tokens via VERC20::burn() should update user balance and total supply
Test Result
- Passed
Run Count
- 5M
Invariant
- Allowance should be modified correctly via VERC20::increaseAllowance/decreaseAllowance() functions
Test Result
- Passed
Run Count
- 5M
Invariant
- [Initialization Invariants]
Test Result
Run Count
Invariant
- Token::initialize() should not revert when it is passed the correct addresses of Identity and Compliance contracts
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::initialize() should not revert when it is passed the correct not empty symbol and name parameters
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::initialize() should not revert when it is passed the correct decimals value which is not bigger than 18
Test Result
- Passed
Run Count
- 5M
Invariant
- IdentityRegistry::init() should not revert when it is passed the correct trustedIssuersRegistry, claimTopicsRegistry, and _identityStorage addresses
Test Result
- Passed
Run Count
- 5M
Invariant
- Identity::initialize() should not revert when it is passed the correct initialManagementKey address
Test Result
- Passed
Run Count
- 5M
Invariant
- Fund::init() should not revert when it is passed the correct non-zero token address and data value, which cannot be smaller than 4 bytes
Test Result
- Passed
Run Count
- 5M
Invariant
- EquityConfig::init() should not revert when it is passed the correct _data value, which cannot be smaller than 5 bytes
Test Result
- Passed
Run Count
- 5M
Invariant
- [Authorization Invariants]
Test Result
Run Count
Invariant
- VERC20::mint() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- VERC20::burn() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::setName() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::setSymbol() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::setOnchainID() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::setIdentityRegistry() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::setCompliance() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::mint() should revert if the caller of the function is not related to the agent role
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::setAddressFrozen() should revert if the caller of the function is not related to the AgentRole
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::freezePartialTokens() should revert if the caller of the function is not related to the AgentRole or TARole
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::pause() should revert if the caller of the function is not related to the AgentRole
Test Result
- Passed
Run Count
- 5M
Invariant
- Token::unpause() should revert if the caller of the function is not related to the AgentRole
Test Result
- Passed
Run Count
- 5M
Invariant
- IdentityRegistry::updateIdentity() should revert if the caller of the function is not related to the AgentRole
Test Result
- Passed
Run Count
- 5M
Invariant
- IdentityRegistry::setIdentityRegistryStorage() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- IdentityRegistry::setClaimTopicsRegistry() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- IdentityRegistry::setTrustedIssuersRegistry() should revert if the caller of the function is not the contract owner
Test Result
- Passed
Run Count
- 5M
Invariant
- Fund::setNAV() should always revert if the caller of the function is not the token agent
Test Result
- Passed
Run Count
- 5M
Invariant
- Fund::shareDividend() should always revert if the caller of the function is not the token agent
Test Result
- Passed
Run Count
- 5M
Invariant
- Fund::setMinInvestment() should always revert if the caller of the function is not the token agent
Test Result
- Passed
Run Count
- 5M
Invariant
- Fund::setMaxInvestment() should always revert if the caller of the function is not the token agent
Test Result
- Passed
Run Count
- 5M
Invariant
- Wrapper::setOnchainID() should always revert if the caller of the function is not the owner of the masteryFactory contract
Test Result
- Passed
Run Count
- 5M
Invariant
- Wrapper::setFundFactory() should always revert if the caller of the function is not the owner of the masteryFactory contract
Test Result
- Passed
Run Count
- 5M
Invariant
- Wrapper::setEscrowController() should always revert if the caller of the function is not the owner of the masteryFactory contract
Test Result
- Passed
Run Count
- 5M
Invariant
- [Roles Management Invariants]
Test Result
Run Count
Invariant
- Roles::add() should always add the role which was not previously added
Test Result
- Passed
Run Count
- 5M
Invariant
- Roles::remove() should always remove the role from the account which has this role
Test Result
- Passed
Run Count
- 5M
Invariant
- Roles::has() should always return the existing role of a non address(0) account.
Test Result
- Passed
Run Count
- 5M
Invariant
- [ClaimTopicsRegistry Management Invariants]
Test Result
Run Count
Invariant
- ClaimTopicsRegistry::addClaimTopic() should always add a claimTopic to the _claimTopics array
Test Result
- Passed
Run Count
- 5M
Invariant
- ClaimTopicsRegistry::addClaimTopic() should always revert if the length of the _claimTopics array exceeds 14
Test Result
- Passed
Run Count
- 5M
Invariant
- ClaimTopicsRegistry::addClaimTopic() should always revert if the claimTopic already exists in the _claimTopics array
Test Result
- Passed
Run Count
- 5M
Invariant
- ClaimTopicsRegistry::removeClaimTopic() should always remove the claimTopic from the _claimTopics array
Test Result
- Passed
Run Count
- 5M
Invariant
- [IdFactory Management Invariants]
Test Result
Run Count
Invariant
- IdFactory::addTokenFactory() should always add a valid TokenFactory, which is not address(0) to the _tokenFactories mapping
Test Result
- Passed
Run Count
- 5M
Invariant
- IdFactory::addTokenFactory() should always revert if the address of the TokenFactory already exists in _tokenFactories mapping
Test Result
- Passed
Run Count
- 5M
Invariant
- IdFactory::removeTokenFactory() should always remove a valid existing TokenFactory address from the _tokenFactories mapping
Test Result
- Passed
Run Count
- 5M
Invariant
- IdFactory::removeTokenFactory() should always revert if the address of the TokenFactory to be deleted does not exist in the _tokenFactories mapping
Test Result
- Passed
Run Count
- 5M
Invariant
- [Fund Management Invariants]
Test Result
Run Count
Invariant
- Fund::shareDividend() should always transfer the stablecoin dividents to the specified addresses
Test Result
- Passed
Run Count
- 5M
Invariant
- [Wrapper Management Invariants]
Test Result
Run Count
Invariant
- If the _amount is zero, Wrapper::toERC20() and Wrapper::toERC3643() will always calculate orderValue and taxAmount as 0
Test Result
- Passed
Run Count
- 5M
Invariant
- if the getTokenTotalSupply() is zero, the calculation in Wrapper::toERC20() and Wrapper::toERC3643() will always revert
Test Result
- Passed
Run Count
- 5M
Invariant
- If the NAV multiplied by 1018 is smaller than getTokenTotalSupply() value, orderValue, tokenPrice and taxAmount will be caclulated as zero in Wrapper::toERC20() and Wrapper::toERC3643()** functions
Test Result
- Passed
Run Count
- 5M
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.