The issue arises when the distribution loop ends prematurely due to the absence of available fertilizers. In this scenario, the final distribution of Beans to the remaining fertilizers occurs outside the loop, where there is no validation to ensure that all owed Beans are indeed distributed. This oversight poses a risk of misallocation of Beans, which could result in inconsistencies between the distributed and owed amounts.
To address this issue, it's recommended to implement a final validation step outside the loop to ensure that all owed Beans are distributed accurately. This can be achieved by placing a require
statement after the loop to validate that the total amount distributed equals the total amount owed across all fertilizers.
/**
* @dev Distributes Beans to Fertilizer.
*/
function rewardToFertilizer(uint256 amount) internal returns (uint256 newFertilized) {
// 1/3 of new Beans being minted
uint256 maxNewFertilized = amount.div(FERTILIZER_DENOMINATOR);
// Get the new Beans per Fertilizer and the total new Beans per Fertilizer
uint256 newBpf = maxNewFertilized.div(s.activeFertilizer); // @audit can s.activeFertilizer be zerO?
//Store the current Beans per Fertilizer (s.bpf) in oldTotalBpf and
uint256 oldTotalBpf = s.bpf;
//calculate the new total Beans per Fertilizer by adding newBpf to oldTotalBpf, storing the result in newTotalBpf.
uint256 newTotalBpf = oldTotalBpf.add(newBpf);
// Get the end Beans per Fertilizer of the first Fertilizer to run out.
uint256 firstEndBpf = s.fFirst; //3
//Loop to Distribute Beans
//While the new total Beans per Fertilizer (newTotalBpf) is greater than or equal to the Beans per Fertilizer
//of the first Fertilizer to run out (firstEndBpf), execute the loop.
//If the next fertilizer is going to run out, then step BPF according
while (newTotalBpf >= firstEndBpf) { // 3
// Calculate BPF and new Fertilized when the next Fertilizer ID ends
newBpf = firstEndBpf.sub(oldTotalBpf);
newFertilized = newFertilized.add(newBpf.mul(s.activeFertilizer));
// If there is no more fertilizer, end
if (!LibFertilizer.pop()) {
s.bpf = uint128(firstEndBpf); // SafeCast unnecessary here.
s.fertilizedIndex = s.fertilizedIndex.add(newFertilized);
require(s.fertilizedIndex == s.unfertilizedIndex, "Paid != owed");
return newFertilized;
}
// Calculate new Beans per Fertilizer values
newBpf = maxNewFertilized.sub(newFertilized).div(s.activeFertilizer);
oldTotalBpf = firstEndBpf;
newTotalBpf = oldTotalBpf.add(newBpf); //4
firstEndBpf = s.fFirst; // 3
}
// Distribute the rest of the Fertilized Beans
s.bpf = uint128(newTotalBpf); // SafeCast unnecessary here.
newFertilized = newFertilized.add(newBpf.mul(s.activeFertilizer));
s.fertilizedIndex = s.fertilizedIndex.add(newFertilized);
+ // Recommended final validation step
+ require(s.fertilizedIndex == s.unfertilizedIndex, "Paid != owed");
}