51887 sc medium safeapprove will cause revert of usdt and similar erc20 token
Submitted on Aug 6th 2025 at 13:16:42 UTC by @SAAJ for Attackathon | Plume Network
Report ID: #51887
Report Type: Smart Contract
Report severity: Medium
Target: https://github.com/immunefi-team/attackathon-plume-network-nucleus-boring-vault/blob/main/src/base/Roles/TellerWithMultiAssetSupportPredicateProxy.sol
Impacts:
Smart contract unable to operate due to lack of token funds
Description
Vulnerability Details
The TellerWithMultiAssetSupportPredicateProxy contract calls a solmate-based safeApprove in its deposit and depositAndBridge methods:
depositAsset.safeApprove(address(vault), depositAmount);depositAsset is the ERC20 token that will be approved to the vault.
USDT (and similar non-EIP-20-compliant tokens) enforce that changing a non-zero allowance to another non-zero allowance requires first setting it to zero. The USDT code causing this behavior:
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender, 0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));Because the contract calls safeApprove without ensuring the existing allowance is zero, a subsequent safeApprove for USDT (or similar tokens) will revert. This affects repeated calls to deposit and depositAndBridge by the same user/token combination, causing those calls to fail.
Affected locations (code references)
https://github.com/immunefi-team/attackathon-plume-network-nucleus-boring-vault/blob/0ee676b5715075c26db6706960fd49ab59b587fc/src/base/Roles/TellerWithMultiAssetSupportPredicateProxy.sol#L89
https://github.com/immunefi-team/attackathon-plume-network-nucleus-boring-vault/blob/0ee676b5715075c26db6706960fd49ab59b587fc/src/base/Roles/TellerWithMultiAssetSupportPredicateProxy.sol#L146
Recommendation
Properly handle approvals by checking whether an existing non-zero allowance exists; if so, first set it to zero before setting it to the new non-zero allowance. Apply this check to both deposit and depositAndBridge.
Suggested patch (illustrative):
Apply equivalent logic to both deposit and depositAndBridge methods so repeated deposits with USDT-like tokens don't revert.
Proof of Concept
The following test reproduces the revert when calling safeApprove for USDT more than once. The snippet demonstrates a first successful approve and a second approve that reverts:
Was this helpful?