As of 30 Oct 2026, Tapir has 1 audit report on Trustblock, by Quantstamp, dated 6 Jan 2026. That report lists 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 |
|---|---|---|---|---|---|
| tapir protocol | Security firm: Quantstamp | Date: | Scope: Not recorded (see 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 | 3 |
| Medium | 0 | 0 | 3 |
| Low | 0 | 0 | 3 |
The logic of the `sweepFeesToTreasury()` function calculates how many of the `ASSET` tokens held in the contract can be transferred to the treasury as earned fees. However, due to an error in how this amount is calculated, `feeAmount` will be half of the total `ASSET` tokens in the contract. This is because `dpSupply` and `ybSupply` are expected to be 50% of the contract's total `ASSET` balance. As this function is permissionless and can be called at state in the protocols lifecycle, there is a significant risk of griefing where funds can be repeatedly forced to the treasury, leaving the protocol under-collateralized.
Open the reportIn the `resolvePtrw()` function, if the attempt to redeem PT tokens from Pendle fails (no tokens are redeemed), a manual price resolve is required. However, in the current implementation, the `resolvePtrw()` function immediately reverts after setting `forceManualResolve = true`, rewinding all state transitions made during the function call, including the setting of the `forceManualResolve` boolean. This means that in scenarios where a manual resolution is necessary, if the `pendlePT` balance of the `TapirPtrwOracle` contract is non-zero it will not be possible to execute a manual resolution, leaving the oracle contract in a deadlocked state where the oracle contract cannot write price data to the `DepegPool` contract.
Open the reportThe `DepegPool` contract has a `MIN_PRICE` that the underlying asset must be when the `TapirOracle` provides the final price data via the `updatePriceData()` function. However, if there is a significant depeg event there is no guarantee that the price of the underlying asset will be greater than `MIN_PRICE`. In this scenario, the `DepegPool` contract will not be able to progress into the `REDEMPTION` stage, preventing users from withdrawing their funds.
Open the reportIn `TapirOracle.checkpoint()`, checks are made to ensure the price provided by an oracle has been updated within a specific `maxPriceStaleness`. However, different price feeds will have different update tolerances (heartbeats), where if the price has not moved outside of the accepted tolerance, the oracle will not be updated unnecessarily until the heartbeat time. This could lead to the `checkpoint()` function mistakenly considering a price stale while it is still actually valid. Alternatively, if the `maxPriceStaleness` was too large for a specific price source, a stale price might be considered valid by the Tapir oracle.
Open the reportThe `minPriceAge` parameter is mutable via `setMinPriceAge()` without state guards. The `resolvePriceDepeg()` function requires `block.timestamp - finalPriceData.timestamp >= minPriceAge` before allowing resolution. When an admin increases `minPriceAge` (up to MAX_DURATION = 30 days) while the pool is in RESOLUTION state, the function reverts with `PriceDataTooRecent`, indefinitely blocking progression to REDEMPTIONS and effectively locking user funds. Unlike HIGH-01 (backwards transition), this freezes the state machine in RESOLUTION.
Open the reportWhen the `ASSET` token of a `DepegPool` instance is a yield bearing asset users are likely to face a `successFee` upon redemption which takes a percentage fee of the users redeemable amount based on the price increase of the underlying asset from the `startingPrice` when the pool was deployed. For users who deposit towards the end of the pools `activeDuration` they will still be forced to pay the full `successFee` even though the majority of the value accrual occurred before the user's assets were added to the pool. This potential risk is not clearly outlined to the user who may end up with less `ASSET` tokens than they originally deposited due to the `successFee`.
Open the reportPools used as price oracles should document the dual-fee system: ```solidity uint24 public immutable override fee; // Pool identifier uint24 public override dynamicFee; // Actual swap fee ``` For Oracle Integrators: - Use `fee()` for pool identification and tick spacing - Use `dynamicFee()` for actual swap cost calculations - TWAP calculations are unaffected (based on price, not fees)
Open the reportThe protocol expected a strict linear progression from the state machine as follows: 1 (`STATE_ACTIVE`) → 2 (`STATE_COOLDOWN`) → 3 (`STATE_RESOLUTION`) → 4 (`STATE_REDEMPTIONS`). However, because the protocol's admin is able to change the `cooldownDuration` at any time, the project could theoretically move from a later state back to `STATE_COOLDOWN`. Due to how the current state is assessed in the `getState()` function, it is possible to end up with a transition from `STATE_RESOLUTION` → `STATE_COOLDOWN `(3 → 2) and from `STATE_REDEMPTIONS` → `STATE_COOLDOWN` (4 → 2).
Open the reportThe `DepegPool` contract packs its storage variables for a more efficient storage layout. However, it also unnecessarily packs variables marked as `constant` and `immutable`. This is unnecessary as both constant and immutable variables in solidity are compiled directly into the contract's bytecode rather than being held in the contract's storage onchain.
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="tapir"></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