Quantstamp reviewed Parity Finance Liquid Staking & Early Purchase on 3 Jun 2025: 32 findings, 0 critical; 22 marked fixed, 10 open.
Report hosted on certificate.quantstamp.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 0 | 6 |
| Medium | 2 | 0 | 7 |
| Low | 8 | 0 | 9 |
The `early-purchase` program includes `Sale` initialization and update instructions. These instructions lack parameter validation, allowing arbitrary updates of the sale properties that may prevent purchases or harm users. For example, it is possible to set: - A too early or too late start date. - Extremely short, extremely long or instantly ending sale. - A Sale with impossible conditions (e.g., `max_tokens_per_user` > `max_tokens_total`). - Zero or too small token price. - Too permissive max price feed age, allowing potential stale prices. - Arbitrary ID values that may have any off-chain implications (e.g., long). - Uninitialized purchase and payment mint accounts (`is_initialized` is not checked). - A zero `max_price_feed_age` which will limit users to only be able to purchase tokens in the same slot a the price feed has been updated which may cause purchase transactions to revert.
The Liquid Staking program appears to have a very limited test suite - there are several unimplemented tests marked with "-" as can be observed in the Test Suite Results section of this report. Contracts that are not extensively tested are at high risk of serious bugs that can impact the protocol's proper functioning and security. While audits can help developers to find bugs, they are not a substitute for thorough unit and integration testing.
The sale organizer (acting through a Guardian) is expected to deposit the `sale.purchase_mint` tokens to fulfill the buyers' purchases after the sale ends in the `early-purchase` program. However, the program currently lacks a mechanism for buyers to cancel their purchases or reclaim their payments if the sale organizer fails to deposit the required `sale.purchase_mint` tokens. This scenario could be exploited by a malicious sale creator who attracts buyers with favorable terms but intentionally fails to deposit the promised tokens. The risk is increased if the ability to create sales is open to all users without significant upfront cost or vetting.
In `liquid_staking::withdraw_fees::handler()` function the collected `Pair` fees are transferred from the `Pair` fee account to a destination account. However, the `destination_owner` of the destination account is an `UncheckedAccount`, which could potentially be an inaccessible address, resulting in permanently lost fees.
The exchange rate calculation logic has improper validation: - It is not validated that `current_timestamp > self.last_yield_change_timestamp`. A malicious validator can slightly manipulate the current timestamp, resulting in a negative elapsed time. - High `interval_amounts` can lead to overflows and can indicate errors. Consider adding an upper limit for interval amounts. - Perform a checked conversion instead of a silent conversion when the amounts are calculated as `u128` and then cast to `u64`, as calculations with lots of decimals can cause overflows.
All `Pubkey` values passed in instructions and set by the program logic are not validated against default value.
Several instructions within the `early-purchase` program and also in the `liquid-staking` program (specifically the `create_pair` instruction) accept Program-Derived Accounts (PDAs) as inputs without sufficiently verifying their derivation using seeds and bump constraints in the `#[account(...)]` macro. While some accounts may have `has_one` constraints (e.g., guardian `has_one = authority`), this only validates a field within the provided account's data against another account (like a signer). It does not guarantee that the account itself is the unique, canonical PDA expected by the program for the given seeds. Without explicit seeds and bump checks, instructions may be potentially tricked into accepting an unintended account address if that account is owned by the same program and its data can be successfully deserialized into the target struct.
In `liquid_staking::update_whitelist.handler()`function, an `access_authority` can update the `access_control.merkle_root` value. However, the function lacks validation to prevent setting the `merkle_root` to an array of all zeros. This oversight creates a potential issue which may block any whitelist verification logic (especially if `is_whitelist_enabled` remains enabled). Additionally, since this function modifies critical program state, an event should be emitted for auditability.
LST token PDAs are initialized only using their prefix and symbol as a seed with the `init_if_needed` directive in the `liquid_staking::create_pair::handler()` function. This permits the creation of pairs using the same LST token, despite the fact that these `base_token_mint` tokens in pairs may have different values. This can result in users staking tokens in one pair and submitting a withdrawal request in a different one.
The `liquid-staking` program implemented the `state::access_control::check_whitelist_access()` function via Merkle proof verification where the leaf node to be verified is created by `solana_program::keccak::hashv(&[user.key().to_string().as_bytes()])`. However, hashing the string representation of a `Pubkey` is non-standard practice, and it can lead to different representations or unexpected results, for example, if different encoding or string conversion is used on the off-chain side.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (436)
236 projects reviewed · 332 reports · latest Oct 2026
See Quantstamp’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us