Audit Report
Audit Report
Customer: Zilliqa
Date: 05/06/2025
We express our gratitude to the Zilliqa team for the collaborative engagement that enabled
the execution of this Smart Contract Security Assessment.
illiqa's Delegated Staking system enables ZIL holders to participate in network security and
Z
earn rewards by delegating their assets to validator nodes.
Document
Name Smart Contract Code Review and Security Analysis Report for Zilliqa
Website https://www.zilliqa.com/
Platform Zilliqa
Language Solidity
Methodology https://hackenio.cc/sc_methodology
Review Scope
Repository https://github.com/Zilliqa/delegated_staking/
Commit 160eadc
Retest b3333fa
2
Audit Summary
The system users should acknowledge all the risks summed up in the risks section of the
report
13 11 0 2
Total Findings Resolved Accepted Mitigated
Findings by Severity
Severity Count
Critical 0
High 0
Medium 4
Low 3
Vulnerability Severity
F-2025-9408 - Unauthorized Reinitialization Permits Arbitrary State Migration Medium
F-2025-9420 - Missing New Address Staking Data Check for replaceOldAddress Medium
function
F-2025-9429 - Potential Overwrite of Validator Data in _depositAndAddToPool Medium
function in BaseDelegation
F-2025-9437 - Potential Denial of Service due to Iteration Over All Validators Medium
During Deposits Updates
F-2025-9442 - Delegator Can Forcibly Transfer Funds Leading to Reward Inflation Low
and Price Manipulation
F-2025-9456 - Commission Rate Configuration Allows Excessively High Rates Low
F-2025-9502 - Inefficient Handling of Zero-Amount Stakings in Reward Calculation Low
F-2025-9404 - Incorrect Comments in Function for Version Change in Info
BaseDelegation Contract
F-2025-9440 - Lack of Events In Delegations Info
F-2025-9450 - Floating Pragma Info
F-2025-9460 - Redundant State Change in Activation Flag Results in Unnecessary Info
Gas Usage
F-2025-9513 - Missing Non-Zero Address Validations Info
F-2025-9581 - Centralized Control Address Setting Info
3
Documentation quality
Functional requirements are detailed.
Project overview is detailed
All roles in the system are described.
Use cases are described and detailed.
For each contract, all futures are described.
All interactions are described.
Technical description is detailed.
Run instructions are provided.
Technical specification is provided.
The NatSpec documentation is sufficient.
Code quality
The development environment is configured.
Test coverage
Code coverage of the project is 30.09% (branch coverage).
4
Table of Contents
System Overview 6
Privileged Roles 6
Potential Risks 8
Findings 9
Vulnerability Details 9
Observation Details 44
Disclaimers 45
Appendix 1. Definitions 46
Severities 46
Potential Risks 46
Appendix 2. Scope 47
Appendix 3. Additional Valuables 48
System Overview
The Delegated Staking system comprises several core smart contracts, each fulfilling specific
roles to facilitate staking operations:
Privileged roles
NonRebasingLST.sol:
Owner:
Can upgrade contracts ( _authorizeUpgrade ).
Can add validators to staking pools ( _addToPool ).
Can set commission rates ( setCommissionNumerator ).
Can set commission receiver addresses ( setCommissionReceiver ).
LiquidDelegation.sol:
Owner:
Can deposit ZIL directly into validator pools ( depositFromPool ).
Can onboard validators into the pool ( joinPool ).
Can stake accrued rewards from the contract balance ( stakeRewards ).
6
Can collect outstanding commissions ( collectCommission ).
NonLiquidDelegation.sol:
Owner:
Can deposit ZIL directly into validator pools ( depositFromPool ).
Can onboard validators into the pool ( joinPool ).
Can collect outstanding commissions ( collectCommission ).
7
Potential Risks
8
Findings
Vulnerability Details
) {
_migrate(fromVersion);
// Additional logic
modifier initializer() {
== 0;
revert InvalidInitialization();
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
_;
if (isTopLevelCall) {
$._initializing = false;
9
emit Initialized(1);
$.pendingRebalancedDeposit = 0;
$.commissionReceiver = owner();
return;
if (!$.activated)
return;
uint256 peerIdLength;
assembly {
temp.slot := BaseDelegationStorageLocation
if (peerIdLength == 1)
return;
abi.encodeWithSignature("getFutureStake(bytes)",
temp.blsPubKey
);
10
$.validators.push(Validator(
temp.blsPubKey,
futureStake,
owner(),
owner(),
0,
ValidatorStatus.Active
));
$.validatorIndex[temp.blsPubKey] = $.validators.length;
Assets:
BaseDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
NonLiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
LiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
Classification
Impact: 5/5
Likelihood: 2/5
Exploitability: Independent
11
Complexity: Medium
Severity: Medium
Recommendations
Evidences
Unauthorized Reinitialization
Reproduce:
PoC Steps:
PoC Code:
vm.prank(owner);
delegation.setCommissionReceiver(commissionReceiver);
assertEq(delegation.getCommissionReceiver(), commissionReceiver);
12
vm.prank(attacker);
delegation.reinitialize(2);
ncodeVersion(0,3,0)
assertEq(delegation.getCommissionReceiver(), delegation.owner());
Results:
Ran 1 test for test/UnauthorizedMigration.t.sol:UnauthorizedMigrationT
est
Files: UnauthorizedMigration.t.sol
13
F-2025-9420 - Missing New Address Staking Data Check for
replaceOldAddress function - Medium
...
require(
$.stakingIndices[to].length == 0,
StakerAlreadyExists(to)
);
$.newAddress[_msgSender()] = to;
...
$.firstStakingIndex[sender] = $.firstStakingIndex[old];
$.availableTaxedRewards[sender] = $.availableTaxedRewards[old];
$.lastTaxedStakingIndex[sender] = $.lastTaxedStakingIndex[old];
$.taxedSinceLastStaking[sender] = $.taxedSinceLastStaking[old];
...
Assets:
NonLiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
14
LiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
Classification
Impact: 4/5
Likelihood: 2/5
Exploitability: Independent
Complexity: Simple
Severity: Medium
Recommendations
15
F-2025-9429 - Potential Overwrite of Validator Data in
_depositAndAddToPool function in BaseDelegation - Medium
function _depositAndAddToPool(
) internal virtual {
...
$.validators.push(Validator(
blsPubKey,
availableStake,
owner(),
owner(),
0,
ValidatorStatus.Active
));
$.validatorIndex[blsPubKey] = $.validators.length;
...
Assets:
BaseDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Mitigated
Classification
Impact: 5/5
16
Likelihood: 3/5
Exploitability: Dependent
Complexity: Simple
Severity: Medium
Recommendations
17
F-2025-9437 - Potential Denial of Service due to Iteration
Over All Validators During Deposits Updates - Medium
uint256 totalContribution;
contribution[i] = $.validators[i].futureStake;
totalContribution += contribution[i];
uint256 totalDeposited;
if (contribution[i] > 0) {
on;
totalDeposited += value;
$.validators[i].futureStake += value;
abi.encodeWithSignature("depositTopup(bytes)",
$.validators[i].blsPubKey
);
value: value
}(callData);
);
$.nonRewards -= totalDeposited;
...
18
for (uint256 i = 0; i < len; i++)
if ($.validators[i].status == ValidatorStatus.Active) {
osit;
totalContribution += contribution[i];
uint256 j = len;
while (
j > 0 &&
if ($.validators[--j].status == ValidatorStatus.Active) {
$.validators[j].pendingWithdrawals +=
$.validators[j].futureStake;
callData =
abi.encodeWithSignature("unstake(bytes,uint256)",
$.validators[j].blsPubKey,
$.validators[j].futureStake
);
require(success, DepositContractCallFailed(callData, d
ata));
$.validators[j].status = ValidatorStatus.FullyUndeposi
ted;
totalContribution -= contribution[j];
contribution[j] = 0;
amount -= $.validators[j].futureStake;
$.validators[j].futureStake = 0;
else {
requested
ingish it
delegator
$.validators[j].futureStake -= amount;
$.pendingRebalancedDeposit += $.validators[j].futu
reStake;
return;
amount = totalContribution;
19
}
th);
uint256 totalUndeposited;
if (contribution[i] > 0) {
ibution;
totalUndeposited += undeposited[i];
// rounding error that was not unstaked from the deposits but
can be
e respective
e more, until
becomes zero
if (value > 0) {
value++;
delta--;
$.validators[i].futureStake -= value;
$.validators[i].pendingWithdrawals += value;
callData =
abi.encodeWithSignature("unstake(bytes,uint256)",
$.validators[i].blsPubKey,
value
);
);
total = $.nonRewards;
20
uint256 len = $.validators.length;
total += $.validators[i].futureStake;
total += $.pendingRebalancedDeposit;
total -= $.undepositedClaims;
total -= $.depositedClaims;
pendingWithdrawals .
yet
uint256 i = len - j;
if (
$.validators[i].status != ValidatorStatus.ReadyToLeave
) {
abi.encodeWithSignature("withdraw(bytes)",
$.validators[i].blsPubKey
);
Data);
$.validators[i].pendingWithdrawals -= amount;
total += amount;
if (
$.validators[i].pendingWithdrawals == 0 &&
$.validators[i].status == ValidatorStatus.FullyUndeposited
) {
total -= $.validators[i].futureStake;
$.pendingRebalancedDeposit -= $.validators[i].futureStake;
_increaseDeposit($.validators[i].futureStake);
$.validators[i].futureStake = 0;
21
_removeFromPool(i);
total) {
total += $.validators[i].pendingWithdrawals;
Assets:
BaseDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
NonLiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
LiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
Classification
Impact: 5/5
Likelihood: 2/5
Exploitability: Independent
Complexity: Simple
Severity: Medium
Recommendations
22
Remediation: The vulnerability described can be mitigated by avoiding the
iteration over all validators in the _increaseDeposit and
_decreaseDeposit functions. This can be achieved by maintaining
a separate variable that keeps track of the totalDeposited
Resolution: Fixed in edfb906 : Introduced a hard cap of 255 validators via the
MAX_VALIDATORS constant to prevent denial-of-service scenarios
caused by excessive gas consumption in validator iteration
loops. Critical functions have been tested for gas limits up to
this threshold.
23
F-2025-9442 - Delegator Can Forcibly Transfer Funds
Leading to Reward Inflation and Price Manipulation - Low
require(
_msgSender() == DEPOSIT_CONTRACT,
InvalidCaller(_msgSender(), DEPOSIT_CONTRACT)
);
$.nonRewards += msg.value;
returns zero.
24
getRewards() computes rewards as address(this).balance -
if (!_isActivated())
return 0;
e();
nNumerator() / DENOMINATOR;
if (commission == 0)
return;
value: commission
}("");
ssion));
Assets:
LiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Mitigated
25
Classification
Impact: 3/5
Likelihood: 2/5
Exploitability: Independent
Complexity: Complex
Severity: Low
Recommendations
Resolution: The minimum stake requirement in the pool mitigates the risk,
therefore, added funds can be considered as a donation that is
harmless and actually benefits all stakers. An additional
comment was provided by the client:
26
F-2025-9456 - Commission Rate Configuration Allows
Excessively High Rates - Low
Description: The contract allows the owner to set the commission rate up to
100% of the generated rewards. This could result in delegators
losing all their staking rewards unexpectedly.
irtual onlyOwner {
_commissionNumerator));
$.commissionNumerator = _commissionNumerator;
) {
ENOMINATOR;
if (commission == 0)
return untaxedRewards;
value: commission
}("");
n));
27
Assets:
BaseDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
Classification
Impact: 4/5
Likelihood: 2/5
Exploitability: Dependent
Complexity: Simple
Severity: Low
Recommendations
Remediation:
Implement a reasonable upper bound (e.g., 10–20%) on
the commission rate to ensure fairness and predictability.
Introduce a time delay between changes to the
commission rate becoming effective, allowing delegators
adequate time to respond or withdraw their assets if they
disagree with proposed commission changes.
28
F-2025-9502 - Inefficient Handling of Zero-Amount Stakings
in Reward Calculation - Low
while (
posInStakingIndices == $.stakingIndices[_msgSender()].length - 1 ?
ices + 1]
) {
if (total > 0) {
resultInTotal += $.stakings[nextStakingIndex].rewards * am
ount / total;
roundingError +=
t / total -
nt / total);
total = $.stakings[nextStakingIndex].total;
nextStakingIndex++;
) {
...
getAdditionalSteps function.
Assets:
NonLiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
29
Classification
Impact: 2/5
Likelihood: 3/5
Exploitability: Independent
Complexity: Simple
Severity: Low
Recommendations
Remediation: The code should be updated to skip the stakings with zero
amount. This can be achieved by adding a condition to check if
the staking amount is zero before processing it in the loop. If
the amount is zero, the loop should continue to the next
staking without processing the current one. This will prevent
unnecessary gas consumption and redundant transaction
steps.
Evidences
Proof of Concept
Reproduce:
function test_RedundantAdditionalSteps() public {
tMode.Fundraising);
vm.startPrank(initialUser);
delegation.unstake(100 ether);
vm.roll(block.number + delegation.unbondingPeriod());
30
delegation.claim();
vm.stopPrank();
akings
vm.deal(secondUser, depositAmount);
vm.startPrank(secondUser);
delegation.stake{value: depositAmount}();
vm.startPrank(initialUser);
assertEq(steps, 26);
Results:
31
F-2025-9404 - Incorrect Comments in Function for Version
Change in BaseDelegation Contract - Info
Description: DRAFT
...
return;
...
$.pendingRebalancedDeposit = 0;
$.commissionReceiver = owner();
return;
...
32
The client confirmed that the comments in the code are not
correct.
Assets:
BaseDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
Classification
Impact: 2/5
Likelihood: 2/5
Exploitability: Dependent
Complexity: Simple
Severity: Info
Recommendations
33
F-2025-9440 - Lack of Events In Delegations - Info
BaseDelegation:
setCommissionReceiver
setCommissionNumerator
completeLeaving
NonLiquidDelegation:
collectCommission
replaceOldAddress
setNewAddress
Assets:
BaseDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
NonLiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
LiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
Classification
Impact: 1/5
Likelihood: 1/5
Exploitability: Independent
Complexity: Simple
Severity: Info
Recommendations
34
Remediation: Consider implementing additional event logging for critical
changes to enhance monitoring and ensure operational
transparency in the contracts.
35
F-2025-9450 - Floating Pragma - Info
Assets:
BaseDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
NonLiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
LiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
WithdrawalQueue.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
NonRebasingLST.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
Classification
Impact: 3/5
Likelihood: 1/5
Exploitability: Dependent
Complexity: Simple
36
Severity: Info
Recommendations
37
F-2025-9460 - Redundant State Change in Activation Flag
Results in Unnecessary Gas Usage - Info
$.activated = true;
function _depositAndAddToPool(
) internal virtual {
// additional logic...
functions.
Assets:
BaseDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
38
Classification
Impact: 1/5
Likelihood: 5/5
Exploitability: Independent
Complexity: Simple
Severity: Info
Recommendations
if (!$.activated) {
$.activated = true;
39
F-2025-9513 - Missing Non-Zero Address Validations - Info
BaseDelegation contract:
setCommissionReceiver : _commissionReceiver
LiquidDelegation contract:
joinPool : controlAddress
NonLiquidDelegation contract:
joinPool : controlAddress
Assets:
BaseDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
NonLiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
LiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
Classification
Impact: 1/5
Likelihood: 1/5
Exploitability: Dependent
Complexity: Simple
Severity: Info
Recommendations
40
Remediation: To fix this vulnerability non-zero address validation checks
should be added to all the functions mentioned above. This
can be done by adding a simple condition to check if the
address is non-zero before proceeding with the rest of the
function.
41
F-2025-9581 - Centralized Control Address Setting - Info
function.
Assets:
NonLiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
LiquidDelegation.sol
[https://github.com/Zilliqa/delegated_staking/tree/main/src]
Status: Fixed
Classification
Impact: 2/5
Likelihood: 2/5
Exploitability: Dependent
Complexity: Simple
Severity: Info
Recommendations
42
Resolution: Fixed in 766bbc8 : Implemented registerControlAddress and
unregisterControlAddress functions to securely manage control
address registration using on-chain verification via the deposit
contract, eliminating the need for operators to trust the pool
owner to provide the correct address during joinPool() calls.
43
Observation Details
44
Disclaimers
Hacken Disclaimer
The smart contracts given for audit have been analyzed based on best industry practices at
the time of the writing of this report, with cybersecurity vulnerabilities and issues in smart
contract source code, the details of which are disclosed in this report (Source Code); the
Source Code compilation, deployment, and functionality (performing the intended functions).
The report contains no statements or warranties on the identification of all vulnerabilities and
security of the code. The report covers the code submitted and reviewed, so it may not be
relevant after any modifications. Do not consider this report as a final and sufficient
assessment regarding the utility and safety of the code, bug-free status, or any other contract
statements.
While we have done our best in conducting the analysis and producing this report, it is
important to note that you should not rely on this report only — we recommend proceeding
with several independent audits and a public bug bounty program to ensure the security of
smart contracts.
English is the original language of the report. The Consultant is not responsible for the
correctness of the translated versions.
Technical Disclaimer
Smart contracts are deployed and executed on a blockchain platform. The platform, its
programming language, and other software related to the smart contract can have
vulnerabilities that can lead to hacks. Thus, the Consultant cannot guarantee the explicit
security of the audited smart contracts.
45
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:
hknio/severity-formula
Severity Description
Critical vulnerabilities are usually straightforward to exploit and can lead to the
Critical
loss of user funds or contract state manipulation.
High vulnerabilities are usually harder to exploit, requiring specific conditions, or
High have a more limited scope, but can still lead to the loss of user funds or contract
state manipulation.
Medium vulnerabilities are usually limited to state manipulations and, in most
Medium cases, cannot lead to asset loss. Contradictions and requirements violations. Major
deviations from best practices are also in this category.
Major deviations from best practices or major Gas inefficiency. These issues will
Low
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.
46
Appendix 2. Scope
The scope of the project includes the following smart contracts from the provided repository:
Scope Details
Repository https://github.com/Zilliqa/delegated_staking/
Commit 160eadc6470c856dd555433e3b367dc40b32abd0
Retest b3333fa8ffeba5258ff68fce69d6dc89b695289d
Whitepaper N/A
Requirements NatSpec
Technical Requirements NatSpec
Asset Type
Smart
BaseDelegation.sol [https://github.com/Zilliqa/delegated_staking/tree/main/src]
Contract
Smart
IDelegation.sol [https://github.com/Zilliqa/delegated_staking/tree/main/src]
Contract
LiquidDelegation.sol Smart
[https://github.com/Zilliqa/delegated_staking/tree/main/src] Contract
NonLiquidDelegation.sol Smart
[https://github.com/Zilliqa/delegated_staking/tree/main/src] Contract
NonRebasingLST.sol Smart
[https://github.com/Zilliqa/delegated_staking/tree/main/src] Contract
WithdrawalQueue.sol Smart
[https://github.com/Zilliqa/delegated_staking/tree/main/src] Contract
47
Appendix 3. Additional Valuables
Fuzz-testing allows the input of many random data points into the system, helping to identify
issues that regular testing might miss. A specific Foundry fuzzing suite was prepared for this
task, and throughout the assessment, 5 invariants were tested over 100,000 runs. This
thorough testing ensured that the system works correctly even with unexpected or unusual
inputs.
Test Run
Invariant
Result Count
Index Consistency: The queue’s indices remain consistent (i.e. first <= last ). Passed 100K+
Queue Length Accuracy: The active queue length ( last - first ) exactly
Passed 100K+
equals the total enqueues minus dequeues.
Item Readiness Consistency: For every active item (from first to last - 1 ),
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:
48
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.
49