Name: Precision Issues - Divide before multiply

Description: The contracts demonstrate a common issue when performing division operations in Solidity, as Solidity doesn't support floating-point numbers. The order of operations can affect the result due to integer truncation.

In the Miscalculation contract, the function price performs the division before the multiplication (price / 100) * discount. Due to the fact that Solidity truncates integers when dividing, the result of price / 100 will be 0 if the price is less than 100. This causes the result of the multiplication to be 0 as well.

On the other hand, in the Calculation contract, the function price performs the multiplication before the division (price * discount) / 100. This way, the result will be correct as the multiplication doesn't get truncated, only the final result does.

Mitigation:

Always perform multiplication before division to avoid losing precision.

REF:

https://twitter.com/1nf0s3cpt/status/1599774264437395461

https://blog.solidityscan.com/precision-loss-in-arithmetic-operations-8729aea20be9

Miscalculation Contract:

contract Miscalculation {
    function price(
        uint256 price,
        uint256 discount
    ) public pure returns (uint256) {
        return (price / 100) * discount; // wrong calculation
    }
}

How to Test:

forge test --contracts src/test/Divmultiply.sol-vvvv

// This function is public, which means it can be called by anyone or any other contract.
function testMiscalculation() public {

    // Initialize a new instance of the MiscalculationContract.
    MiscalculationContract = new Miscalculation();
    // Log a string message to the console to indicate the test on MiscalculationContract is starting.
    console.log("Perform Miscalculation Contract");
    // Log the scenario details.
    console.log("Scenario: DeFi store 10% off now, Then we buy 1 item price: $80.");
    // Call the price method on the MiscalculationContract with two arguments, 80 and 90, and log the return value.
    console.log("Subtract the discount, get the sale price:", MiscalculationContract.price(80, 90));
    // Log the explanation of the wrong calculation.
    console.log("Solidity doesn't do decimals, so dividing before multiplying will round to zero. 0.8*90=0");
    // Log a line to separate the two different scenarios.
    console.log("---------------------------------------------------------");
    // Initialize a new instance of the CalculationContract.
    CalculationContract = new Calculation();
    // Log a string message to the console to indicate the test on CalculationContract is starting.
    console.log("Perform Correct calculation Contract");
    // Log the scenario details.
    console.log("Scenario: DeFi store 10% off now, Then we buy 1 item price: $80.");
    // Call the price method on the CalculationContract with two arguments, 80 and 90, and log the return value.
    console.log("Subtract the discount, get the sale price:", CalculationContract.price(80, 90));
    // Log the explanation of the correct calculation.
    console.log("Multiply before dividing is correct. 80*90/100=72");
}

Red box: miscalculated price.

Untitled