Skip to content

Amateur Banana Fish - Incorrect Protocol Activity-Time Calculation in Distribution Module Due to Unaccounted Pauses #141

@sherlock-admin4

Description

@sherlock-admin4

Amateur Banana Fish

Medium

Incorrect Protocol Activity-Time Calculation in Distribution Module Due to Unaccounted Pauses

### Summary:
The distribution module incorrectly computes timePassed by simply subtracting lastOnChainDistributionTimestamp from block.timestamp. This calculation does not account for pauses in the contract, leading to incorrect distribution values upon resumption.

### Finding Description:
The _calculateGamma function calculates time elapsed (timePassed) since the last distribution:

uint256 timePassed = block.timestamp - $.lastOnChainDistributionTimestamp;

However, the DistributionModule supports pausing and resuming, the time the contract spends in a paused state should not count towards timePassed. Since the function does not account for pauses, the distribution formula treats the entire elapsed time as active distribution time, which can cause incorrect calculations.
The integrity of the distribution mechanism is then compromised.

Affected Code: here.

### Impact Explanation:
This issue directly affects financial calculations, i suppose the impact is Medium.

### Proof of Concept:

  1. Assume the contract is paused for 90 days:
// Last on-chain distribution was 6 months ago
$.lastOnChainDistributionTimestamp = block.timestamp - 180 days;

// Contract is paused for 90 days
pause();
wait(90 days);
unpause();

// timePassed should be 90 days, but incorrectly calculates as 180 days
uint256 timePassed = block.timestamp - $.lastOnChainDistributionTimestamp;

Expected: timePassed = 90 days
Actual: timePassed = 180 days → Incorrect gamma scaling

### Recommendation
Fix: Track Unpaused Time Instead of Using block.timestamp Directly
Modify timePassed to exclude paused durations:

// New variable to track cumulative unpaused time
uint256 unpausedTime;

function _updateUnpausedTime() internal {
    if (!paused) {
        unpausedTime += block.timestamp - $.lastUpdatedTimestamp;
    }
    $.lastUpdatedTimestamp = block.timestamp;
}

function _calculateGamma(DistributionModuleStorageV0 storage $)
    internal
    view
    returns (uint256)
{
    uint256 timePassed = unpausedTime - $.lastOnChainDistributionTimestamp;
    require(timePassed >= 0, "Invalid timestamp: future time");

    if (timePassed == 0 || timePassed <= DISTRIBUTION_FREQUENCY_SCALAR) {
        return Math.mulDiv($.baseGamma, SCALAR_ONE, BPS_SCALAR, Math.Rounding.Floor);
    }

    uint256 denominator = Math.mulDiv(SCALAR_ONE, timePassed, DISTRIBUTION_FREQUENCY_SCALAR, Math.Rounding.Floor);
    uint256 numerator = Math.mulDiv($.baseGamma, SCALAR_ONE, BPS_SCALAR, Math.Rounding.Floor);
    return Math.mulDiv(numerator, SCALAR_ONE, denominator, Math.Rounding.Floor);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions