hats-finance / Circles-0x6ca9ca24d78af44582951825bef9eadcb210e5cf

Circles Protocol contracts
https://aboutcircles.com
GNU Affero General Public License v3.0
0 stars 0 forks source link

Precision Loss in Token Conversion and Time Calculations #36

Open hats-bug-reporter[bot] opened 1 week ago

hats-bug-reporter[bot] commented 1 week ago

Github username: -- Twitter username: -- Submission hash (on-chain): 0x88e236915ae04b6d86151a528ba86514642a7490c66b95d02deb2fc07fb5ba6f Severity: low

Description: Description

Two smart contracts in the Circles V2 system, the Migration.sol and Demurrage.sol, contain functions that perform calculations involving division operations. These operations can potentially lead to a loss of precision due to Solidity's integer division behavior.

  1. In Migration.sol:

    • The convertFromV1ToDemurrage function performs complex calculations to convert token amounts from V1 to V2.
  2. In Demurrage.sol:

    • The day function calculates the number of days since a reference point (inflationDayZero).

In both cases, Solidity's integer division behavior, which truncates any fractional part, can lead to a loss of precision. This is particularly problematic when dividing by large numbers or when the result of a division is very small.

Overview of potential Issues:

If these precision loss issues are not addressed, the issues that could arise are the following:

  1. In Migration.sol:

    • The convertFromV1ToDemurrage function could produce inaccurate conversion results.
    • Users might receive fewer V2 tokens than they should when migrating from V1.
    • In extreme cases, if precision loss causes the division result to become zero, users could lose their entire balance during migration.
    • There's also a risk of overflow in intermediate calculations, which could cause unexpected results or transaction reversions.
  2. In Demurrage.sol:

    • The day function's intentional rounding down to the nearest day could accumulate errors over time.
    • This could affect any time-sensitive calculations in the system, such as:
      • Demurrage calculations
      • Token issuance schedules

This may lead to loss of user funds, inconsistencies in token economics, or erosion of trust in the platform.

Attachments

  1. Proof of Concept (PoC) File
// File: Migration.sol
contract Migration is ICirclesErrors {
    // ... (other contract code)

    function convertFromV1ToDemurrage(uint256 _amount) public view returns (uint256) {
        // ... (earlier code omitted for brevity)

        uint256 rP =
            factorCurrentPeriod * (period - secondsIntoCurrentPeriod) + factorNextPeriod * secondsIntoCurrentPeriod;

        return (_amount * 3 * ACCURACY * period) / rP; // Potential precision loss
    }
}

// File: Demurrage.sol
contract Demurrage is ICirclesDemurrageErrors {
    // ... (other contract code)

    function day(uint256 _timestamp) public view returns (uint64) {
        return uint64((_timestamp - inflationDayZero) / DEMURRAGE_WINDOW); // Intentional precision loss
    }
}
  1. Revised Code File
// File: Migration.sol
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract Migration is ICirclesErrors {
    using SafeMath for uint256;

    // ... (other contract code)

    function convertFromV1ToDemurrage(uint256 _amount) public view returns (uint256) {
        // ... (earlier code remains the same)

        uint256 rP = factorCurrentPeriod.mul(period.sub(secondsIntoCurrentPeriod))
            .add(factorNextPeriod.mul(secondsIntoCurrentPeriod));

        // Increase precision and use SafeMath
        uint256 numerator = _amount.mul(3).mul(ACCURACY).mul(period).mul(1e18);
        return numerator.div(rP);
    }
}

// File: Demurrage.sol
contract Demurrage is ICirclesDemurrageErrors {
    // ... (other contract code)

    function day(uint256 _timestamp) public view returns (uint64) {
        uint256 result = (_timestamp - inflationDayZero) / DEMURRAGE_WINDOW;
        require(result <= type(uint64).max, "Result exceeds uint64 max value");
        return uint64(result);
    }
}

Comments on the revised code:

  1. For Migration.sol:

    • Imported and used OpenZeppelin's SafeMath library to prevent overflow and underflow issues in the convertFromV1ToDemurrage function.
    • Precision has been increased by multiplying the numerator by 1e18 before division, helping to preserve more decimal places in the result.
    • The order of operations has been adjusted to reduce the risk of intermediate overflow.
  2. For Demurrage.sol:

    • In the day function, while the precision loss is intentional, we've added a check to ensure the result doesn't exceed the maximum value of uint64, preventing potential overflow issues.

These modifications aim to mitigate precision loss and make the calculations more robust. However, it's crucial to thoroughly test these changes with a wide range of input values to ensure they meet the specific requirements of the Circles V2 system.

benjaminbollen commented 1 week ago

Thank you for your report on potential precision loss in token conversion and time calculations. After careful review, we've determined that this is not an issue.

Regarding the convertFromV1ToDemurrage function, we use an explicit ACCURACY parameter (10**8) to compensate for integer division, ensuring sufficient precision in our calculations.

As for time calculations, our definition of a 'day' is based on seconds and hours, which is the only precise way to track time in Solidity contracts. Calendar day tracking is not feasible within smart contract limitations, nor the intended design.

We appreciate your attention to precision in mathematical operations. However, it seems there might be some misunderstandings about Solidity's handling of overflows (which is managed by the compiler since Solidity v8) and the constraints of time tracking in smart contracts.

Your report demonstrates engagement with our system's mathematical operations, which we value. Thank you for your contribution to our ongoing security review process.