Smooth Sapphire Barbel
Medium
DebitaIncentives::incentivizePair
Unsafe Use of ERC20.transferFrom
Could Artificially Inflate Balance
The incentivizePair
function in the DebitaIncentives
contract allows users to bribe a principal-collateral pair in order to incentivize liquidity. However, a vulnerability exists in the current implementation. Specifically, when tokens are transferred to the DebitaIncentives
contract, the contract does not enforce a whitelist on the tokens, meaning any ERC20 token could be used. Additionally, the contract calls transferFrom
without checking the return value. This creates a potential attack vector, as a malicious user could use a token that returns false
on failed transfers to incentivize a pair, causing the contract state to be incorrectly updated, even though the tokens were never actually transferred. Importantly, the only token checked against the whitelist is the principal token, leaving the incentive token unchecked. This issue could lead to the loss of funds.
function incentivizePair(
address[] memory principles,
address[] memory incentiveToken,
bool[] memory lendIncentivize,
uint[] memory amounts,
uint[] memory epochs
) public {
require(
principles.length == incentiveToken.length &&
incentiveToken.length == lendIncentivize.length &&
lendIncentivize.length == amounts.length &&
amounts.length == epochs.length,
"Invalid input"
);
for (uint i; i < principles.length; i++) {
uint epoch = epochs[i];
address principle = principles[i];
address incentivizeToken = incentiveToken[i];
uint amount = amounts[i];
require(epoch > currentEpoch(), "Epoch already started");
@> require(isPrincipleWhitelisted[principle], "Not whitelisted");
// @> Only the principle is validated against the whitelist.
...
// transfer the tokens
@> IERC20(incentivizeToken).transferFrom(
msg.sender,
address(this),
amount
);
// @> In case the transferFrom returns false, the tx should revert
// add the amount to the total amount of incentives
if (lendIncentivize[i]) {
@> lentIncentivesPerTokenPerEpoch[principle][
hashVariables(incentivizeToken, epoch)
] += amount;
} else {
@> borrowedIncentivesPerTokenPerEpoch[principle][
hashVariables(incentivizeToken, epoch)
] += amount;
}
...
}
}
In DebitaIncentives::incentivizePair, the return value of ERC20.transferFrom
is not checked. Additionally, the bribe token is not validated against the whitelist, allowing an attacker to use a token that does not revert on a failed transfer, but instead returns false
.
No response
No response
- The attacker calls the
incentivizePair
function, passing anincentiveToken
parameter (an array with a single element) that points to a token which does not revert on failed transfers. - The attacker deliberately fails to provide the necessary token allowance to the
DebitaIncentives
contract, causing thetransferFrom
function to fail and returnfalse
instead of reverting. - As a result, the contract incorrectly updates its state, despite no actual transfer of tokens taking place.
- The contract's balance may be artificially inflated, as no tokens are actually transferred to the protocol.
No response
Implement a whitelist for bribe tokens and use safeTransferFrom
instead of transferFrom
to ensure proper token transfer handling and security.