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

The No Deadline in MintFertilizer function and Deadline timestamp set to max in addLiquidity function , could be vulnerable to miner attacks

Summary

The FertilizerFacet:mintFertilizer calculate the Fertilizer token to mint and also calls the Will.addLiquidity() function to add new tokens to liquidity, However there is no deadline check in place to prevent transaction execution when it is not in favor of user and it also passes the type(uint256).max as deadline to addLiquidity function external call.

Vulnerability Details

The MintFertilizer function has two Issues:

  1. Whenever user calls mintFertilizer to mint New fertilizer token. The Protocol takes the minLPTokensOut as arguments and pass it to addFertilizer function. The addFertilizer function pass type(uint256).max as a deadline for transaction execution at Pool contract.

  2. There is no deadline checks on mintFertilizer transaction execution as the code tries to calculate the amount of fertilizer token to be minted for user using Oracle Price.
    Both of these vulnerabilities will lead to miner attack. In the 1st case the miner only need to check for price drop to a certain level where the minFertilizerOut amount could be satisfied. where in second 2nd case the miner need to wait for newLP minted will satisfy minLPTokensOut.

  3. Vulnerable Code for 1st case:

function addUnderlying(uint256 tokenAmountIn, uint256 usdAmount, uint256 minAmountOut) internal {
...
uint256 newLP = IWell(barnRaiseWell).addLiquidity( // call addLiquidityFeeOnTransfer
tokenAmountsIn,
minAmountOut,
address(this),
@> type(uint256).max // @audit : use some deadline for slipage Low
);
LibUnripe.incrementUnderlying(C.UNRIPE_BEAN, newDepositedBeans);
LibUnripe.incrementUnderlying(C.UNRIPE_LP, newLP);
// console.log(C.bean().balanceOf(address(this)));
s.recapitalized = s.recapitalized.add(usdAmount);
}
  1. Vulnerable Code for 2nd case:

function mintFertilizer(
uint256 tokenAmountIn,
uint256 minFertilizerOut,
uint256 minLPTokensOut
) external payable returns (uint256 fertilizerAmountOut) {
fertilizerAmountOut = _getMintFertilizerOut(tokenAmountIn, LibBarnRaise.getBarnRaiseToken());
// @audit : No DeadLine
require(fertilizerAmountOut >= minFertilizerOut, "Fertilizer: Not enough bought.");
require(fertilizerAmountOut > 0, "Fertilizer: None bought.");
uint128 remaining = uint128(LibFertilizer.remainingRecapitalization().div(1e6)); // remaining <= 77_000_000 so downcasting is safe.
require(fertilizerAmountOut <= remaining, "Fertilizer: Not enough remaining.");
uint128 id = LibFertilizer.addFertilizer(
uint128(s.season.current),
tokenAmountIn,
fertilizerAmountOut,
minLPTokensOut
);
C.fertilizer().beanstalkMint(msg.sender, uint256(id), (fertilizerAmountOut).toUint128(), s.bpf);
}

POC :

The user submit transaction to mintFertilizer with following arguments:

tokenAmountIn=10
minFertilizerOut=9
minLPTokensOut=9

At the time when user submit transaction the user could receive 9.5 Fertilizer tokens and add 9.5 as a LP token to Pool. as we know that there are no deadline so the miner can hold the transaction for a wait when it is possible to mint exact 9 Fertilizer token and add 9 tokens to Liquidity Pool, then miner execute the transaction.

Impact

The user minted Fertilizer token and New Liquidity will be tricked to forcefully mint min amount.

Tools Used

Manual Review

Recommendations

The Possible fix for the vulnerability could be as follows:
Add Following changes to FertilizerFacet.sol file:

--- a/protocol/contracts/beanstalk/barn/FertilizerFacet.sol
+++ b/protocol/contracts/beanstalk/barn/FertilizerFacet.sol
// add this modifier
+ modifier ensureDeadline(uint32 deadline) {
+ require(block.timestamp < deadline, "Deadline has passed");
+ _;
+ }
...
@@ -64,8 +69,9 @@ contract FertilizerFacet {
function mintFertilizer(
uint256 tokenAmountIn,
uint256 minFertilizerOut,
- uint256 minLPTokensOut
- ) external payable returns (uint256 fertilizerAmountOut) {
+ uint256 minLPTokensOut,
+ uint32 deadline
+ ) external ensureDeadline(deadline) payable returns (uint256 fertilizerAmountOut) {
fertilizerAmountOut = _getMintFertilizerOut(tokenAmountIn, LibBarnRaise.getBarnRaiseToken());
require(fertilizerAmountOut >= minFertilizerOut, "Fertilizer: Not enough bought.");
@@ -78,7 +84,8 @@ contract FertilizerFacet {
uint128(s.season.current),
tokenAmountIn,
fertilizerAmountOut,
- minLPTokensOut
+ minLPTokensOut,
+ deadline
);
C.fertilizer().beanstalkMint(msg.sender, uint256(id), (fertilizerAmountOut).toUint128(), s.bpf);

Add Following changes to LibFertilizer.sol file:

@@ -44,7 +45,8 @@ library LibFertilizer {
uint128 season,
uint256 tokenAmountIn,
uint256 fertilizerAmount,
- uint256 minLP
+ uint256 minLP,
+ uint32 deadline
) internal returns (uint128 id) {
AppStorage storage s = LibAppStorage.diamondStorage();
@@ -61,7 +63,7 @@ library LibFertilizer {
s.fertilizer[id] = s.fertilizer[id].add(fertilizerAmount128);
s.activeFertilizer = s.activeFertilizer.add(fertilizerAmount);
// Add underlying to Unripe Beans and Unripe LP
- addUnderlying(tokenAmountIn, fertilizerAmount.mul(DECIMALS), minLP);
+ addUnderlying(tokenAmountIn, fertilizerAmount.mul(DECIMALS), minLP, deadline);
// If not first time adding Fertilizer with this id, return
if (s.fertilizer[id] > fertilizerAmount128) return id;
// If first time, log end Beans Per Fertilizer and add to Season queue.
@@ -84,7 +86,7 @@ library LibFertilizer {
* @dev Any token contributions should already be transferred to the Barn Raise Well to allow for a gas efficient liquidity
* addition through the use of `sync`. See {FertilizerFacet.mintFertilizer} for an example.
*/
- function addUnderlying(uint256 tokenAmountIn, uint256 usdAmount, uint256 minAmountOut) internal {
+ function addUnderlying(uint256 tokenAmountIn, uint256 usdAmount, uint256 minAmountOut,uint32 deadline) internal {
...
@@ -136,16 +141,17 @@ library LibFertilizer {
(newDepositedLPBeans, tokenAmountIn) :
(tokenAmountIn, newDepositedLPBeans);
- uint256 newLP = IWell(barnRaiseWell).addLiquidity(
+ uint256 newLP = IWell(barnRaiseWell).addLiquidity( // call addLiquidityFeeOnTransfer
tokenAmountsIn,
minAmountOut,
address(this),
- type(uint256).max
+ deadline
);
Updates

Lead Judging Commences

giovannidisiena Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Fertilizer deadline

Support

FAQs

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