As of 30 Oct 2026, OnRe has 6 audit reports by 1 security firm on Trustblock; the latest is by Quantstamp, dated 22 Sep 2026. Those reports list no open critical findings.
Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| OnRe EVM | Security firm: Quantstamp | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · certificate.quantstamp.com |
| OnRe - mmbot | Security firm: Quantstamp | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · certificate.quantstamp.com |
| Onre - Re-Audit - 2 | Security firm: Quantstamp | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · certificate.quantstamp.com |
| OnRe Solana Diff | Security firm: Quantstamp | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · certificate.quantstamp.com |
| OnRe Solana | Security firm: Quantstamp | Date: | Scope: Files named in the report | No open critical findings | Open · certificate.quantstamp.com |
| Nayms - OnRe Offer/Redemption Program Spec | Security firm: Quantstamp | Date: | Scope: Files named in the report | No open critical findings | Open · certificate.quantstamp.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 0 | 2 |
| Medium | 1 | 2 | 4 |
| Low | 5 | 11 | 16 |
The `cancel_redemption_request` instruction accepts both `redemption_offer` and `redemption_request` as mutable accounts but does not enforce any relationship between them. There is no validation that the provided redemption request belongs to the specified redemption offer.
Open the reportWhen an offer's total duration is not evenly divisible `price_fix_duration`, a partial interval will occur at the end of the offer period that is not accounted for in the protocol's pricing calculation. The price calculation logic fails to properly handle this edge case, causing users who transact during this period to pay one interval more than the intended maximum price (`sell_token_end_amount`). An additional note here is that in the current implementation this behaviour will also occur if a user calls one of the take offer functions in the final second of the offer, because of the inclusive upper bounds check on `offer.offer_end_time`. Consider this simplified example: 1. The team creates an offer where the duration from start to end is 2.5 days. 2. The `price_fix_duration` is set to one day. 3. Assuming the start price is 10e9 and the end price is 20e9. 3. The `number_of_intervals` used in `calculate_current_sell_amount()` will round down to two one day long intervals due to integer division (`216_000 // 86_400`). 4. Users transacting on day one will pay the start price plus one interval price (15e9). 5. Users transacting on day two will pay the start price plus two interval prices, this will be the intended `sell_token_end_amount` ratio (20e9). 6. However users transacting in the additional half day will be charged the start price plus 3 interval bumps, causing them to pay a greater amount (25e9) than the intended `sell_token_end_amount` ratio.
Open the reportThe Prop AMM tracks net sell volume across discrete time windows of length`epochDurationSeconds` to adjust dynamic wall liquidity and apply redemption haircuts. However, epoch boundaries are not aligned with absolute time (e.g., using `block.timestamp % epochDuration`). Instead, epochs are event-driven and updated lazily on trade execution by setting `state.epochStart = uint64(currentTime)`. Because the decay calculation evaluates time elapsed relative to this stateful `epochStart` timestamp, historical volume does not decay smoothly across real time during low-activity periods. This introduces two primary vulnerabilities: 1. **Frozen & Prolonged Decay:** If trading halts after a high-volume sell-off, the historical volume penalty remains frozen at high severity. When a trade finally occurs long after the epoch should have ended, the protocol treats the elapsed time as a fresh epoch rollover (`elapsed >= epochDuration`), applying the full historical penalty to the new trade and re-anchoring `epochStart` to the current block timestamp. This extends the penalty's decay window far beyond its intended duration. 2. **Incentivized Market Freeze:** If a user trades between `1.0 * epochDuration` and `1.99 * epochDuration` after a spike in sell volume, they absorb 100% of the historical penalty and restart the full decay clock for everyone else. However, if all users wait past the `2 * epochDuration` threshold, the contract evaluates `elapsed >= epochDuration * 2` and completely wipes `previousNet` to 0. This creates an unaligned incentive where rational users are forced to freeze trading until the `2.0 * epochDuration` threshold passes to avoid absorbing an unfair haircut.
Open the reportThe `MakeRedemptionOffer` instruction defines `redemption_offer` as a PDA. However, subsequent instructions that accept the `redemption_offer` account do not revalidate its PDA derivation, which may allow incorrect redemption offer accounts to be passed in. Similarly, the `redemption_request` account is defined as a PDA in the `CreateRedemptionRequest` instruction, but its PDA constraints are not validated in other instructions that interact with it.
Open the reportThe `RedemptionOffer` account holds the state related to a given redemption offer, including the current `requested_redemptions` amount. This value is added to when a user creates a redemption request; however, it is not subtracted from when their redemption request is fulfilled. The inline documentation above the `fulfill_redemption_request()` function suggests that this action should take place, but the necessary logic is missing in the current implementation. This causes inconsistent accounting, making it more difficult for the protocol team to correctly ensure they provide the necessary amount of tokens to fulfill all requests.
Open the reportThe `pub boss: Signer<'info>` account in `CloseOfferTwo` is not marked as mutable. The boss account is intended to receive rent refunds from closing the offer account and associated token accounts, requiring it to be mutable (`#[account(mut)]`). This missing constraint may cause the `close_offer_two()` instruction to fail.
Open the reportThe protocol uses an emergency killswitch that is described to prohibit minting and redeeming tokens while allowing token transfers, quotes, and other view functions. We highlight the following locations where the killswitch may depart from the specification: - `LibOnReVault._withdrawConfigurableVault()`: `isKilled` is checked before processing any withdrawal. This blocks fee, proceeds, liquidity, and buffer-reserve transfers to their configured destinations. - `LibOnReOffer._previewExecution()`: This function routes previews through `_requireExecutableOfferConfig`, which reverts when killed. This freezes all quotes. - `LibOnReBuffer.setBufferGrossApr()` and `LibOnReBuffer.setBufferFeeConfig()`: Buffer configuration changes can still result in a mint. Both functions settle accrued returns through `_accrue()` and a potential downstream call to `IManagedToken.mintBuffer()` without checking the killswitch. - `ManagedToken.mintBuffer()`: Direct token supply changes remain possible for authorized minters / burners.
Open the reportThe token contract exposes the `burnFrom` function to facilitate burns using allowances. However, since this function forces a call to `_spendAllowance()`, it will revert if the caller attempts to burn from their own balance (`account = msg.sender`). This may cause unexpected reverts for integrating protocols and thus composability issues.
Open the reportThe `swap_v2` implementation allows any `remaining_accounts_info` passed in the data to be included as supplementary tick arrays on top of the three default tick arrays provided. Requiring that the swap is executed within the three default tick arrays may mean some swaps that can be executed correctly may unexpectedly fail. The likelihood of this increases if the liquidity across ticks is shallow or if the size of the desired swap is large.
Open the reportThe architecture of the mmbot programs intends to limit the actions of the `executor` to making swaps between ONYC token and stablecoins (USDC and USDG). However, due to there being no validation on which tokens are swapped, a malicious `executor` would be able to trade the funds present in the protocol for any arbitrary tokens. This opens up a number of scenarios where theft of the protocol's funds is possible.
Open the reportWhen requesting a redemption within the protocol, a `RedemptionRequest` account is created on behalf of the user to track all state tied to their outstanding request. Once this request is either cancelled (by the user or by an admin) or fulfilled by the redemption admin, the `RedemptionRequest` account is no longer used but does not get closed. This means that every redemption request has a non-refundable rent overhead that users must pay even though their storage requirements are only temporary.
Open the reportThe approval verification logic reconstructs a signed approval message from the Ed25519 instruction (`signed_msg`) and separately accepts an `ApprovalMessage` argument (`msg`). While both messages are individually validated for `program_id`, `user_pubkey`, and `expiry_unix`, the implementation does not enforce that the signed message content exactly matches the message passed into the instruction. As a result, the cryptographic signature is not strictly bound to the instruction level message argument, relying instead on implicit assumptions about message construction. Since all fields are checked this additional check acts as a defence in depth.
Open the reportThe `add_approver()` function does not verify whether the proposed approver address is already assigned to an existing approver slot. As a result, if any approver slot is empty, the function may assign an address that is already set as the other approver, leading to duplicate approver assignments. Additionally, the `remove_approver()` function first checks whether `approver1` matches the address to be removed and returns immediately if it does, without checking `approver2`. If the same address was mistakenly assigned to both `approver1` and `approver2`, removing it would require calling the function twice.
Open the reportThe `calculate_fees()` function currently uses floor division when computing `token_fee_amount`. While the difference per transaction is small, repeated calls can cumulatively reduce the protocol’s collected fees.
Open the reportThe `verify_approval_message_generic()` function verifies an Ed25519 signature by extracting the previous instruction via its index and checking that the program ID matches the `ed25519_program`. However, the Ed25519 program is stateless, meaning the instruction should not include any accounts.
Open the reportFunctionality exists within the protocol to close an existing `Offer` account. In such a scenario, any `RedemptionOffer` that is tied to the closed offer will be unable to fulfill further redemption requests as the function will return an error when trying to read the now closed `Offer` state. However, the `create_redemption_request()` function will not cause an error if the `offer` stored in the `RedemptionOffer` instance does not exist, meaning redemption requests can be created that will not be fulfilled. The severity of this issue is downgraded because the `create_redemption_request()` action will require a signature from the `redemption_admin` who should handle validating that the underlying offer is still active. However, a more robust design would be helpful to remove this potential point of failure.
Open the reportThe `CreateRedemptionRequest` instruction includes an `expires_at` parameter that is validated against the current on chain timestamp to ensure the request has not expired. However, this expiration check is redundant because Solana transactions already include built-in replay protection via recent blockhashes. In this flow, both the `redeemer` and the `redemption_admin` sign the same transaction containing a recent blockhash. Solana only accepts transactions referencing blockhashes from the last ~150 blocks, which inherently limits how long a signed transaction can be executed. As a result, the explicit `expires_at` parameter does not provide additional security guarantees for redemption request creation.
Open the reportIn multiple places, accounts are held directly on the stack (e.g., `Account<'info, T>`). However, large or numerous account structs combined with deeper call stacks can exceed per-frame limits and cause runtime access violations, leading to transaction reverts. Practically, this manifests as stack overflow errors, producing instruction failures and degraded UX/temporary denial of service for affected flows. This directly affects reliability and user experience.
Open the reportThe `OfferTakenOne` and `OfferTakenTwo` events include a `remaining_sell_token_amount` field. This is calculated (`sell_token_end_amount - offer_sell_token_account.amount`) instead of the remaining buy token amounts. This value may be incorrect as the am.
Open the reportThe `instructions::initialize::initialize()` function does not display an error if `state.boss` is already set. Not returning an error makes it harder to detect if the state was already initialized.
Open the reportThe `instructions::set_boss::set_boss()` function does not validate the `new_boss` public key value before setting it, resulting in resetting the boss account identifier to the default public key if it is sent in the transaction. Setting the default value allows another user to reinitialize the program and gain admin-level permission. Note that this is similar to the lack of zero address checks in Solidity code bases.
Open the reportThe `instructions::take_offer::calculate_buy_amount()` function does not return an error if the calculated buy token amount is zero because of integer truncation (rounding). Not checking the return value may lead to unexpected behavior.
Open the reportThe reports name a repository or files. Open a report to see its scope.
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="nayms"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
Something 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