F-2026-0007·incorrect-guard-proxy

Incomplete rescueERC1155 guard allows extraction of active position tokens after full deleverage

Fixedvaultleveragedprediction-marketgithub.com/bloom-art/dripster-lend
TL;DR

rescueERC1155 uses tokenTotalBorrowedUsdcUnits[tokenId] > 0 as a proxy for active positions; after a force-unwind drains borrowed capital to zero while the position retains conditional tokens, globalAdmin can drain those tokens and brick all subsequent exits.

Severity
LOW
Impact
MEDIUM
Likelihood
LOW
Method
MManual review
CAT.
Complexity
MEDIUM
Exploitability
LOW
02Section · Description

Description

The rescueERC1155 function guards against rescuing active position tokens by checking tokenTotalBorrowedUsdcUnits[tokenId] > 0. This proxy variable tracks protocol-borrowed capital per tokenId, not whether active positions still hold tokens of that type.

solidity
function rescueERC1155(address token, uint256 tokenId, uint256 amountUnits)
external nonReentrant onlyGlobalAdmin
{
if (token == conditionalTokens && tokenTotalBorrowedUsdcUnits[tokenId] > 0) {
revert CannotRescueOperationalToken();
}
IERC1155(token).safeTransferFrom(address(this), globalAdmin, tokenId, amountUnits, "");
}

After a position is fully deleveraged via force unwind (all borrowed capital repaid, borrowedUsdcUnits reduced to 0), tokenTotalBorrowedUsdcUnits[tokenId] reaches zero, even though the position still holds ERC1155 tokens in positionTokenUnits and remains in Opened state. A rescueERC1155 call for that tokenId succeeds, draining the vault's token balance. All subsequent exit paths (closePosition, settlePosition, liquidatePosition) revert permanently because the vault cannot transfer tokens it no longer holds. The user's accumulated pendingRefundUsdcUnits from prior unwind cycles are also permanently locked.

03Section · Recommendation

Recommendation

Track a separate tokenTotalPositionUnits counter that is incremented at finalizeOpen and decremented only at terminal finalization, and use it as the rescue guard:

solidity
if (token == conditionalTokens && tokenTotalPositionUnits[tokenId] > 0) {
revert CannotRescueOperationalToken();
}
04Section · Resolution

Resolution

Fixed. New tokenTotalPositionUnits counter replaces the borrowed-capital proxy in the rescue guard. Closes the related Krait pre-audit observation on the same surface.

Status
Fixed
F-2026-0007

oog
zealynx

Smart Contract Security Digest

Monthly exploit breakdowns, audit checklists, and DeFi security research — straight to your inbox

© 2026 Zealynx