Onre - Re-Audit - 2

QuantstampCode version: not recorded (see the report)

Audit · firm’s term: re-audit

Quantstamp reviewed OnRe (Onre - Re-Audit - 2) on 23 Jan 2026: 15 findings, 0 critical; 10 marked fixed, 5 open.

No open critical findings
Open the reportOpen OnRe’s security record

Report hosted on certificate.quantstamp.com.

Trustblock does not review code.

Findings15

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High001
Medium102
Low407
HighMissing Association Checks Between Redemption Offer and Redemption Request Enables State CorruptionFixed

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.

MediumConsider Enforcing Pda Constraint ValidationFixed

The `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.

MediumThe `RedemptionOffer.requested_redemptions` Field Not Updated upon a Successful Redemption Request FulfillmentFixed

The `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.

Low`RedemptionRequest` Accounts Should Be Closed Once They Are No Longer NeededFixed

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

LowApproval Message Not Strictly Bound to Signed PayloadFixed

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

LowEnsure Distinct Accounts for Approver RolesFixed

The `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.

LowFloor Division During Fee CalculationsFixed

The `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.

LowPerform a Statelessness Check for the **Ed25519** ProgramFixed

The `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.

LowRedemption Requests Can Still Be Made for `RedemptionOffer` Instances Where the Underlying `Offer` Has Been ClosedFixed

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

LowRedundant `expires_at` Parameter in `CreateRedemptionRequest` Due to Solana Replay ProtectionFixed

The `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.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Part of OnRe’s security record

OnRe
Audits6 audits by 1 firmLast audit Sep 2026, by Quantstamp.
Critical findingsNo open critical findingsAcross 6 reports.
Open OnRe’s security record

Security firm

Quantstamp

236 projects reviewed · 332 reports · latest Oct 2026

See Quantstamp’s audits

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