53011 sc critical uncleaned partial approval consumption in dex aggregator integration leads to permanent dos
Submitted on Aug 14th 2025 at 16:38:29 UTC by @vivekd for Attackathon | Plume Network
Report ID: #53011
Report Type: Smart Contract
Report severity: Critical
Target: https://github.com/immunefi-team/attackathon-plume-network-nucleus-boring-vault/blob/main/src/helper/DexAggregatorWrapperWithPredicateProxy.sol
Impacts:
Smart contract unable to operate due to lack of token funds
Description
Brief/Intro
The DexAggregatorWrapperWithPredicateProxy contract fails to clean up token approvals after DEX swaps, creating a critical vulnerability when aggregators consume less than the approved amount.
Since 1inch and OKX aggregators support partial fills as a documented feature, lingering approvals will cause safeApprove to revert on all subsequent transactions for affected tokens, permanently disabling the contract.
Vulnerability Details
The vulnerability exists in the approval pattern used in both _oneInchHelper and _okxHelper functions.
When the contract approves tokens for swapping, it does not verify that the full approved amount was consumed, nor does it reset approvals to zero after the swap.
Vulnerable Code in _oneInchHelper (lines 267-271):
// Approve aggregator for full amount
depositAsset.safeApprove(address(aggregator), depositAmount);
// Perform swap - ignores spentAmount return value!
(supportedAssetAmount,) = aggregator.swap(executor, desc, data);
// No cleanup of remaining approvalVulnerable Code in _okxHelper (lines 321-333):
// Approve OKX for full amount
depositAsset.safeApprove(okxApprover, fromTokenAmount);
// Execute swap
(bool success, bytes memory result) = address(okxRouter).call(okxCallData);
// No verification of actual consumption or cleanupThe 1inch AggregationRouterV6 explicitly supports partial fills through the _PARTIAL_FILL flag, returning a spentAmount that can be less than the approved amount. This is a documented feature for handling low liquidity scenarios.
When a swap consumes only 80% of the approved tokens (common during slippage optimization), 20% approval remains active.
The critical issue arises because SafeERC20.safeApprove() reverts when attempting to change a non-zero approval to another non-zero value:
// Next user attempting to swap the same token
depositAsset.safeApprove(address(aggregator), newAmount); // REVERTS!
// Error: "SafeERC20: approve from non-zero to non-zero allowance"Impact Details
Once triggered, the contract becomes permanently unusable for affected tokens.
All users attempting to swap the same token will have transactions revert.
No recovery mechanism exists — the contract lacks owner functions to reset approvals.
Can be triggered accidentally through normal usage or deliberately by an attacker.
Severity: Critical — leads to permanent DoS for affected tokens.
Proof of Concept
Was this helpful?