Halborn Logo

Tokenomics - Chiliz


Prepared by:

Halborn Logo

HALBORN

Last Updated 05/22/2024

Date of Engagement by: May 1st, 2024 - May 6th, 2024

Summary

100% of all REPORTED Findings have been addressed

All findings

10

Critical

0

High

0

Medium

0

Low

0

Informational

10


1. Introduction

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.

2. Assessment Summary

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.

3. Test Approach and Methodology

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.

3.1 Out-of-scope

    • 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

4. RISK METHODOLOGY

Every vulnerability and issue observed by Halborn is ranked based on two sets of Metrics and a Severity Coefficient. This system is inspired by the industry standard Common Vulnerability Scoring System.
The two Metric sets are: Exploitability and Impact. Exploitability captures the ease and technical means by which vulnerabilities can be exploited and Impact describes the consequences of a successful exploit.
The Severity Coefficients is designed to further refine the accuracy of the ranking with two factors: Reversibility and Scope. These capture the impact of the vulnerability on the environment as well as the number of users and smart contracts affected.
The final score is a value between 0-10 rounded up to 1 decimal place and 10 corresponding to the highest security risk. This provides an objective and accurate rating of the severity of security vulnerabilities in smart contracts.
The system is designed to assist in identifying and prioritizing vulnerabilities based on their level of risk to address the most critical issues in a timely manner.

4.1 EXPLOITABILITY

Attack Origin (AO):
Captures whether the attack requires compromising a specific account.
Attack Cost (AC):
Captures the cost of exploiting the vulnerability incurred by the attacker relative to sending a single transaction on the relevant blockchain. Includes but is not limited to financial and computational cost.
Attack Complexity (AX):
Describes the conditions beyond the attacker’s control that must exist in order to exploit the vulnerability. Includes but is not limited to macro situation, available third-party liquidity and regulatory challenges.
Metrics:
EXPLOITABILIY METRIC (mem_e)METRIC VALUENUMERICAL 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
Exploitability EE is calculated using the following formula:

E=meE = \prod m_e

4.2 IMPACT

Confidentiality (C):
Measures the impact to the confidentiality of the information resources managed by the contract due to a successfully exploited vulnerability. Confidentiality refers to limiting access to authorized users only.
Integrity (I):
Measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of data stored and/or processed on-chain. Integrity impact directly affecting Deposit or Yield records is excluded.
Availability (A):
Measures the impact to the availability of the impacted component resulting from a successfully exploited vulnerability. This metric refers to smart contract features and functionality, not state. Availability impact directly affecting Deposit or Yield is excluded.
Deposit (D):
Measures the impact to the deposits made to the contract by either users or owners.
Yield (Y):
Measures the impact to the yield generated by the contract for either users or owners.
Metrics:
IMPACT METRIC (mIm_I)METRIC VALUENUMERICAL 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
Impact II is calculated using the following formula:

I=max(mI)+mImax(mI)4I = max(m_I) + \frac{\sum{m_I} - max(m_I)}{4}

4.3 SEVERITY COEFFICIENT

Reversibility (R):
Describes the share of the exploited vulnerability effects that can be reversed. For upgradeable contracts, assume the contract private key is available.
Scope (S):
Captures whether a vulnerability in one vulnerable contract impacts resources in other contracts.
Metrics:
SEVERITY COEFFICIENT (CC)COEFFICIENT VALUENUMERICAL VALUE
Reversibility (rr)None (R:N)
Partial (R:P)
Full (R:F)
1
0.5
0.25
Scope (ss)Changed (S:C)
Unchanged (S:U)
1.25
1
Severity Coefficient CC is obtained by the following product:

C=rsC = rs

The Vulnerability Severity Score SS is obtained by:

S=min(10,EIC10)S = min(10, EIC * 10)

The score is rounded up to 1 decimal places.
SeverityScore Value Range
Critical9 - 10
High7 - 8.9
Medium4.5 - 6.9
Low2 - 4.4
Informational0 - 1.9

5. SCOPE

Files and Repository
(a) Repository: v2-genesis-config
(b) Assessed Commit ID: 97bd063
(c) Items in scope:
  • contracts/Injector.sol
  • contracts/Tokenomics.sol
  • contracts/interfaces/IInjector.sol
↓ Expand ↓
Out-of-Scope: 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/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
Out-of-Scope: New features/implementations after the remediation commit IDs.

6. Assessment Summary & Findings Overview

Critical

0

High

0

Medium

0

Low

0

Informational

10

Security analysisRisk levelRemediation Date
Unlocked pragma compilersInformationalAcknowledged
Unused importsInformationalAcknowledged
Use of full file importsInformationalAcknowledged
Use of custom errors instead of revert stringsInformationalAcknowledged
Inconsistent file namingInformationalAcknowledged
Unused local variableInformationalAcknowledged
Public functions not called within the contract can be made externalInformationalAcknowledged
Large literal value can improve its readabilityInformationalAcknowledged
Check-effects-interaction pattern not followedInformationalAcknowledged
Event emission practicesInformationalAcknowledged

7. Findings & Tech Details

7.1 Unlocked pragma compilers

// Informational

Description

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.

Score
Recommendation

Lock the pragma version to the same version used during development and testing.

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

7.2 Unused imports

// Informational

Description

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";
Score
Recommendation

Remove the aforementioned unused imports.

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

References
IInjector.sol#L4-L10
Injector.sol#L9
Tokenomics.sol#L6

7.3 Use of full file imports

// Informational

Description

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.

Score
Recommendation

It's considered best practice to follow named imports instead of importing full files.

import {MyContract} from "./MyContract.sol";

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

7.4 Use of custom errors instead of revert strings

// Informational

Description

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.

Score
Recommendation

Consider replacing all revert strings with custom errors. For more reference, see here.

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

References
Tokenomics.sol#L65
Tokenomics.sol#L73
Tokenomics.sol#L85
Injector.sol#L112
Injector.sol#L117
Injector.sol#L125
Injector.sol#L130
Injector.sol#L135
Injector.sol#L140

7.5 Inconsistent file naming

// Informational

Description

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.

Score
Recommendation

Rename the InjectorContextHolder contract to Injector, or rename the Injector.sol file to InjectorContextHolder.sol.

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

7.6 Unused local variable

// Informational

Description

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);
}
Score
Recommendation

Verify the return data from the external call if it needs validating. Alternatively, remove the unused data variable.

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

References
Tokenomics.sol#L72

7.7 Public functions not called within the contract can be made external

// Informational

Description

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.

Score
Recommendation

Modify the aforementioned functions with the external visibility modifier.

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

References
Injector.sol#L71
Injector.sol#L144

7.8 Large literal value can improve its readability

// Informational

Description

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.

Score
Recommendation

Replace the large literal value with power of ten or native units :

8_888_888_888e18

or

8_888_888_888 ether

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

References
Tokenomics.sol#L9

7.9 Check-effects-interaction pattern not followed

// Informational

Description

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.

Score
Recommendation

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.

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

References
Tokenomics.sol#L64-L82

7.10 Event emission practices

// Informational

Description

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.

Score
Recommendation

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.

Remediation Plan

ACKNOWLEDGED: The Chiliz team made a business decision to acknowledge this finding and not alter the contracts.

References
Tokenomics.sol#L20-L21
Injector.sol#L144-L146

8. Automated Testing

Static Analysis Report

Description

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.

Output

Chiliz - slither1Chiliz - slither2
Chiliz - slither3

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.

Unit tests and fuzzing

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.

Chiliz - Fuzz testing

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.

© Halborn 2024. All rights reserved.