Enosys ~ Multi-Decimal Collateral Support
Smart Contract Security Assessment
December 2, 2025

SUMMARY
ABSTRACT
Dedaub was commissioned to perform a security audit of an upgrade of Enosys, a LiquityV2 fork, to support multi-decimal collateral tokens (beyond the standard 18-decimal collateral tokens supported by LiquityV2), and the creation of new Pauser and Unpauser roles. A low issue was found and an advisory noted.
BACKGROUND
Enosys is a LiquityV2 fork to be deployed on the Flare blockchain. Dedaub has audited a previous version of this protocol. This audit focuses on an extension of this protocol that adds support for multi-decimal collateral tokens. New Pauser and Unpauser roles were also created to pause/unpause the protocol.
The stated goal is to support 6 decimal tokens, but the changes are generic and handle any uint8 decimal amount. This support for multi-decimal tokens is implemented by scaling, when needed, for tokens that have less than 18 decimals up to 18 decimals, and dually for tokens that have more than 18 decimals down to 18 decimals. Collateral tokens are normalized to 18 decimals only in specific sections of the code, while most of the code remains the same. This audit focused on checking that normalising was implemented correctly, and used wherever it is needed.
SETTING & CAVEATS
This audit report mainly covers the contracts of the at-the-time private https://github.com/flrfinance/liquity-v2_bold-fork/ repository of the Enosys Protocol at commit 9936614a3701b507dfb58d06ff8ceeb1b85d09d5 in PR #44. This audit focused on the changes introduced in this PR. Review of the fixes at commit b4320d6bcd5d343442c80807bd976d6cf36909ac in PR #51 was also performed.
Audit Start Date: November 25, 2025
Report Submission Date: December 2, 2025
2 auditors worked on the changes to following contracts:
The audit’s main target is security threats, i.e., what the community understanding would likely call "hacking", rather than the regular use of the protocol. Functional correctness (i.e. issues in "regular use") is a secondary consideration. Typically it can only be covered if we are provided with unambiguous (i.e. full-detail) specifications of what is the expected, correct behavior. In terms of functional correctness, we often trusted the code’s calculations and interactions, in the absence of any other specification. Functional correctness relative to low-level calculations (including units, scaling and quantities returned from external protocols) is generally most effectively done through thorough testing rather than human auditing.
VULNERABILITIES & FUNCTIONAL ISSUES
This section details issues affecting the functionality of the contract. Dedaub generally categorizes issues according to the following severities, but may also take other considerations into account such as impact or difficulty in exploitation:
- User or system funds can be lost when third-party systems misbehave
- DoS, under specific conditions
- Part of the functionality becomes unusable due to a programming error
- Breaking important system invariants but without apparent consequences
- Buggy functionality for trusted users where a workaround exists
- Security issues which may manifest when the system evolves
Issue resolution includes “dismissed” or “acknowledged” but no action taken, by the client, or “resolved”, per the auditors.
CRITICAL SEVERITY
[No critical severity issues]
HIGH SEVERITY
[No high severity issues]
MEDIUM SEVERITY
[No medium severity issues]
LOW SEVERITY
The gas compensation paid to liquidators uses a hard coded cap that implicitly assumes the collateral token has 18 decimals. In TroveManagerBase the amount of collateral paid as gas compensation per liquidation is computed as:
TroveManagerBase::_getCollGasCompensation():184-187function _getCollGasCompensation(uint256 _coll) internal view returns (uint256) {
return LiquityMath._min(_coll / COLL_GAS_COMPENSATION_DIVISOR, COLL_GAS_COMPENSATION_CAP);
}
The divisor and cap are set as:
TroveManager::initialize():53-54COLL_GAS_COMPENSATION_DIVISOR = 200;
COLL_GAS_COMPENSATION_CAP = 2 ether;
Here COLL_GAS_COMPENSATION_CAP = 2 ether is effectively 2 * 10**18 units of the collateral token, not native tokens. This matches the intended design only when the collateral token uses 18 decimals. If a branch ever uses a collateral with fewer decimals such as 6 or 8. the raw collateral amounts _coll are in 10**6 or 10**8 units while the cap remains at 2 * 10**18. For any realistic trove size _coll / 200 will always be much smaller than 2e18, so the cap will never be hit and the function effectively will return: collGasComp = _coll / 200.
This silently changes the economics from “min(0.5% of trove collateral, 2 units of collateral)” to “exactly 0.5% of trove collateral with no 2 token cap” on less than 18 decimal collaterals. The system remains functional but the economic parameters for liquidations differ from Liquity v2 and from the intended “2 tokens” cap once you move away from 18 decimal tokens. This contradicts the goal of “support 6 decimals collateral” in a fully parameterized way. Moreover, the design supports tokens using more than 18 decimals, which dually would result in the minimum being hit much more often.
We recommend making the gas compensation cap scale according to the collateral token decimals instead of hard coding 2 ether, e.g., COLL_GAS_COMPENSATION_CAP = 2 * 10 ** collDecimals.
OTHER / ADVISORY ISSUES
This section details issues that are not thought to directly affect the functionality of the project, but we recommend considering them.
_configureMinTotalStakes checks whether the normalisation numerator and denominator are 0 before calculating the minTotalStakes required.
TroveManagerBase::_configureMinTotalStakes():220-228function _configureMinTotalStakes() internal {
uint256 numerator = collateralNormalizationNumerator;
uint256 denominator = collateralNormalizationDenominator;
if (numerator == 0) {
numerator = 1;
}
if (denominator == 0) {
denominator = 1;
}
However, at this stage they will both at least be one. Note that the only flow into this function is called in TroveManager.initialize, after __LiquityBase_init. And __LiquityBase_init calls __LiquityBase_init_unchained, which calls _configureCollateralDecimals. The latter sets the normalization numerator and denominator to at least 1.
For the current code these checks are thus unneeded. However, given the code may be further updated after this audit, reverting on one of these values being 0 would be more appropriate. If this is the case it would indicate that the collateral decimals have not been initialized properly.
DISCLAIMER
The audited contracts have been analyzed using automated techniques and extensive human inspection in accordance with state-of-the-art practices as of the date of this report. The audit makes no statements or warranties on the security of the code. On its own, it cannot be considered a sufficient assessment of the correctness of the contract. While we have conducted an analysis to the best of our ability, it is our recommendation for high-value contracts to commission several independent audits, a public bug bounty program, as well as continuous security auditing and monitoring through Dedaub Security Suite.
ABOUT DEDAUB
Dedaub offers significant security expertise combined with cutting-edge program analysis technology to secure some of the most prominent protocols in DeFi. The founders, as well as many of Dedaub's auditors, have a strong academic research background together with a real-world hacker mentality to secure code. Protocol blockchain developers hire us for our foundational analysis tools and deep expertise in program analysis, reverse engineering, DeFi exploits, cryptography and financial mathematics.