DeFiHardhat
35,000 USDC
View results
Submission Details
Severity: low
Invalid

Fee on transfer tokens are not considered in the `FertilizerFacet::claimFertilized`, `FertilizerFacet::payFertilizer` and `UnripeFacet::pick` functions

Summary

The internal balance can be not correct if we have fee-on-transfer tokens in FertilizerFacet::claimFertilized, FertilizerFacet::payFertilizer and UnripeFacet::pick functions.

Vulnerability Details

The FertilizerFacet::claimFertilized, FertilizerFacet::payFertilizer and UnripeFacet::pick functions call the LibTransfer::sendToken function and assume that the amount of token transferred after calling token.safeTransfer(recipient, amount); is the passed amount to the function.

FertilizerFacet:

function claimFertilized(uint256[] calldata ids, LibTransfer.To mode)
external
payable
{
uint256 amount = C.fertilizer().beanstalkUpdate(msg.sender, ids, s.bpf);
@> LibTransfer.sendToken(C.bean(), amount, msg.sender, mode);
}
function payFertilizer(address account, uint256 amount) external payable {
require(msg.sender == C.fertilizerAddress());
@> LibTransfer.sendToken(
C.bean(),
amount,
account,
LibTransfer.To.INTERNAL
);
}

UnripeFacet:

function pick(
address token,
uint256 amount,
bytes32[] memory proof,
LibTransfer.To mode
) external payable nonReentrant {
bytes32 root = s.u[token].merkleRoot;
require(root != bytes32(0), "UnripeClaim: invalid token");
require(!picked(msg.sender, token), "UnripeClaim: already picked");
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
require(MerkleProof.verify(proof, root, leaf), "UnripeClaim: invalid proof");
s.unripeClaimed[token][msg.sender] = true;
@> LibTransfer.sendToken(IERC20(token), amount, msg.sender, mode);
emit Pick(msg.sender, token, amount);
}

LibTransfer::sendToken:

function sendToken(
IERC20 token,
uint256 amount,
address recipient,
To mode
) internal {
if (amount == 0) return;
if (mode == To.INTERNAL)
LibBalance.increaseInternalBalance(recipient, token, amount);
else token.safeTransfer(recipient, amount);
}

Also, the LibTransfer::sendToken function updates the internal balance according to this amount.

Impact

If the token is a transfer-on-fee token or a deflationary/rebasing token the received/send amount will be less than the accounted amount in the state variables.

Tools Used

Manual Review

Recommendations

It is important to get the actual received amount of token. This can be done by calculating the difference of token balance before and after transfer as in the LibTransfer::reciveToken is done.

Updates

Lead Judging Commences

giovannidisiena Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

Fee-on-transfer/rebase tokens

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.