sherlock-audit / 2024-09-predict-fun-judging

0 stars 0 forks source link

Energetic Tangelo Starfish - Refinance Batch may DoS due to quadratic memory expansion cost #268

Open sherlock-admin3 opened 2 days ago

sherlock-admin3 commented 2 days ago

Energetic Tangelo Starfish

Medium

Refinance Batch may DoS due to quadratic memory expansion cost

Summary

The refinance(batch) function uses an array of RefinancingResult structures, stored in memory to pass it to emit LoansRefinanced(results). As can be seen in the code below, one instance of RefinancingResult takes up ~300 bytes.

According to the Ethereum Yellow Paper and this discussion, if a call uses more than 724 bytes of memory, the cost for each new byte will be calculated quadratically, which increases the transaction cost.

Thus, this extremely inefficient use of memory can lead to a situation where if the array has ~700 elements, the transaction will always DoS due to the limitation of block.gasLimit = 30m.

struct RefinancingResult {
        bytes32 proposalId; 
        uint256 refinancedLoanId;
        uint256 newLoanId;
        address lender;
        uint256 collateralAmount;
        uint256 loanAmount;
        uint256 interestRatePerSecond;
        uint256 minimumDuration;
        uint256 protocolFee;
 }

function refinance(
        Refinancing[] calldata refinancings
    ) external nonReentrant whenNotPaused onlyRole(REFINANCIER_ROLE) {
        RefinancingResult[] memory results = new RefinancingResult[](refinancings.length);
        for (uint256 i; i < refinancings.length; ++i) {
            Refinancing calldata refinancing = refinancings[i];
            (uint256 id, Loan memory loan, uint256 protocolFee) = _refinance(refinancing);

            // Doing this check after the refinancing, but in realitiy
            // it does not matter because the transaction simulation would've
            // failed before it is submitted on-chain.
            address borrower = loan.borrower;
            if (autoRefinancingEnabled[borrower] == 0) {
                revert BorrowerDidNotEnableAutoRefinancing(borrower);
            }

            results[i] = RefinancingResult(
                hashProposal(refinancing.proposal),
                refinancing.loanId,
                id,
                loan.lender,
                loan.collateralAmount,
                loan.loanAmount,
                loan.interestRatePerSecond,
                loan.minimumDuration,
                protocolFee
            );
        }
        emit LoansRefinanced(results);
    }

Root Cause

Using memory array with heavy structs in unbounded loop leads to tx DoS due to block.gasLimit

Internal pre-conditions

No response

External pre-conditions

Refinancer must include to refinansings list ~700 loans

Attack Path

No response

Impact

DoS of core function of protocol with low likelihood. Severity: Medium

PoC

No response

Mitigation

Change event parameters for this function.This will save you from a potential DoS, as well as save money for Refinancer