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.

80.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

"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 Rust / Solana
Onboard date 2026/01/27
Revision date 2026/01/27

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 not renounced

The owner retains significant control, which could potentially be used to modify key contract parameters.

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.


Ownership Privileges
  • The Admin can initialize the sale configuration, defining the token price, sale window, caps, and claim schedule.
  • The Admin can update all sale parameters (price, times, caps, destination wallet) only while the sale is not frozen.
  • The Admin can "freeze" the configuration, which permanently locks the sale terms and enables user contributions, effectively marking the sale as "Ready".
  • The Admin can update the Whitelist (Merkle Root) at any time, even while the sale is live, allowing them to add or remove users mid-sale.
  • The Admin can deposit Sale Tokens into the vault at any time to ensure solvency.
  • The Admin can withdraw the raised SOL proceeds only after the sale end time (wl_end_ts) has passed.
  • The Admin can sweep unsold or excess Sale Tokens only after the sale end time has passed, but cannot touch tokens already allocated to users.
  • The Admin can recover mistaken USDC deposits from the treasury at any time.

Note - This audit report consists of a security analysis of the Presale contract. This analysis did not include functional testing (or unit testing) of the token’s logic. Furthermore, we only audited the mentioned contract associated with this project. Other contracts related to this project were not audited by our team. We recommend investors conduct their own research before engaging with the token.

Files and details

Functions
public

/

State variables
public

/

Total lines
of code

/

Capabilities
Hover on items

/

Findings and Audit result

medium Issues | 3 findings

Resolved

#1 medium Issue
Unvalidated Funds Destination (Self-Transfer & Type Lock)
lib.rs
L38
L164
Description

Both functions accept a funds_destination Pubkey without performing critical validation checks. This leads to two distinct "Trap" scenarios that result in permanent fund loss: Self-Transfer Lock: If the destination is set to the treasury_sol PDA itself, the withdraw_sol instruction will invariably crash due to a runtime double-borrow panic (borrowing the same account as both source and destination). Type Mismatch Lock: If the destination is set to a non-system account (e.g., a PDA or Program) and the config is subsequently frozen, the funds become irretrievable. withdraw_sol will fail because it strictly requires a SystemAccount, and the Admin cannot correct the destination because set_funds_destination is blocked by the !is_frozen check.

Resolved

#2 medium Issue
Centralization Risk (Mutable Whitelist)
lib.rs
L111-124
Description

The set_merkle_root instruction explicitly bypasses the is_frozen check, allowing the Admin to update the whitelist Merkle Root at any time, even while the sale is active. While this enables dynamic whitelist additions (a business requirement), it introduces significant centralization risk. A malicious or compromised admin key could front-run legitimate buyer transactions to deny them entry (by changing the root) or insert their own wallets into a sold-out round. Furthermore, legitimate updates during high-traffic periods will cause concurrent user transactions to fail ("Invalid Merkle Proof") because the proofs generated off-chain against the old root will not match the new on-chain root.

Resolved

#3 medium Issue
Precision Loss (Dust Overpayment)
lib.rs
L196
Description

The contract calculates token allocation using integer division (tokens = (sol * rate) / 1e9), which implicitly rounds down. This effectively confiscates any SOL "change" that doesn't amount to a full token base unit. In scenarios with low exchange rates or awkward contribution amounts (e.g., contributing 1.5 SOL when the rate dictates integer steps of 1 SOL), a user could lose a significant percentage (up to ~50%) of their contribution value instantly. The contract takes the full SOL amount but only credits the floored token amount.

low Issues | 5 findings

Resolved

#1 low Issue
Logical Cap Inconsistency
lib.rs
L56-57
L156-157
Description

Both initialize_sale and set_caps allow the configuration of sale caps without performing basic sanity checks. Specifically, they permit: max_tokens_per_wallet to be strictly greater than max_tokens_total. max_tokens_total to be set to 0. While these settings do not introduce security vulnerabilities, they create a broken logical state. A zero total cap effectively "bricks" the sale (making contributions impossible), and a wallet cap larger than the total cap creates a confusing user experience where transactions fail due to the total limit despite being within the user's personal limit.

Resolved

#2 low Issue
Zero-Cap Initialization
lib.rs
L56
L153-159
Description

Both initialize_sale and set_caps lack validation to prevent max_tokens_total from being set to 0. If this occurs, the sale is rendered functionally useless ("bricked"). Any user attempting to contribute_sol with a non-zero amount will immediately trigger a "Total Cap Exceeded" error, as the new total (0 + amount) will always exceed the max cap of 0. This requires an additional admin transaction to fix before the sale can proceed.

Resolved

#3 low Issue
One-Way Freeze (Permanent Lock)
lib.rs
L98
Description

The contract implements a strictly one-way state transition for the configuration. Once freeze_config is called, is_frozen is set to true, and there is no instruction to reverse this state (unfreeze). While this ensures immutability during the sale, it creates a significant operational risk: if the Admin detects a configuration error (e.g., incorrect price rate, wrong timestamps, or typo in caps) after freezing but before any user has contributed, they cannot correct it. The entire sale instance must be abandoned and redeployed, wasting SOL and setup time.

Resolved

#4 low Issue
Invalid Time Configuration (Past Timestamps)
lib.rs
L126-134
Description

The function updates wl_start_ts and wl_end_ts without verifying that the wl_end_ts is in the future relative to the current blockchain time (Clock::get()?.unix_timestamp). This permits the Admin to accidentally configure a sale window that has already expired, rendering the sale unstartable or instantly finished, which is a logic error in state management.

Resolved

#5 low Issue
Missing Rate Cap (Sanity Check)
lib.rs
L136-142
Description

While the function checks that the rate is positive (> 0), it lacks a "Maximum Reasonable Rate" sanity check. An admin could accidentally set the rate to an extremely high value (e.g., u64::MAX), ensuring that the first user who sends even 1 lamport would instantly drain the entire max_tokens_total allocation, effectively ruining the sale distribution.

informational Issues | 7 findings

Resolved

#1 informational Issue
Hardcoded Legacy Token Program Prevents Token-2022 Support
lib.rs
L504
L513
L525
L651
L695
L738
L806
Description

The contract explicitly defines the token_program account as Program<'info, Token>, which strictly enforces the use of the legacy Solana Token Program (Tokenkeg...). Additionally, constraints on mints and token accounts use specific types (Account<'info, Mint>, Account<'info, TokenAccount>) that default to the legacy standard. As a result, if a project attempts to initialize a sale using a modern Token-2022 asset (e.g., a token with transfer fees or metadata extensions), the transaction will fail during account validation because the Mint's owner (Token-2022 Program) will not match the expected Legacy Token Program ID. This severely limits the contract's compatibility with newer Solana standards.

Acknowledged

#2 informational Issue
Wasted Storage (Dead Code)
lib.rs
L51
L52
Description

The function stores usdc_decimals and sale_decimals into the SaleConfig account, but these values are never read by any other instruction (logic reads decimals directly from mint accounts). This needlessly increases the account size (Rent cost) and wastes compute units.

Resolved

#3 informational Issue
Missing Zero-Check for Merkle Root (Accidental DoS)
lib.rs
L111-124
Description

The function set_merkle_root does not validate that the new_root is non-zero. If the admin accidentally (or maliciously) updates the root to [0; 32], the sale effectively becomes paused for everyone, as generating a valid Merkle proof for a zero root is cryptographically impossible. While this could be used as an emergency pause feature, the lack of an explicit check increases the risk of accidental "bricking" of the whitelist during updates.

Resolved

#4 informational Issue
Price Precision Limitation (Integer Only)
lib.rs
L136-142
Description

The contract defines the exchange rate as wl_rate_num_sol (Tokens per 1 SOL), which is a single integer. This data structure strictly forces the token price to be less than or equal to 1 SOL. It makes it mathematically impossible to configure a sale for expensive tokens where 1 Token costs more than 1 SOL (e.g., a rate of 0.5 tokens per SOL would require a fractional numerator or a denominator).

Resolved

#5 informational Issue
Redundant Initialization Logic
lib.rs
L210
Description

The manual call to init_allocation_if_needed is largely redundant because the Anchor constraint init_if_needed (Line 584) already handles the zero-initialization of the account memory. Relying on alloc.buyer == Pubkey::default() to detect a "new" account is valid but unconventional compared to simply trusting Anchor's init process.

Resolved

#6 informational Issue
Missing Event Emission
lib.rs
L319-333
Description

The deposit_sale_tokens function performs a state change (moving tokens) but does not emit an Anchor event. This makes it difficult for off-chain indexers and frontends to track when the sale has been funded without monitoring raw SPL Token logs.

Resolved

#7 informational Issue
Restrictive Account Type (SystemAccount Only)
lib.rs
L761
Description

The withdraw_sol instruction enforces that funds_destination must be a SystemAccount. This implementation detail prevents the project from setting the withdrawal destination to a Smart Contract Vault, a Multisig PDA, or any account not owned by the System Program. Given that SOL can technically be credited to any account on Solana, this restriction limits the project's ability to use advanced treasury management solutions (e.g., Squads, Gnosis Safe style logic) directly.