Audit · firm’s term: audit report
QuillAudits reviewed Hivello (Hivello V2) on 29 Jan 2025: 11 findings, 0 critical; all marked fixed.
Report hosted on quillaudits.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 0 | 5 |
| Medium | 0 | 0 | 4 |
| Low | 0 | 0 | 2 |
In the process function of InitializeGlobalConfig, the is_paused flag is set to true by default when initializing the global config. This immediately blocks users from claiming rewards after deployment. Setting is_paused = true during initialization causes: Protocol starts in a non-functional state Users cannot claim rewards immediately after deployment
The SECONDS_FOR_DAY constant is incorrectly set to 60 seconds instead of the correct value of 86,400 seconds (24 hours * 60 minutes * 60 seconds). This error affects the calculation of days_locked in the staking. The incorrect constant leads to significantly wrong calculations of locked days: Current: Using 60 seconds as one day When user commits for 1 day (86,400 seconds), the calculation results in: 86,400 / 60 = 1,440 days (incorrect) Expected: Using 86,400 seconds as one day 86,400 / 86,400 = 1 day (correct)
The staking contract's reward claiming function lacks ownership validation checks, which allows unauthorized users to claim rewards belonging to other users. When calling the claim_rewards instruction, the contract does not verify if the caller is the actual owner of the UserState account. An attacker can steal staking rewards from any user by: Obtaining victim's UserState account address Executing claim_rewards with their own payer_reward_token_ata but using victim's UserState Successfully transferring victim’s rewards to their account
In the assign_tier function, the calculation order for tier requirements leads to precision loss. The current implementation divides token requirements by TOKENREQUIREMENTPRECISION (1000) be fore multiplication with the multiplier, causing potential rounding to zero. When tier.token_requirements values are less than 1000: Division by TOKENREQUIREMENTPRECISION (1000) first results in 0 Subsequent multiplication with any multiplier yields 0 Function reverts with InvalidAmount error as no tier matches the requirements.
The validate_claim_rewards function has incorrectly implemented logic for the pausing mechanism. While the GlobalConfig struct includes an is_paused flag to prevent reward claims, the validation function implements the opposite behavior of what's intended. The reversed logic causes: Users can claim rewards when system is paused (should be blocked) Users are blocked from claiming when system is not paused (should be allowed)
The total_staker field in global_config is incremented when a user is initialized but not decremented when a user account is closed. This leads to an incorrect count of active stakers.
During the staking process ,global_config.total_staked , user_state.stake_time, user_state.to tal_staked_token are updated correctly but when in reward claiming process these fields are not updated, leading to inconsistencies in global_config and user_state accounts.
The reward_vault account in the handler_add_rewards and handler_claim_rewards instructions and is not verified to be derived from the expected seeds. This allows the function to accept any arbitrary account, which can lead to incorrect reward distribution
The initialization of both stake_treasury and reward_treasury accounts uses the init constraint<br>without checking if the accounts already exist. Since these accounts are PDA-derived token accounts,<br>they can be created in advance by an attacker.<br>An attacker can cause a denial of service by:<br>Calculating the PDA addresses for treasury accounts using known seeds<br>Creating token accounts at these addresses before protocol deployment<br>Preventing the protocol initialization as init will fail if accounts exist
In the process function of the ClaimRewards implementation, there is no check to ensure that total_reward is greater than zero before executing the reward transfer logic. If total_reward is zero, the _transfer_from_vault function will still be called, leading to unnecessary computations, potential gas costs.
In the process function of Stake, there is no explicit check that the provided user_state account belongs to the actual staking user (payer)
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (8)
350 projects reviewed · 459 reports · latest Apr 2025
See QuillAudits’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us