Pre-auditThree days of senior audit time for $1,500, credited in full toward your Zealynx audit.3 audit days, credited toward your auditSee how →
L-05·

The maker leg's measured receipt is not clamped, so a token that credits balances mid-transfer lets a fill settle below the taker's signed floor

Fixedrwarfqoracles
TL;DR

fillRFQOrder measures both legs by balance difference, but only one of them is clamped.

Severity
LOW
Method
MManual review
CAT.
02Section · Affected files

Affected files

src/octarine-rfq/OctarineRFQ.sol#L377-L381 src/octarine-rfq/OctarineRFQ.sol#L430-L470

03Section · Description

Description

fillRFQOrder measures both legs by balance difference, but only one of them is clamped. The taker leg is clamped to the amount sent (_deliverTakerLeg, L430-L443), and the comment explains why:

it is clamped to amount so a token that credits more than it was sent — a rebase landing mid-transfer — cannot inflate the maker's bill beyond the order's terms.

The maker leg is measured the same way but not clamped (_payMakerLegs, L466-L468):

solidity
uint256 before_ = makerToken.balanceOf(receiver);
makerToken.safeTransferFrom(payer, receiver, proceeds);
received = makerToken.balanceOf(receiver) - before_;

That received is what the taker's signed floor is checked against (L377-L381, _checkTakerFloor). If the maker token credits balances during the transfer, that is the case the taker-leg comment describes, then the credit on the taker's existing balance is counted as proceeds. A fill whose real proceeds are below the taker's floor then passes the check, and the taker's whole input is delivered.

The fill moment is chosen by the taker, or by anyone holding both signatures when sender is unset. So a fill can be timed to land on a scheduled rebase.

04Section · Impact

Impact

The taker sells below the floor it signed. The shortfall is bounded by the credit that lands on the taker's own balance of the maker token during the proceeds transfer. In the PoC, a taker holding 100,000 units and hit by a +0.01% rebase receives 960.30 against a 965 floor (0.49% short).

The precondition is a maker token whose balances change inside a transfer. None of the assets named for launch behaves that way: BENJI, Pareto, ULTRA, OUSG and USDC. The taker-leg comment shows the code already anticipates this token class on the other leg.

05Section · Recommendations

Recommendations

Clamp the maker leg the same way as the taker leg:

diff
@@ -466,6 +466,7 @@ contract OctarineRFQ is IOctarineRFQ, AccessControl, ReentrancyGuard, Pausable,
uint256 before_ = makerToken.balanceOf(receiver);
makerToken.safeTransferFrom(payer, receiver, proceeds);
received = makerToken.balanceOf(receiver) - before_;
+ if (received > proceeds) received = proceeds;
if (fee > 0) makerToken.safeTransferFrom(payer, feeRecipient, fee);
}

The full test suite passes with this change (706 passed, 1 skipped).

The clamp closes the case shown, but one token class still slips past the floor: a token that both charges a transfer fee and credits a rebase in the same transfer. The credit masks the fee, so the measured receipt never exceeds the proceeds and the clamp never engages. In our test (not included here), the taker received 987.03 against a 990 floor. No balance-delta check can separate the two, and the taker leg has the same limit. State that such tokens are unsupported on either leg.

06Section · Remediation

Remediation

Fixed in 6f10cab, as recommended. The PoC's fill now reverts ReceivedBelowMinimum(960300000, 965000000).

As the recommendation notes, a token that charges a transfer fee and credits a rebase in the same transfer still settles below the floor: 960.30 against 965 in our test. No balance-delta check can catch it. DESIGN.md still lists fee-on-transfer tokens as supported; it should say such tokens are unsupported on both legs.

A runnable Foundry proof of concept for this finding is in the corresponding issue on the project tracker.

07Section · Report provenance

Report provenance

Source: the final report dated 28 October 2026. Original finding identifiers are retained, including M-03 and M-04, which were regraded Low. Final dispositions and residual conditions are preserved in this write-up. Read the full report.

Status
Fixed
L-05