Veilon Info

A zk-native cross-chain wallet and protocol for private, untraceable transfers, swaps, and bridging powered by stealth addresses & encrypted transaction layers.

Veilon Logo

TrustNet Score

The TrustNet Score evaluates crypto projects based on audit results, security, KYC verification, and social media presence. This score offers a quick, transparent view of a project's credibility, helping users make informed decisions in the Web3 space.

20.00
Poor Excellent

Real-Time Threat Detection

Real-time threat detection, powered by Cyvers.io, is currently not activated for this project.

This advanced feature provides continuous monitoring and instant alerts to safeguard your assets from potential security threats. Real-time detection enhances your project's security by proactively identifying and mitigating risks. For more information, click here.

Security Assessments

Select the audit
"Static Analysis Dynamic Analysis Symbolic Execution SWC Check Manual Review"
Contract address
N/A
Network N/A
License N/A
Compiler N/A
Type N/A
Language Solidity
Onboard date 2026/02/25
Revision date 2026/02/25

Summary and Final Words

No crucial issues found

The contract does not contain issues of high or medium criticality. This means that no known vulnerabilities were found in the source code.

Contract owner cannot mint

It is not possible to mint new tokens.

Contract owner cannot blacklist addresses.

It is not possible to lock user funds by blacklisting addresses.

Contract owner cannot set high fees

The fees, if applicable, can be a maximum of 25% or lower. The contract can therefore not be locked. Please take a look in the comment section for more details.

Contract cannot be locked

Owner cannot lock any user funds.

Token cannot be burned

There is no burning within the contract without any allowances

Ownership is renounced

The contract does not include owner functions that allow post-deployment modifications.

Contract is not upgradeable

The contract does not use proxy patterns or other mechanisms to allow future upgrades. Its behavior is locked in its current state.

Scope of Work

This audit encompasses the evaluation of the files listed below, each verified with a SHA-1 Hash. The team referenced above has provided the necessary files for assessment.

The auditing process consists of the following systematic steps:

  1. Specification Review: Analyze the provided specifications, source code, and instructions to fully understand the smart contract's size, scope, and functionality.
  2. Manual Code Examination: Conduct a thorough line-by-line review of the source code to identify potential vulnerabilities and areas for improvement.
  3. Specification Alignment: Ensure that the code accurately implements the provided specifications and intended functionalities.
  4. Test Coverage Assessment: Evaluate the extent and effectiveness of test cases in covering the codebase, identifying any gaps in testing.
  5. Symbolic Execution: Analyze the smart contract to determine how various inputs affect execution paths, identifying potential edge cases and vulnerabilities.
  6. Best Practices Evaluation: Assess the smart contracts against established industry and academic best practices to enhance efficiency, maintainability, and security.
  7. Actionable Recommendations: Provide detailed, specific, and actionable steps to secure and optimize the smart contracts.

A file with a different Hash has been intentionally or otherwise modified after the security review. A different Hash may indicate a changed condition or potential vulnerability that was not within the scope of this review.

Final Words

The following provides a concise summary of the audit report, accompanied by insightful comments from the auditor. This overview captures the key findings and observations, offering valuable context and clarity.


No Audit Comments.

Files and details

Findings and Audit result

critical Issues | 3 findings

Acknowledged

#1 critical Issue
Catastrophically Weak PIN Hashing
stores/securityStore.ts
L38-50
Description

The securityStore uses a custom bit-shifting hash function with a hardcoded salt ('veilon_salt_2024') to hash user PINs. This is not a cryptographic hash function — it produces a 32-bit integer, meaning there are at most ~4 billion possible outputs. With a 4-6 digit PIN space (10,000 to 1,000,000 PINs), the entire keyspace can be brute-forced in microseconds. The hardcoded salt means all users share the same salt, enabling rainbow table attacks. Critically, a proper PinSecurityService exists at services/pin-security.ts that uses SHA-256 with random per-user salts, but the securityStore does NOT use it.

Resolved

#2 critical Issue
Commitment Secrets Stored Unencrypted
utils/commitment-store-rn.ts
L218
Description

All commitment data — including secret, nullifier, randomness (the private witness values for ZK proofs) — is stored in AsyncStorage via JSON.stringify. On Android, AsyncStorage uses an unencrypted SQLite database. On a rooted device or via a backup extraction, an attacker can read all commitment secrets and: (1) reconstruct all user commitments, (2) generate valid nullifier hashes to spend user funds, (3) compute the user's full private balance and transaction history. Private keys are correctly stored in SecureStore (encrypted), but the commitment secrets that protect shielded funds are not.

Pending

#3 critical Issue
Private Secrets Transmitted to Backend Relayer
services/veilon-sdk.ts
L700-726
L975-1001
Description

The mobile app transmits commitment secrets (secret, nullifier, randomness, changeSecret, changeNullifier, changeRandomness, inputSecret, inputNullifier, etc.) to the backend relayer in plaintext via HTTP POST. The relayer sees all private values needed to reconstruct commitments, generate nullifier hashes, and forge proofs. This completely negates the privacy guarantees of ZK proofs. In a genuine privacy protocol, proof generation MUST happen client-side so that secret values never leave the user's device.

high Issues | 2 findings

Pending

#1 high Issue
Extensive Console Logging of Private Keys and Secrets
services/veilon-sdk.ts + stores/walletStore.ts + utils/commitment-store-rn.ts
Lveilon-sdk.ts:289-295,735,1010-1012,1110-1118; walletStore.ts:77; commitment-store-rn.ts:33-37
Description

The mobile app logs private keys, commitment secrets, nullifiers, and proof data to the JavaScript console. On Android, these logs are accessible via adb logcat. On iOS, they are accessible via Xcode console. In production builds, console.log may still be active and can be captured by crash reporting tools or device logs.

Resolved

#2 high Issue
No PIN Brute-Force Protection
stores/securityStore.ts
L135-160
Description

The verifyPin function has no rate limiting, lockout mechanism, or attempt counter. With a 4-digit PIN (10,000 possibilities) and no delays, an attacker with physical device access can brute-force the PIN in seconds. Even with the weak hash (see FE-CRIT-01), the attack is trivial.

medium Issues | 7 findings

Pending

#1 medium Issue
Proof Format Conversion — Fragile and Inconsistent
services/veilon-sdk.ts
L863-872
L1103-1108
Description

The mobile app converts snarkjs proof format to the on-chain Verifier.Proof struct in two different places with slightly different patterns. The pi_b coordinate handling is critical: snarkjs outputs pi_b in one order, but the Groth16 verifier expects reversed inner arrays. The Verifier.sol contract (line 276-279) reverses pi_b internally. The mobile app does NOT reverse pi_b before sending (which is correct since the contract does it), but a code comment says 'Fixed: DO NOT reverse - tests show this is correct format' — indicating this was arrived at through trial-and-error rather than understanding. The submitUnshieldTransaction and submitPrivateTransfer use different proof construction logic, creating inconsistency risk.

Pending

#2 medium Issue
Cryptographic Randomness Source Concerns
services/veilon-sdk.ts
L689-691
L965-972
Description

The mobile app generates change secrets and output secrets using ethers.randomBytes(31). In React Native, ethers.randomBytes depends on the polyfilled crypto.getRandomValues. The app imports 'react-native-get-random-values' for this, but the quality of randomness depends on the native implementation. For a privacy-critical application, the randomness source should be explicitly validated.

Pending

#3 medium Issue
No TLS Certificate Pinning
constants/relayer.ts
L17-19
Description

API calls to the relayer use HTTPS in production but have no certificate pinning. This makes the app vulnerable to MITM attacks via compromised or rogue CAs. Given that the relayer receives secret commitment values (see FE-CRIT-04), a MITM attacker could intercept all private transaction data.

Pending

#4 medium Issue
Dual Private Key Storage Paths
stores/walletStore.ts + services/veilon-sdk.ts
LwalletStore.ts:301-305
Lveilon-sdk.ts:56-57
Description

Private keys are stored in SecureStore under two different key patterns: (1) walletStore uses 'wallet_pk_{address}' and 'wallet_mnemonic_{address}' per wallet, (2) veilon-sdk's initializeFromStorage uses 'wallet_private_key' (a single global key). This creates confusion about which key is the source of truth and could lead to stale or incorrect key usage if one path is updated but not the other.

Pending

#5 medium Issue
Hardcoded Contract Address Mismatch
services/veilon-sdk.ts
L1276
Description

The syncCommitments method hardcodes the PrivacyPool address as '0x2f2B95f475F9774F52bE449e060fC52727B11dEB' for Sepolia. However, the canonical contract address in constants/contracts.ts is '0x33a5f045eECb8C17b0CBeaE29ed08e6D0E2ec5f5'. This means commitment synchronization queries the WRONG contract, so the spent status of nullifiers is never correctly synced. Users may see stale balances and attempt to double-spend commitments that were already spent, causing transaction failures.

Pending

#6 medium Issue
Cross-Chain Replay Attack — No Chain Binding in Proofs
services/veilon-sdk.ts
L262
L418
L698
Description

The mobile app sends chainId and network to the backend relayer, but these are not included in the ZK circuit inputs or public signals. A proof generated on one chain can be replayed on another. The mobile app does not validate that the proof it receives back is bound to the current chain before submitting it to the smart contract.

Pending

#7 medium Issue
Raw Nullifier Used for On-Chain Query Without Format Conversion
utils/commitment-store-rn.ts
L280-282
Description

The syncCommitmentsWithBlockchain method queries spentNullifiers(c.nullifier) using the raw nullifier string from local storage. The contract expects bytes32, and ethers.js will attempt auto-conversion. If the nullifier is stored as a decimal string (from Poseidon hash), the conversion to bytes32 may not match how the contract stored the nullifierHash. The contract stores bytes32(proof.publicInputs[0]) which is the nullifierHash (H(nullifier, randomness)), not the raw nullifier.

low Issues | 7 findings

Pending

#1 low Issue
Hardcoded Token Decimals Assumption
utils/commitment-store-rn.ts
L199
Description

getAllPrivateBalances assumes USDC/USDT use 6 decimals and everything else uses 18. This is fragile and will produce incorrect balances for tokens with non-standard decimal counts.

Pending

#2 low Issue
Only Sepolia Supported Despite Multi-Chain UI
constants/chains.ts + constants/contracts.ts
LN/A
Description

The app has chain switching UI and network configuration for mainnet, BSC, Polygon, and Arbitrum, but getContractAddress only supports 'sepolia'. Attempting to use another network will throw an error. This creates a confusing UX and could lead to unintended behavior.

Pending

#3 low Issue
Hardcoded Development IP Address
constants/relayer.ts
L18
Description

The development relayer URL is hardcoded to 'http://172.20.10.3:3004'. This is a specific local network IP that only works on the developer's machine. Other developers will need to change this manually.

Pending

#4 low Issue
Hardcoded API Key in Version Control
eas.json
LN/A
Description

The Etherscan API key 'B1U18KNWNRF73Y58K35G2E84AN6I1W9SJZ' is hardcoded in eas.json and committed to version control. This key can be extracted from the repository and abused for rate-limited Etherscan API access.

Pending

#5 low Issue
leafIndex Always Zero — Incomplete Implementation
services/veilon-sdk.ts
L1206
Description

When saving commitments, the leafIndex is hardcoded to 0 with a TODO comment: 'TODO: Parse from contract event logs'. While this doesn't currently affect functionality (since there's no Merkle tree), it means if a Merkle tree is ever implemented, all existing commitments will have incorrect leaf indices, making them impossible to spend without migration.

Pending

#6 low Issue
Hardcoded RPC Endpoint
services/veilon-sdk.ts
L27-29
Description

The RPC provider URL 'https://ethereum-sepolia-rpc.publicnode.com' is hardcoded in the service. Public RPC nodes may have rate limits, reliability issues, and can observe all queries from the app (wallet balances, transaction submissions). For a privacy-focused wallet, the RPC provider can observe which addresses the user queries, partially de-anonymizing them.

Pending

#7 low Issue
Public Signal Ordering Not Validated Before Contract Submission
services/veilon-sdk.ts
L882-887
L1103-1108
Description

The mobile app receives public signals from the backend relayer and passes them directly to the smart contract without validating their ordering or contents. The contract assumes proof.publicInputs[0] is the nullifierHash, but the mobile app does not assert this before submission. If the backend ever changes signal ordering (e.g., after circuit recompilation), transactions will fail silently or, worse, succeed with mismatched state.

informational Issues | 5 findings

Pending

#1 informational Issue
No Test Coverage
N/A
LN/A
Description

The mobile codebase contains zero test files. There are no unit tests for commitment storage, no tests for proof format conversion, no tests for the critical contract interaction flows (shield, unshield, transfer), and no tests for PIN security.

Pending

#2 informational Issue
Deprecated Method Still in Use
services/veilon-sdk.ts
L542-554
Description

The shield() method is marked @deprecated in favor of shieldETH(), but it still exists and redirects. Deprecated methods should be removed after migration.

Pending

#3 informational Issue
SDK Wrapper Layer Adds Complexity Without Value
utils/sdk/
LN/A
Description

The mobile app has a VeilonSDK class in utils/sdk/ that is initialized in veilon-sdk.ts, but the actual contract interactions (shield, unshield, transfer) bypass the SDK and call the contract directly via ethers.js. The SDK layer adds indirection without providing additional functionality.

Pending

#4 informational Issue
No Monitoring, Alerting, or Error Reporting
N/A
LN/A
Description

There is no application monitoring, crash reporting (Sentry, Bugsnag, etc.), performance tracking, or analytics. Console.log is the only logging mechanism. For a financial application handling real value, this is insufficient.

Pending

#5 informational Issue
AppState Listener for Auto-Lock Not Cleaned Up
stores/securityStore.ts
L95-99
Description

The AppState.addEventListener call in initialize() is never cleaned up (no removeEventListener). If initialize() is called multiple times, duplicate listeners accumulate, causing multiple lock() calls on each app state change.