Assessment reports>cyberRaise>Medium findings>Dust loss in RoundManager allocation
Category: Business Logic

Dust loss in RoundManager allocation

Medium Impact
Medium Severity
High Likelihood

Description

The allocate function in RoundManager computes units and investmentUSD through integer division. Remainders from allocatedAmount that do not divide evenly by pricePerUnit or 10 ** paymentDecimals are not processed.

The contract records unitsRepresented and investmentAmountUSD based on these truncated values while maintaining the original allocatedAmount for treasury accounting. The remainder is neither refunded nor accounted for elsewhere.

function allocate(
    LexScrowStorage.LexScrowData storage ls,
    bytes32 agreementId,
    uint256 allocatedAmount
)
external // Use external library to save space
returns (uint256 tokenId, uint256[] memory certIds, uint256 refund) {
    // [...]
    uint256 units = allocatedAmount / round.pricePerUnit;
    uint8 paymentDecimals = IERC20Metadata(round.paymentToken).decimals();
    uint256 investmentUSD = allocatedAmount / (10 ** paymentDecimals);
    // [...]
    CertificateDetails memory details = CertificateDetails({
        signingOfficerName: officerName,
        signingOfficerTitle: officerTitle,
        investmentAmountUSD: investmentUSD,
        issuerUSDValuationAtTimeOfInvestment: round.valuation,
        unitsRepresented: units,
        legalDetails: round.legalDetails,
        extensionData: round.extensionData
    });
    // [...]
    refund = escrow.buyerAssets[0].amount - allocatedAmount;
    escrow.buyerAssets[0].amount = allocatedAmount;
    // [...]
}

Impact

Investors lose funds when payments are not divisible by pricePerUnit or token decimals. Certificates reflect fewer units and lower USD amounts than paid.

Recommendations

Calculate and refund remainders. After computing units = allocatedAmount / pricePerUnit, calculate uint256 remainder = allocatedAmount - (units * pricePerUnit). Apply the same calculation for USD amounts. Return remainders to investors before finalizing the escrow.

Remediation

This issue has been acknowledged by MetaLex, and fixes were implemented in the following commits:

The dust amount resulting from rounding is now refunded.

Note: Although usedAmount (rather than allocatedAmount) now represents the actual raised amount, the allocate function in the RoundManager contract continues to enforce the minimum amount requirement against allocatedAmount instead of usedAmount. MetaLex provided the following rationale for this design decision:

MetaLex provided the following response to this finding:

The maximum rounding down is 1 unit of precision of the payment token. (0.000001 for usdc) If the founder wants to allocate at the exact minAmount of the eoi, we would not want the contract to revert for that reason for usability/UX. We are okay acknowledging that this small amount may be under the min set -- the minimum here is mainly to avoid very small nuisance investments.

Zellic © 2025Back to top ↑