Assessment reports>Avon>Informational findings>Function ,_previewAccrueInterest, and function ,_accrueInterest, calculate the fee shares in different ways
Category: Coding Mistakes

Function _previewAccrueInterest and function _accrueInterest calculate the fee shares in different ways

Informational Impact
Informational Severity
N/A Likelihood

Description

The function _previewAccrueInterest is used to preview the result of accruing interest before calling a function of the contract AvonPool, while the function _accrueInterest is the one invoked by the contract AvonPool during actual execution. However, there are discrepancies between the function _previewAccrueInterest and function _accrueInterest in the calculation of managerFeeShares and protocolFeeShares.

In the function _accrueInterest, it's like treating managerFeeAmount and protocolFeeAmount as a whole, and calculating managerFeeShares and protocolFeeShares based on totalSupplyShares and assetsWithoutFees. While in the function _previewAccrueInterest, the calculations of managerFeeShares and protocolFeeShares are based on the different total amounts of assets.

function _previewAccrueInterest(PoolStorage.PoolState storage s) internal view returns (PoolData memory previewPool) {
    // [...]

    uint256 managerFeeAmount;
    uint256 protocolFeeAmount;

    if (s.managerFee != 0) {
        managerFeeAmount = accruedInterest.mulDiv(s.managerFee, PoolConstants.WAD);
        uint256 managerFeeShares = managerFeeAmount.mulDiv(s.totalSupplyShares, previewPool.totalSupplyAssets - managerFeeAmount);
        previewPool.totalSupplyShares += managerFeeShares;
    }

    protocolFeeAmount = accruedInterest.mulDiv(IPoolImplementation(address(this)).getProtocolFee(), PoolConstants.WAD);
    uint256 protocolFeeShares = protocolFeeAmount.mulDiv(s.totalSupplyShares, previewPool.totalSupplyAssets - protocolFeeAmount);
    previewPool.totalSupplyShares += protocolFeeShares;

    // [...]
}

function _accrueInterest(PoolStorage.PoolState storage s) internal returns (uint256 managerFeeShares, uint256 protocolFeeShares) {
    // [...]

    uint256 totalNewShares;
    uint256 managerFeeAmount = accruedInterest.mulDiv(managerFee, PoolConstants.WAD);
    uint256 protocolFeeAmount = accruedInterest.mulDiv(IPoolImplementation(address(this)).getProtocolFee(), PoolConstants.WAD);

    uint256 assetsWithoutFees = totalSupplyAssets - managerFeeAmount - protocolFeeAmount;

    // Calculate manager fee shares if applicable
    if (managerFee != 0 && accruedInterest > 0) {
        managerFeeShares = managerFeeAmount.mulDiv(totalSupplyShares, assetsWithoutFees);
        totalNewShares += managerFeeShares;
    }
    
    // Calculate protocol fee shares if there's interest
    if (accruedInterest > 0) {
        protocolFeeShares = protocolFeeAmount.mulDiv(totalSupplyShares, assetsWithoutFees);
        totalNewShares += protocolFeeShares;
    }
    
    // Only update if there are new shares
    if (totalNewShares > 0) {
        totalSupplyShares += totalNewShares;
    }
    
    // [...]
}

Impact

If the time elapsed is the same, this will cause the fee shares calculated by the function _previewAccrueInterest to be slightly smaller than that from the function _accrueInterest.

Recommendations

Consider keeping the calculation of managerFeeShares and protocolFeeShares consistent between function _previewAccrueInterest and function _accrueInterest.

Remediation

Zellic © 2025Back to top ↑