Introduction
We express our gratitude to the ZetaChain team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.
zeta-migration is a Solana migration program that pays ZETA holders a new SPL token in place of the token they hold, according to an off chain snapshot. The program only enforces that no entry is paid twice and that total payouts stay within fixed caps, while an off chain Go service decides who is paid. The audit covered the zeta_migration Anchor program.
Document | |
|---|---|
| Name | Smart Contract Code Review and Security Analysis Report for ZetaChain |
| Audited By | Kerem Solmaz |
| Approved By | Hamza Sajid |
| Website | https://www.zetachain.com/→ |
| Changelog | 23/10/2026 - Preliminary Report |
| 30/10/2026 - Final Report | |
| Platform | Solana |
| Language | Rust (Anchor 0.31.1) |
| Tags | Claims, Airdrop, Centralization, Fungible Token |
| Methodology | https://docs.hacken.io/methodologies/smart-contracts→ |
Document
- Name
- Smart Contract Code Review and Security Analysis Report for ZetaChain
- Audited By
- Kerem Solmaz
- Approved By
- Hamza Sajid
- Website
- https://www.zetachain.com/→
- Changelog
- 23/10/2026 - Preliminary Report
- 30/10/2026 - Final Report
- Platform
- Solana
- Language
- Rust (Anchor 0.31.1)
- Tags
- Claims, Airdrop, Centralization, Fungible Token
Review Scope | |
|---|---|
| Repository | https://github.com/zeta-chain/zeta-migration→ |
| Initial Commit | ffe0064 |
| Final Commit | dfe7a2d |
Review Scope
- Initial Commit
- ffe0064
- Final Commit
- dfe7a2d
Audit Summary
The system users should acknowledge all the risks summed up in the risks section of the report
Documentation quality
The README describes the launch sequence, the cold key operations, the incident flow, key rotation, and the pinned toolchain versions in detail.
A formal specification or whitepaper is not present.
Rustdoc and inline comments cover every instruction handler and the state types.
The README matches the implemented instruction set and role model. Some safety properties rely on off chain procedures that are documented only as operator guidance.
A small number of code comments diverge from the implemented behavior.
Code quality
The code follows Anchor practice: account constraints,
has_onechecks, stored PDA bumps, and typed program accounts are used consistently.The program is small and split into one module per instruction, with named custom errors and checked arithmetic on all value fields.
Dependency versions are pinned with
=and the lockfile is committed.Issues in business logic, input validation, and code hygiene were identified and reported.
Test coverage
The code coverage of the project cannot be measured, because neither cargo llvm-cov nor cargo tarpaulin is available in the audit environment, however:
The tests live in a separate harness workspace and run the built program in litesvm.
Test files exist for initialization, claim creation, manual claims, claiming, amount updates, entry closing, pausing, authority rotation, and guard conditions.
A mutation score was not produced.
System Overview
The zeta-migration system moves the holdings of ZETA holders onto Solana as a new SPL token. It has two parts: an off chain Go service that reads the ZetaChain snapshot, verifies holder signatures, and submits transactions, and the on chain zeta_migration program, which is the only component in the audit scope. The program verifies no identity and mints nothing. Its two jobs are that no entry is paid twice and that total payouts never exceed the configured caps.
The program keeps three kinds of state. A single Config account at a fixed PDA stores the token mint, the vault address, the two authorities with their pending nominees, the pause flag, and two populations of counters, each with an immutable cap and running committed and claimed totals. One Entry account per source key, derived from the chain id and a 32 byte source key, records the recipient, the destination token account, the amount, a claimed flag, and a manual flag. A vault is an SPL token account PDA owned by a vault authority PDA, and it is funded by an external mint, since the program has no funding instruction. The token is a classic SPL Token mint with 9 decimals, no freeze authority, and a mint authority held outside the program. Amounts are u64 base units, and the supply is set by the snapshot total, with no fixed value in the code.
The initialize instruction runs once and is gated on the program upgrade authority. It creates Config and the vault, and takes the mint, both caps, and both authorities as arguments. The entry authority, a hot key held by the relay, calls create_claim, which writes an Entry paying the recipient associated token account and commits the amount against the cap. The cold authority, a Squads multisig vault, calls create_manual_claim to write an Entry with a named destination token account, update_entry_amount to correct an amount, close_unclaimed_entry to release a commitment and free the address, pause and unpause, and set_authorities to nominate new keys. A nominee confirms a role change through accept_authority. Any signer may call claim, which checks the entry, the destination, the mint, and the recipient, creates the associated token account if needed, records the claim against the cap, transfers tokens from the vault with transfer_checked signed by the vault authority PDA, and marks the entry claimed. The program depends on the SPL Token, Associated Token, System, and BPF upgradeable loader programs. Token-2022 is not supported.
Privileged roles
Upgrade authority (zeta_migration): The BPF loader upgrade authority is handed to a Squads multisig after deployment. It is the only signer able to call
initialize, which sets the mint, both caps, and the initial authorities with no on chain validation of those values. It can also upgrade the program, which is the only way to change the caps or any immutable configuration. A compromised or misused upgrade authority can replace the program logic and redirect vault funds. The role is protected by a multisig, and no timelock is described.Entry authority (zeta_migration): A hot key held in a key file by the relay service. It can call
create_claimto write an entry for any recipient and any amount up to the remaining cap, while the program is not paused. It cannot update or close entries. Compromise of this key allows false entries that any caller can then claim, bounded by the cap. Pausing, voiding, and rotation are the available mitigations, and all of them depend on the cold authority. The key is a single key file with no multisig or timelock.Cold authority (zeta_migration): A Squads multisig vault. It can call
create_manual_claimwith an arbitrary destination token account and amount,update_entry_amount,close_unclaimed_entrywith an arbitrary rent receiver,pause,unpause, andset_authorities. Update, close, and rotation calls remain available while the program is paused. Misuse could redirect payouts through manual entries, void entries, or take over both roles through nominations. The role sits behind the Squads multisig threshold, with no timelock. The program does not prevent the entry and cold authorities from being the same key.Nominee (zeta_migration): Any key named as a pending entry or cold authority by
set_authorities. The nominee callsaccept_authorityto take the role, which clears the pending nomination. Accepting the cold role also clears a pending entry nomination. The role grants no power until the cold authority nominates the key.Mint authority holder (external to zeta_migration): The mint authority is an SPL token multisig with threshold 1, whose signers are the issuer bridge and the migration minter. It is not part of the program, but it controls the vault funding and can mint further tokens after launch. The program never compares the vault balance with the caps.
Potential Risks
Scope Definition and Security Guarantees: The audit covers only the zeta_migration Anchor program. The off chain Go service that reads the snapshot, verifies holder signatures, builds transactions, and runs the funding step is outside the scope, as are the Squads multisig configuration, the key handling of the relay, and the launch procedures described in the README. The program trusts the service completely for recipient identity and amount, so the correctness of every payout depends on code and operations that were not assessed.
Dependency on External Logic for Implemented Logic: The zeta_migration program does not fund its own vault and has no instruction that deposits tokens. The vault is filled by an external mint step performed by the migration minter, and the program never compares the vault balance with the two caps. If the vault is funded below the snapshot total, claims past the funded amount fail at the token transfer until the vault is topped up. The match between the caps and the funded amount relies on the off chain funding step and its operator checks.
Centralized Control of Minting Process: The mint authority of the migrated token is an SPL token multisig with threshold 1, whose signers are the issuer bridge and the migration minter. Either signer alone can mint further tokens after launch, and the supply has no fixed value in the code, because it follows the snapshot total. The program has no control over this authority, so holders depend on the operational discipline of these two signers.
Administrative Key Control Risks: The entry authority is a single hot key stored in a key file on the relay. It can write entries for any recipient and amount up to the remaining cap, and any caller can claim them. If the key is stolen, the only containment is a pause by the cold authority, and entries created and claimed in the same transaction can be paid before a pause takes effect. The loss is bounded by the cap but cannot be recovered once tokens leave the vault.
Single Entity Upgrade Authority: The upgrade authority of zeta_migration is held by one Squads multisig after deployment. It alone can call the initialization instruction that sets the mint, both caps, and both authorities without on chain validation, and it can replace the program code. The caps are immutable except through an upgrade, so a mistaken initialization or a malicious upgrade directly affects the vault funds.
Absence of Time-lock Mechanisms for Critical Operations: No timelock protects program upgrades, authority nominations, manual claims, entry amount changes, or entry closing. The cold authority can write a manual entry with an arbitrary destination token account and amount and can rotate both authorities, and holders have no review window before such changes take effect. Update, close, and rotation operations also stay available while the program is paused.
Insufficient Multi-signature Controls for Critical Functions: The cold authority is a multisig, while the entry authority is a single key, and the program does not prevent both roles from being set to the same key at initialization. If the same key fills both roles, the pause and void protections that depend on the cold authority are available to an attacker who holds the hot key.
Findings
Code ― | Title | Status | Severity | |
|---|---|---|---|---|
| F-2026-2002 | Missing requirewallet Recipient Validation In createclaim | fixed | Low | |
| F-2026-2002 | Missing Destination Owner Revalidation In claim Allows Third Parties To Redirect Manual Payout | fixed | Low | |
| F-2026-2006 | claim Re-Derives The Retail ATA Address That create_claim Already Stored | fixed | Observation | |
| F-2026-2006 | Missing Canonical Anchor IDL Initialisation In Launch Flow | fixed | Observation | |
| F-2026-2005 | Doc Comments Diverge From Implemented Behaviour In claim And pause | fixed | Observation | |
| F-2026-2005 | Missing Event Emission In initialize | fixed | Observation | |
| F-2026-2005 | Missing Zero Address Validation For entryauthority And coldauthority In initialize | fixed | Observation | |
| F-2026-2005 | Missing Decimals Check On mint In initialize | fixed | Observation | |
| F-2026-2005 | Unused rent Sysvar Account In Initialize | fixed | Observation | |
| F-2026-2003 | Error Messages Disagreeing With Guarded Conditions In RecipientNotAWallet And EntryClaimed | mitigated | Observation |
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 | |
|---|---|
| Repository | https://github.com/zeta-chain/zeta-migration→ |
| Initial Commit | ffe006474a2ad924daf10322b6dedc44b3eaa44e |
| Final Commit | dfe7a2d3f09a9a37213bfeb41b5a0da9207fc725 |
| Whitepaper | - |
| Requirements | README.md |
| Technical Requirements | README.md |
Scope Details
- Initial Commit
- ffe006474a2ad924daf10322b6dedc44b3eaa44e
- Final Commit
- dfe7a2d3f09a9a37213bfeb41b5a0da9207fc725
- Whitepaper
- -
- Requirements
- README.md
- Technical Requirements
- README.md
Assets in Scope
Appendix 3. Additional Valuables
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:
Detecting changes to ownership or critical permissions.
Monitoring large or unexpected transactions and minting events.
Pausing operations when irregularities are identified.
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.
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.