<!-- canonical: https://0xsimao.com/findings/yieldoor-activity-look-far-back -->

# `Strategy::checkPoolActivity()` does not look as far back as it should

Medium · Sherlock · CLMM · 24th February 2025

Finding M-5 of the Yieldoor competition.

- Protocol: https://audits.sherlock.xyz/contests/791
- Report: /reports/yieldoor
- Codebase: https://github.com/0xsimao/2025-02-yieldoor/tree/b5a0f779dce4236b02665606adb610099451a51a
- Source: https://github.com/sherlock-audit/2025-02-yieldoor-judging/issues/97

---

### Summary

`Strategy::checkPoolActivity()` is supposed to check tick delta until the tick more in the past than `lookAgo`. From the security doc:
> `checkPoolActivity` does intentionally check 1 extra observation before `lookAgo`

"1 extra observation" and "before" indicate that it is intended that **1 more observation** in the past is looked at, with a timestamp **before** `lookAgo`.

However, the opposite actually happens, due to the way observations are being calculated.

### Root Cause

In `Strategy:335`, it will [return](https://github.com/sherlock-audit/2025-02-yieldoor/blob/main/yieldoor/src/Strategy.sol#L335-L337) 1 observation too early.

### Internal Pre-conditions

None.

### External Pre-conditions

None.

### Attack Path

1. Protocol rebalances but the twap price validation is incomplete and may lead to losses when depositing in the pool at a bad price.

### Impact

Loss of funds that will be arbitraged.

### PoC

The way the observations work is:
1. The current tick of the pool is taken.
2. The tick cumulative of the most recent observation is taken.
3. The tick cumulative of the second most recent observation is taken.
4. These cumulative ticks are subtracted and divided by the delta timestamp, leading to the tick at the **most recent observation**.
5. The current tick of the pool is compared with the tick of the most recent observation in the first iteration. In the second, it's the most recent tick with the second most recent, so on and so forth.

However, when checking `lookAgo` vs `timestamp`, it is using the timestamp of the second most recent observation, but it evaluated the tick of the most recent observation. Thus, it will return 1 tick too early.

Note: recent and second most recent can be shifted in time to 3rd and 4th and so on.

### Mitigation

The timestamp check should be made on the `nextTimestamp`, before it is updated to `timestamp` in the loop.

---

Related findings:

- [`BaseTOFT.sol`: `retrieveFromStrategy` can be used to manipulate other user's positions due to absent approval check](https://0xsimao.com/findings/tapioca-dao-retrieve-manipulate-absent-approval): Tapioca DAO
- [Strategy percentages will differ over time as yield accrued differs in ReservePool](https://0xsimao.com/findings/glacier-percentages-differ-yield-differs): Glacier
- [In GReservePool, if a strategy is frozen reserve pool stops working](https://0xsimao.com/findings/glacier-reserve-frozen-stops-working): Glacier
- [Strategies in the ReservePool could be implemented as an array and Strategy packed](https://0xsimao.com/findings/glacier-strategies-implemented-array-packed): Glacier
