sherlock-audit / 2024-04-teller-finance-judging

10 stars 9 forks source link

EgisSecurity - TellerV2.sol#lenderAcceptBid() - Protocol fee and marketplace fee can change after the borrower `submitBid` #180

Closed sherlock-admin3 closed 4 months ago

sherlock-admin3 commented 4 months ago

EgisSecurity

medium

TellerV2.sol#lenderAcceptBid() - Protocol fee and marketplace fee can change after the borrower submitBid

Summary

TellerV2.sol#lenderAcceptBid() - Protocol fee and marketplace fee can change after the borrower submitBid

Vulnerability Detail

When a borrower calls submitBid, he sees and expects that the protocol fee and marketplace fee don't change during the period when his bid is pending, but they can change.

When a lender fills a bid, some part of the principal goes to the protocol and to the marketplace as fees. If the fees go up, then the borrower can be left with less amountToBorrower than he expected when he originally submitted the bid.

function lenderAcceptBid(uint256 _bidId)
        external
        override
        pendingBid(_bidId, "lenderAcceptBid")
        whenNotPaused
        returns (
            uint256 amountToProtocol,
            uint256 amountToMarketplace,
            uint256 amountToBorrower
        )
    {
        // Retrieve bid
        Bid storage bid = bids[_bidId];

        address sender = _msgSenderForMarket(bid.marketplaceId);

        (bool isVerified, ) = marketRegistry.isVerifiedLender(
            bid.marketplaceId,
            sender
        );
        require(isVerified, "Not verified lender");

        require(
            !marketRegistry.isMarketClosed(bid.marketplaceId),
            "Market is closed"
        );

        require(!isLoanExpired(_bidId), "Bid has expired");

        // Set timestamp
        bid.loanDetails.acceptedTimestamp = uint32(block.timestamp);
        bid.loanDetails.lastRepaidTimestamp = uint32(block.timestamp);

        // Mark borrower's request as accepted
        bid.state = BidState.ACCEPTED;

        // Declare the bid acceptor as the lender of the bid
        bid.lender = sender;

        // Tell the collateral manager to deploy the escrow and pull funds from the borrower if applicable
        collateralManager.deployAndDeposit(_bidId);

        // Transfer funds to borrower from the lender
        amountToProtocol = bid.loanDetails.principal.percent(protocolFee());
        amountToMarketplace = bid.loanDetails.principal.percent(
            marketRegistry.getMarketplaceFee(bid.marketplaceId)
        );
        amountToBorrower =
            bid.loanDetails.principal -
            amountToProtocol -
            amountToMarketplace;

        //transfer fee to protocol
        if (amountToProtocol > 0) {
            bid.loanDetails.lendingToken.safeTransferFrom(
                sender,
                owner(),
                amountToProtocol
            );
        }

        //transfer fee to marketplace
        if (amountToMarketplace > 0 ) {
            bid.loanDetails.lendingToken.safeTransferFrom(
                sender,
                marketRegistry.getMarketFeeRecipient(bid.marketplaceId),
                amountToMarketplace
            );
        }

        //transfer funds to borrower
        if (amountToBorrower > 0) {
            bid.loanDetails.lendingToken.safeTransferFrom(
                sender,
                bid.receiver,
                amountToBorrower
            );
        }

        // Record volume filled by lenders
        lenderVolumeFilled[address(bid.loanDetails.lendingToken)][sender] += bid
            .loanDetails
            .principal;
        totalVolumeFilled[address(bid.loanDetails.lendingToken)] += bid
            .loanDetails
            .principal;

        // Add borrower's active bid
        _borrowerBidsActive[bid.borrower].add(_bidId);

        // Emit AcceptedBid
        emit AcceptedBid(_bidId, sender);

        emit FeePaid(_bidId, "protocol", amountToProtocol);
        emit FeePaid(_bidId, "marketplace", amountToMarketplace);
    }

Impact

Fees can change between when a bit is submitted and when is accepted, which is unfair to the borrower.

Code Snippet

https://github.com/sherlock-audit/2024-04-teller-finance/blob/defe55469a2576735af67483acf31d623e13592d/teller-protocol-v2-audit-2024/packages/contracts/contracts/TellerV2.sol#L524-L527

Tool used

Manual Review

Recommendation

Whenever a bit is submitted, cache the protocol fee and the marketplace fee for that bid.

Duplicate of #125