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 |
Every listed asset is treated as worth exactly one dollar by the protocol, regardless of its market price.`PricingDenomination` has one member, `Usd` (`OnReTypes.sol:L4-L6`), every pricer ID resolves through `_usdPricerId(managedToken)` (`OnReIds.sol:L21-L27`), and both conversion functions, `_calculateTokenOutAmount` and `_calculateRedemptionAssetOutAmount` (`OnReMath.sol:L30-L59`), take only the NAV and two decimal exponents. With one listed asset, this does not create a cross-asset arbitrage opportunity, since the asset itself is the unit of account and the NAV scalar cancels across both legs. Once a second asset is listed, the two trade at par inside the protocol even when their market prices differ, so a depeg can become a standing arbitrage opportunity against the redemption reserve. The protocol never observes an external price through an oracle.
Open the reportThe `_accrue()` function in `LibOnReBuffer.sol` calculates fee minting based on the time elapsed since the last accrual (`secondsElapsed = timestamp - state.lastAccrualTimestamp`). It fetches a single `currentApr` from the currently active pricing vector via `_currentPricing()` and applies it across this entire time window to calculate the `aprDelta`. While `_setBufferGrossApr()` and `_setBufferFeeConfig()` call `_accrue()` before writing changes, pricing-vector updates do not settle outstanding buffer accrual. Instead, pricing vectors in `LibOnRePricer.sol` advance passively. The `_activePricingVectorIndex()` function simply iterates backwards until it finds a vector where `startTime <= block.timestamp`. Because vectors activate silently based on absolute time, there is no automatic state hook that forces an accrual at the exact moment a new vector begins. If multiple days pass without a user action triggering `_accrue()`, a pricing vector boundary may be crossed silently. When the accrual finally triggers, the contract fetches only the newly active vector's APR and applies it retroactively to the entire `secondsElapsed` window. This effectively overwrites historical time periods with the wrong APR, leading to severe under-minting or over-minting of protocol fees.
Open the report`removeApprover` is the only revocation primitive for the EIP-712 gate. It is global, in that removing a key invalidates every outstanding attestation that key produced, for every user. However, it is not durable against later adding the same address as an approver. Combined with an `expiry` that has no upper bound (`type(uint64).max` is accepted and the signature can still be executed years later), an approval is effectively a standing authorization over the user's entire future interaction with the protocol. There is no revocation for a single approval and no `ApprovalUsed` event, so neither the operator nor the user can tell which attestations are outstanding.
Open the reportThe Prop AMM leverages a cadence wave mechanic (`_cadenceWaveTargetHaircutScaled`) to dynamically penalize rapid, repetitive sell-offs within an active epoch. This penalty scales linearly with `state.currentSellTradeCount` until it hits `state.config.cadenceThreshold`, at which point it enforces the maximum configured cadence haircut (`cadenceWaveScaled`) on all subsequent quotes. However, `LibOnReOffer._previewExecution` only validates that the input amounts are strictly greater than zero, allowing for swaps of 1 wei. Furthermore, `LibOnRePropAmm._recordSell` increments `currentSellTradeCount` by 1 unconditionally for every sell trade, regardless of the token volume. This allows an attacker to exploit the system by spamming negligible "dust" trades. By artificially inflating the trade count at near-zero capital cost, the attacker can force the cadence wave to max out, aggressively penalizing innocent users who subsequently attempt to sell during that epoch.
Open the reportThe `_previewEffectiveSellVolume()` function calculates the `currentNet` selling pressure, after its neutralized by any existing buying pressure: ```js uint256 currentNet = state.currentSellValueStable > state.currentBuyValueStable ? state.currentSellValueStable - state.currentBuyValueStable : 0; ``` The function ends by adding `currentSellValueStable` to the net pressure so that the quote can price its own impact: ```js return decayedPrevious + effectiveCurrentNet + currentSellValueStable; ``` However, adding `currentSellValueStable` at the end discards potential buying pressure relief, as the `state.currentBuyValueStable` can be discarded rather than decrementing the `currentSellValueStable`.
Open the report`ManagedToken` notifies the buffer controller unconditionally on every supply change, and that notification resolves the current pricing, which requires an executable pricer. As a result `setPricerEnabled(false)`, `setManagedTokenEnabled(false)` and `deleteAllPricingVectors` each make `mint`, `burn` and `burnFrom` revert for every holder and every minter, not merely for the route the flag names. The documentation presents the first two as switches at the level of a route and of a token but their actual blast radius is the whole token. Because `ManagedToken` is a Chainlink `IBurnMintERC20`, this reaches cross chain traffic. A CCIP transfer already burned on the source chain cannot be minted on the destination while the flag is set, so value in flight is stranded for the duration rather than merely delayed at the source. The inversion is the notable part: `setKillSwitch(true)`, the switch that exists to freeze the system, leaves `mint` working, while three flags that read as narrow limits on scope stop the token entirely.
Open the reportThe `_calculateFeeSplit()` function divides the minted protocol shares (`bufferMintAmount`) into up to three allocations: `managementAmount`, `performanceAmount`, and `reserveAmount`. The performance fee is only triggered if the high-water mark policy is disabled or when the `currentNav` equals or exceeds the `state.performanceFeeHighWatermark`. However, when this condition evaluates to true, the fee applies the `state.performanceFeeBasisPoints` to the entirety of the `afterManagement` shares, which represents the total yield generated in that period. It does not isolate the excess profits above the watermark. Traditionally, a performance fee is a cut of the returns that strictly exceed a previous peak. In this implementation, the high-water mark acts strictly as a boolean on/off switch.
Open the reportOn the upgrade path, it fires only when a proxy deployed against the `OnReToken` implementation that predates the rename (`2120ce6`) is carried across the upgrade rather than deployed fresh. `OnReToken` hard coded `decimals() pure returns (9)` and ended its layout with `address private _bufferController` at slot 5. `ManagedToken` appends `uint8 private _decimals`, written only from `InitializeParams` (`ManagedToken.sol:28,43,65`). Appending is normally the safe upgrade move, but a `uint8` gets no fresh slot, solidity packs it beside the 20 byte address already there. The old implementation only ever wrote bytes 0 to 19 of that word, so byte 20 reads zero and an in place `upgradeToAndCall` leaves `decimals() == 0`.
Open the reportThe initialize instruction accepts `onre_program_id` as a caller-supplied parameter. This value is used to validate the `onre_state` account via owner and seeds constraints, and is then stored permanently in `ProgramState`. All subsequent boss and admin authorization checks (`withdraw`, `set_executor`, `set_kill_switch`) derive the `onre_state` PDA from this stored value. However, because `onre_program_id` is a free parameter at initialization, an attacker who observes the program deployment can front-run the legitimate initialization by deploying their own fake program with a state PDA containing the attacker's pubkey as boss. The attacker calls initialize with their fake `onre_program_id`, signs as the boss that matches their fake state, and all Anchor validations pass. The `ProgramState` is then permanently linked to the attacker's program. Since init can only be called once, the legitimate team is locked out. This could lead to full program takeover if initialization is not performed atomically with deployment. The attacker would control the boss role, could set the executor to their own key, disable the kill switch, and withdraw any funds deposited after the hijack. The legitimate boss of the real Onre program would have no authority over the mmbot program.
Open the reportThe `initialize()` function can be frontrunned after the program is deployed. An attacker might create a state and set themselves as a `boss`, preventing the real admin from controlling the program.
Open the reportThe `take_offer::take_offer_*` instruction enables users to purchase `buy_tokens` by providing a `sell_token_amount`. The exchange rate is determined by the offer's `current_interval` at execution time. However, users who submit transactions near interval boundaries face a risk: if the interval changes before transaction execution, users may receive fewer `buy_tokens` than expected due to the change in price, with no mechanism to specify acceptable limits.
Open the reportWhen calculating how many tokens the user should receive in the `calculate_buy_amount()` function, there is a potential for some small amount of precision to be lost when the sell token has more decimals than the buy token. This is important because the team has outlined their intent to use `USDC` (six decimals) in the protocol, while the `ONe` and `rONe` tokens both have nine decimals. There are two scenarios to be aware of that could cause problems: 1. The user requests to redeem an amount of `rONE` tokens with seven or more decimals. 2. The `USDC`:`ONe` ratio when calling the `take_offer_two()` function causes a normal redemption amount to lose some precision when redeeming a partial amount in `USDC`.
Open the reportThe protocol uses `offer_id` values as critical seed values for deriving Program Derived Addresses (PDAs) for both offer accounts and offer authorities. While the program specification implicitly assumes offer IDs are unique, there is no on-chain mechanism to enforce this uniqueness. Offer IDs can be reused after an offer is closed, which could lead to confusion in the event logs and also issues where previously opened (and then closed) PDA accounts are reopened again. An additional issue here would be if the `boss` account becomes compromised, the new malicious boss could create an offer specifying a particular token exchange rate and then front run a users attempt to fill that order to create a new order with the same `offer_id` now offering a significantly worse exchange rate to the user.
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