Prepared by:
HALBORN
Last Updated 05/22/2024
Date of Engagement by: May 1st, 2024 - May 6th, 2024
100% of all REPORTED Findings have been addressed
All findings
10
Critical
0
High
0
Medium
0
Low
0
Informational
10
Chiliz
engaged Halborn
to conduct a security assessment on their smart contracts beginning on 05-01-2024 and ending on 05-06-2024. The security assessment was scoped to the smart contracts provided in the https://github.com/chiliz-chain/v2-genesis-config/tree/devel GitHub repository. Commit hashes and further details can be found in the Scope section of this report.
Halborn was provided 4 days for the engagement and assigned 1 full-time security engineer to review the security of the smart contracts in scope. The engineer is a blockchain and smart contract security expert with advanced penetration testing and smart contract hacking skills, and deep knowledge of multiple blockchain protocols.
The purpose of the assessment is to:
Identify potential security issues within the smart contracts.
Ensure that smart contract functionality operates as intended.
In summary, Halborn identified some minor improvements to reduce the likelihood and impact of further risks, which were acknowledged by the Chiliz team
.
Halborn performed a combination of manual and automated security testing to balance efficiency, timeliness, practicality, and accuracy in regard to the scope of this assessment. While manual testing is recommended to uncover flaws in logic, process, and implementation; automated testing techniques help enhance coverage of the contracts' solidity code and can quickly identify items that do not follow security best practices. The following phases and associated tools were used throughout the term of the assessment:
Research into architecture and purpose.
Smart contract manual code review and walk-through.
Manual assessment of use and safety for the critical Solidity variables and functions in scope to identify any arithmetic-related vulnerability classes.
Manual testing with custom scripts (Foundry
).
Static Analysis of security for scoped contract, and imported functions.
External libraries and financial-related attacks.
New features/implementations after/within the remediation commit IDs.
The following files, while they affect the in-scope code, were not specifically reviewed as part of the scope and are assumed to not contain any issues:
contracts/ChainConfig.sol
contracts/DeployerProxy.sol
contracts/Governance.sol
contracts/RuntimeUpgrade.sol
contracts/SlashingIndicator.sol
contracts/Staking.sol
contracts/StakingPool.sol
contracts/SystemReward.sol
contracts/Tokenomics.sol
contracts/interfaces/IChainConfig.sol
contracts/interfaces/IDeployerProxy.sol
contracts/interfaces/IGovernance.sol
contracts/interfaces/IRuntimeUpgrade.sol
contracts/interfaces/IRuntimeUpgradeEvmHook.sol
contracts/interfaces/ISlashingIndicator.sol
contracts/interfaces/IStaking.sol
contracts/interfaces/IStakingPool.sol
contracts/interfaces/ISystemReward.sol
contracts/interfaces/IValidatorSet.sol
EXPLOITABILIY METRIC () | METRIC VALUE | NUMERICAL VALUE |
---|---|---|
Attack Origin (AO) | Arbitrary (AO:A) Specific (AO:S) | 1 0.2 |
Attack Cost (AC) | Low (AC:L) Medium (AC:M) High (AC:H) | 1 0.67 0.33 |
Attack Complexity (AX) | Low (AX:L) Medium (AX:M) High (AX:H) | 1 0.67 0.33 |
IMPACT METRIC () | METRIC VALUE | NUMERICAL VALUE |
---|---|---|
Confidentiality (C) | None (I:N) Low (I:L) Medium (I:M) High (I:H) Critical (I:C) | 0 0.25 0.5 0.75 1 |
Integrity (I) | None (I:N) Low (I:L) Medium (I:M) High (I:H) Critical (I:C) | 0 0.25 0.5 0.75 1 |
Availability (A) | None (A:N) Low (A:L) Medium (A:M) High (A:H) Critical (A:C) | 0 0.25 0.5 0.75 1 |
Deposit (D) | None (D:N) Low (D:L) Medium (D:M) High (D:H) Critical (D:C) | 0 0.25 0.5 0.75 1 |
Yield (Y) | None (Y:N) Low (Y:L) Medium (Y:M) High (Y:H) Critical (Y:C) | 0 0.25 0.5 0.75 1 |
SEVERITY COEFFICIENT () | COEFFICIENT VALUE | NUMERICAL VALUE |
---|---|---|
Reversibility () | None (R:N) Partial (R:P) Full (R:F) | 1 0.5 0.25 |
Scope () | Changed (S:C) Unchanged (S:U) | 1.25 1 |
Severity | Score Value Range |
---|---|
Critical | 9 - 10 |
High | 7 - 8.9 |
Medium | 4.5 - 6.9 |
Low | 2 - 4.4 |
Informational | 0 - 1.9 |
Critical
0
High
0
Medium
0
Low
0
Informational
10
Security analysis | Risk level | Remediation Date |
---|---|---|
Unlocked pragma compilers | Informational | Acknowledged |
Unused imports | Informational | Acknowledged |
Use of full file imports | Informational | Acknowledged |
Use of custom errors instead of revert strings | Informational | Acknowledged |
Inconsistent file naming | Informational | Acknowledged |
Unused local variable | Informational | Acknowledged |
Public functions not called within the contract can be made external | Informational | Acknowledged |
Large literal value can improve its readability | Informational | Acknowledged |
Check-effects-interaction pattern not followed | Informational | Acknowledged |
Event emission practices | Informational | Acknowledged |
// Informational
The files in scope currently use floating pragma version ^0.8.0
, which means that the code can be compiled by any compiler version that is greater than or equal to 0.8.0
, and less than 0.9.0
.
It is recommended that contracts should be deployed with the same compiler version and flags used during development and testing. Locking the pragma helps to ensure that contracts do not accidentally get deployed using another pragma. For example, an outdated pragma version might introduce bugs that affect the contract system negatively.
Lock the pragma version to the same version used during development and testing.
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
// Informational
Throughout the code in scope, there are several instances where the files are imported but not used in a file:
In IInjector.sol
:
import "./ISlashingIndicator.sol";
import "./ISystemReward.sol";
import "./IGovernance.sol";
import "./IStaking.sol";
import "./IDeployerProxy.sol";
import "./IStakingPool.sol";
import "./IChainConfig.sol";
In Injector.sol
:
import "./interfaces/IValidatorSet.sol";
In Tokenomics.sol
:
import "@openzeppelin/contracts/utils/Address.sol";
Remove the aforementioned unused imports.
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
// Informational
Throughout the codebase, there are multiple instances where full file imports are used; although this does not impact functionality, it is generally not considered a best practice.
It's considered best practice to follow named imports instead of importing full files.
import {MyContract} from "./MyContract.sol";
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
// Informational
In Solidity development, replacing hard-coded revert message strings with the Error()
syntax is an optimization strategy that can significantly reduce gas costs. Hard-coded strings, stored on the blockchain, increase the size and cost of deploying and executing contracts.
The Error()
syntax allows for the definition of reusable, parameterized custom errors, leading to a more efficient use of storage and reduced gas consumption. This approach not only optimizes gas usage during deployment and interaction with the contract but also enhances code maintainability and readability by providing clearer, context-specific error information.
Consider replacing all revert strings with custom errors. For more reference, see here.
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
// Informational
In Solidity development, it is a common convention that contracts, interfaces and library names should also match their filenames. However, the Injector.sol
file does not follow this convention and contains a contract called InjectorContextHolder
instead.
Rename the InjectorContextHolder
contract to Injector
, or rename the Injector.sol
file to InjectorContextHolder.sol
.
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
// Informational
The _deposit()
function in the Tokenomics.sol
file makes a low-level call
to the _systemRewardContract
address transferring systemAmount
to it. However, the call
function returns a data
variable that is not used.
function _deposit(address validatorAddress, uint256 newTotalSupply, uint256 inflationPct) internal {
require(msg.value > 0, "dizt"); // deposit is zero
// Distribute
uint256 stakingAmount = msg.value * _state.shareStaking / 10000;
uint256 systemAmount = msg.value * _state.shareSystem / 10000;
_stakingContract.deposit{value: stakingAmount}(validatorAddress);
(bool sent, bytes memory data) = address(_systemRewardContract).call{value: systemAmount}("");
require(sent, "sr"); // transfer to systemRewardsContract failed
// Update state
_state.inflationPct = inflationPct;
_state.introducedSupply = msg.value;
_state.totalIntroducedSupply += msg.value;
_state.totalSupply = newTotalSupply;
emit Deposit(msg.value, newTotalSupply, inflationPct, validatorAddress, stakingAmount, systemAmount);
}
Verify the return data from the external call if it needs validating. Alternatively, remove the unused data
variable.
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
// Informational
The Injector.sol
contract currently defines the initManually()
and the setTokenomics()
functions with public
visibility, even though they are not called from within the smart contract or any contract that inherits from Injector.sol
, resulting in higher gas costs than necessary.
Modify the aforementioned functions with the external
visibility modifier.
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
// Informational
The INITIAL_TOTAL_SUPPLY
variable in the Tokenomics.sol
file is a large literal value set to 8888888888000000000000000000
. Managing such large numbers directly can be cumbersome and error-prone. To enhance code readability and reduce potential mistakes, Solidity best practices recommend employing native units or employing powers of ten.
Replace the large literal value with power of ten or native units :
8_888_888_888e18
or
8_888_888_888 ether
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
// Informational
The _deposit()
function within the Tokenomics
smart contract is designed to facilitate the transfer of native assets to the contract and subsequently distribute these assets between the Staking
contract and the SystemReward
contract. However, this function does not adhere to the checks-effects-interactions pattern, as it updates the contract's state after making external calls.
While the reentrancy vulnerability may not directly affect the contract or funds in this specific instance, it is crucial to follow the check-effects-interaction pattern. This practice ensures that the contract state is updated prior to executing external calls, thus safeguarding against potential vulnerabilities.
Refactor the _deposit()
function to follow the check-effects-interaction pattern and update the contract's state prior to making the external calls.
Additionally, ensure that each of the ds.account
to which transfers are made in the _claimSystemFee
in the SystemReward
contract in not allowed to re-enter the system for any of that contract's functions and any other functions in the system beyond the receive
function in the SystemReward
contract.
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
// Informational
In Solidity development, emitting events is a recommended practice when state-changing functions are invoked. Currently, the Injector.sol
file lacks an event emission in the setTokenomics()
function. This absence may hamper effective state tracking in off-chain monitoring systems.
Additionally, the events declared in the Tokenomics
contract are missing the indexed
keyword, which make the data more quickly accessible to off-chain tools that parse events, and adds them to a special data structure known as "topics" instead of the data part of the log.
However, indexing more fields increases the gas cost for each event emitted. Therefore, extensive indexing is recommended when the advantages of easier data retrieval outweigh the higher gas expenses, particularly in cases where gas efficiency is less of a concern.
Introduce an event declaration and ensure its emission within the setTokenomics()
function. This change will enhance transparency and facilitate accurate tracking of state changes.
If the value to emit in an event is fix-sized, it is recommended to add the indexed
keyword when declaring events. For more reference, see the Solidity documentation.
ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.
Halborn used automated testing techniques to enhance the coverage of certain areas of the smart contracts in scope. Among the tools used was Slither, a Solidity static analysis framework. After Halborn verified the smart contracts in the repository and was able to compile them correctly into their ABIs and binary format, Slither was run against the contracts. This tool can statically verify mathematical relationships between Solidity variables to detect invalid or inconsistent usage of the contracts' APIs across the entire code-base.
The security team assessed all findings identified by the Slither software, however, findings with related to external dependencies are not included in the below results for the sake of report readability.
The findings obtained as a result of the Slither scan were reviewed, and not included in the report because they were determined as false positives.
The original repository used the Truffle environment to develop and test the smart contracts. All tests were executed successfully. Additionally, the project in scope was cloned to a Foundry
environment, to allow for additional testing and fuzz testing that covered ~10,000,000 runs per test. These additional tests were run successfully.
Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.
// Download the full report
* Use Google Chrome for best results
** Check "Background Graphics" in the print settings if needed