Q2 2026 Security & Compliance Report67 incidents, $764M in losses, 88% from operational failures.
Get the report →

Audit name:

[SCA] Zilliqa | Zilliqa Ownership Proof | Aug2026

Date:

Sep 9, 2026

Table of Content

Introduction
Audit Summary
System Overview
Potential Risks
Findings
Appendix 1. Definitions
Appendix 2. Scope
Appendix 3. Additional Valuables
Disclaimer

Want a comprehensive audit report like this?

Introduction

We express our gratitude to the Zilliqa team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.

Zilliqa 1.0 was a legacy L1 that combined PoW consensus with pBFT finality and EC-Schnorr transaction signing over secp256k1. It was shut down over a year ago in favor of Zilliqa 2.0, an EVM-compatible, PoS-based chain that continues to support legacy Zilliqa 1.0 accounts and transaction formats. Following a July 2026 incident which exposed the leaf keys of a section of users, affected holders now need a way to migrate their legacy accounts to new EVM-compatible accounts without exposing the underlying key material — the leaked leaf keys must not be sufficient to migrate funds.

Migration requires proving ownership of a legacy account without exposing the underlying key material — the leaked leaf keys must not be sufficient to migrate funds. The audited system addresses this with a Groth16 zero-knowledge circuit: a holder proves in zero knowledge that they control a BIP-44 HD wallet address, by supplying the parent node one level up the derivation tree (m/44'/313'/n'/0' or m/44'/313'/n'/0) as a private witness and re-deriving the final child key in-circuit. Because the incident only exposed leaf keys, an attacker holding a stolen leaf key cannot reconstruct the parent and therefore cannot produce a valid migration proof — only the rightful seed holder can. The proof binds the source address, migration destination, and a replay domain as public signals, and is submitted to an on-chain escrow contract that verifies the proof and releases the corresponding balance to the new address.

Review Scope

Repositoryhttps://github.com/Zilliqa/zkp_recovery_app
Commitd52b9210fa3c53439bd217ca04dbad6b0d6fa154

Audit Summary

20Total Findings
18Resolved
1Accepted
1Mitigated

The system users should acknowledge all the risks summed up in the risks section of the report

Documentation quality

The circuit files themselves carry dense, precise inline comments explaining the BIP-32 CKD logic, security fixes and constraints.

Code quality

The circom circuit (circuit.circom ) is tight, well-commented, and shows real security hardening. Flutter app (~1,677 lines across lib/) is a small, single-purpose onboarding-stepper UI wired to a ProofService/DownloadService pair; reasonably organized by widget/service/model.

Test coverage

securecoretest.js is a real end-to-end self-test (proves both Ledger-hardened and BIP-44 paths against a known test vector, and asserts tampered public signals fail verification) — good for what it covers, but it's a single manual script, not a suite (no assertions library, no CI-style runner, no edge-case/negative fuzzing beyond the two tamper checks). Flutter-side tests (circom_test.dart ) are thin and self-admittedly limited to pre-dependency validation (can't exercise the native/zkey path in a test env), plus a near-empty default widget smoke test.

System Overview

Circuits (circuit.circom, bip32lib.circom)

The circuit proves ownership of a legacy Zilliqa address by re-deriving it from a private parent key one level up the BIP-32 tree, rather than from the leaf key itself — the core design choice that keeps the tool safe even though leaf keys were exposed in the incident. It supports both wallet conventions in use today: Ledger's fully-hardened path (m/44'/313'/n'/0'/0') and the standard hot-wallet path (m/44'/313'/n'/0/i), selected by a public isHardened flag. The circuit derives the child private key from the parent via one HMAC-SHA512-based BIP-32 step (with the standard IL < n / childPriv ≠ 0 validity checks), turns it into a public key, and hashes that down to a 160-bit address. Three values are bound into the proof as public signals: the address being claimed (expectedAddr, must match what the circuit derives), the migration destination (newAddr, range-checked as a valid 160-bit address), and a replay-protection domain value.

Contract (src/contracts/escrow/escrow_v1.sol)

An upgradeable escrow contract with two entry points: lodge(), which anyone can call to deposit funds credited against their own address, and claim(), which accepts a proof and its public signals, checks the domain matches the current chain, verifies the proof against the generated Groth16 verifier, and — if valid — pays out the depositor's balance to the migration destination named in the proof. Balances are cleared before the payout is sent, which is the correct ordering to avoid reentrancy. Because Zilliqa's legacy (Scilla-side) transactions can't directly call EVM contracts, node software special-cases transfers to this contract's fixed address and routes them into lodge().

Privileged roles

lodge() and claim() are fully permissionless — there is no admin who can block, redirect, or approve a claim; a valid proof is sufficient on its own. The one privileged action is upgrading the contract's implementation, which is gated to calls originating from the zero address. In practice that means only the protocol's own infrastructure — not a deployer, multisig, or any ordinary key — can trigger an upgrade, though exactly how and when that happens is outside this contract and can't be verified from the code alone.

Potential Risks

Native-token-only scope; no coverage for staked ZIL, other tokens, or NFTs. escrow_v1.sol's balances mapping tracks only native ZIL lodged via lodge(). Any ZIL staked at the time of the incident, ZRC-2 fungible tokens, or ZRC-6/721 NFTs held by an affected legacy address have no migration path through this contract — a holder proving ownership recovers their liquid native balance only, while staked or tokenized assets remain stranded with no analog remediation flow defined anywhere in scope.

No migration path for multisig or custom-derivation wallets. The entire scheme assumes a single-signer BIP-44 HD wallet. Legacy multisig holders (different derivation scheme entirely) have no way to prove ownership under this circuit — a scope gap, not a bug, but a real class of funds this tool cannot rescue.

Unaudited, partly unmaintained wallet-side dependencies. Flutter app's bech32 package is unmaintained since Feb 2023 with no audit, and bip32_keys/bip39_mnemonic are actively maintained but single-maintainer and unaudited. A single unaudited/unmaintained dependency in the mnemonic→key path is a supply-chain risk to the tool.

No mnemonic/parent-key export path for some legacy wallets (e.g. ZHIP/Zesame).  Zesame only exports a keystore or raw leaf private key — never the BIP-39 mnemonic or any BIP-32 extended key. Since the circuit requires the parent node (one level above the leaf) as a witness, a holder whose only wallet was ZHIP-class has no way to produce that witness at all: they cannot migrate through this tool regardless of any fix, not because their leaf key is compromised, but because the wallet software never gave them the derivation material the proof needs.

Trusted-setup ceremony execution risk. A properly executed trusted ceremony includes 5–10 independent, ideally air-gapped contributors, each contribution hash published, a public verifiable beacon, and a re-runnable snarkjs zkey verify transcript. A ceremony that runs but falls short of these (too few contributors, no air-gapping, no public transcript) is a soft failure the current finding wouldn't catch.

Device/environment assumptions for local proving. The tool requires proving on a non-shared, single-user machine so no other local user can read the process via /proc while key material is resident. This is an environmental assumption the tool cannot enforce and isn't captured by the existing findings.

RAM/swap exposure during proving. Groth16 witness generation for this circuit size is memory-heavy; on constrained devices this can force paging to swap, writing key-derived material to disk unencrypted outside the app's control — a distinct risk from any in-memory zeroization gaps.

Findings

Code
Title
Status
Severity
F-2026-1902Hardcoded hardened-only derivation path locks out all standard BIP-44 wallets
fixed

High
F-2026-1900.trim() on the BIP-39 passphrase silently derives a different master key
fixed

High
F-2026-1900Missing EIP-55 check and destination confirmation send the balance to a mistyped address
fixed

High
F-2026-1902Unconstrained newAddr and domain desynchronize the proof from the on-chain address
fixed

Medium
F-2026-1902 Missing IL < n and childPriv != 0 checks break BIP-32 conformance in CKDHardened
fixed

Medium
F-2026-1900Unnormalized U+3000 separator rejects Japanese phrases typed with a regular space
fixed

Medium
F-2026-1900ASCII-only mnemonic validator rejects six of the ten wordlists
fixed

Medium
F-2026-1900Mnemonic checksum validation is optional and English-only, silently skipping on non-English mnemonics or a missing dependency
fixed

Medium
F-2026-1900Missing length validation on decoded addresses in ProofService
fixed

Medium
F-2026-1900stdin is not checked for TTY before prompting, so the "hidden" mnemonic prompt can silently echo in cleartext
fixed

Medium
1-10 of 20 findings

Identify vulnerabilities in your smart contracts.

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:

Severity

Description

Critical
Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.

High
High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.

Medium
Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.

Low
Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution.
  • Severity

    Critical

    Description

    Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.

    Severity

    High

    Description

    High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.

    Severity

    Medium

    Description

    Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.

    Severity

    Low

    Description

    Major deviations from best practices or major Gas inefficiency. These issues will 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.

Appendix 2. Scope

The scope of the project includes the following smart contracts from the provided repository:

Scope Details

Repositoryhttps://github.com/Zilliqa/zkp_recovery_app
Commitd52b921
Whitepaperhttps://hackenio.cc/hacken-methodologies
Requirements
Technical Requirements

Assets in Scope

bip32lib.circom - bip32lib.circom
circuit.circom - circuit.circom
0xPARC
circom-ecdsa - 0xPARC › circom-ecdsa
Electron-Labs
sha512 (circuits) - Electron-Labs › sha512 (circuits)
escrow_v1.sol - escrow_v1.sol
input_step_content.dart - input_step_content.dart
onboarding_stepper_page.dart - onboarding_stepper_page.dart
prepare.js - prepare.js
print_submit.js - print_submit.js
proof_service.dart - proof_service.dart
prove_secure.js - prove_secure.js

Appendix 3. Additional Valuables

Verification of System Invariants

The following invariants were independently checked and confirmed sound:

  • Circuit arithmetic: the secp256k1 field prime, group order, and 8160-entry precomputed generator table (ecdsa_func.circom) were checked entry-by-entry against reference values; CheckInRangeSecp256k1, CheckCarryToZero, and BigMultNoCarry's polynomial identity were verified sound with overflow margins recomputed by hand.

  • SHA-512/HMAC stack: padding is correct for every instantiated bit-length, and the in-circuit chain code was verified byte-for-byte against real @scure/bip32 derivations — the load-bearing check that the whole CKD path is faithful to BIP-32.

  • Contract invariants: the ERC-7201 storage slot was recomputed and matches; CEI ordering in claim() is correct (no reentrancy); _disableInitializers() correctly locks the implementation; public signals [old, new, domain, isHardened] are read from the bound proof output rather than a separate caller-supplied argument, which closes proof-redirection; chainid binding prevents cross-chain replay.

  • Client/circuit consistency: the Dart proof-input encoding, calldata ABI layout (selector, G2 coordinate swap, uint256[2],uint256[2][2],uint256[2],uint256[4] head layout), and address derivation (sha256(pubkey)[-20:]) all match the circuit's constraints exactly, with reproduction scripts pinning the correspondence.

Recommendations

The Escrow.sol contract in scope could benefit from the introduction of automatic emergency actions for critical activities, such as unauthorized operations like proxy upgrades, as well as unexpected fund manipulations, including large or anomalous claim() payouts. Adding such mechanisms would enable the protocol to react automatically to unusual activity, ensuring that the contract remains secure and functions as intended even if a circuit soundness gap or a compromised proving artifact is exploited before it is caught.

To improve functionality, these emergency actions could be designed to trigger under specific conditions, such as:

  • Detecting proxy upgrade attempts, given that _authorizeUpgrade currently gates on msg.sender address(0) with no on-chain visibility into what triggers that call.

  • Monitoring large or unexpected claim() payouts relative to typical lodged balances, and flagging repeated claims against the same srcAddress in a short window.

  • Pausing lodge() and claim() when irregularities are identified, so deposits and payouts halt while the anomaly is investigated rather than continuing to process against a potentially compromised verifier or zkey.

These enhancements would provide an added layer of security, making the contract more robust and better equipped to handle unexpected situations — such as a forged proof slipping past an unaudited trusted-setup contribution, or a circuit defect surfacing post-deployment — while maintaining smooth operations for legitimate migration claims.

Frameworks and Methodologies

This security assessment was conducted in alignment with recognised penetration testing standards, methodologies and guidelines, including the NIST SP 800-115 – Technical Guide to Information Security Testing and Assessment , and the Penetration Testing Execution Standard (PTES) , These assets provide a structured foundation for planning, executing, and documenting technical evaluations such as vulnerability assessments, exploitation activities, and security code reviews. Hacken’s internal penetration testing methodology extends these principles to Web2 and Web3 environments to ensure consistency, repeatability, and verifiable outcomes.

This was supplemented by methodologies specific to the audited system:

  • Spec cross-referencing: Findings are anchored to primary sources — BIP-32/39/44, BIP-173 (bech32), SEC 1 v2 §2.3.3 (point compression), RFC 4231/2104 (HMAC), EIP-55, SLIP-44.

  • Incident-driven threat modeling: The July 2026 Zilliqa Ledger nonce-bias postmortem directly shaped the security property under test — that a leaked leaf key must be insufficient to forge a migration proof, since only the parent node (never exposed by the incident) is accepted as a witness.

  • Conventional unit/integration testing: circom_tester + Mocha across circuit templates (BitAdd, ModAddN, Hmac512, CompressedPub, PrivBitsToAddr, CKDFinalStep, SeedOwnershipMin) and full end-to-end proof/CLI flows.

Disclaimer