Wise Lending - DanielArmstrong's results

Decentralized liquidity market that allows users to supply crypto assets and start earning a variable APY from borrowers.

General Information

Platform: Code4rena

Start Date: 21/02/2024

Pot Size: $200,000 USDC

Total HM: 22

Participants: 36

Period: 19 days

Judge: Trust

Total Solo HM: 12

Id: 330

League: ETH

Wise Lending

Findings Distribution

Researcher Performance

Rank: 19/36

Findings: 1

Award: $940.15

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: t0x1c

Also found by: DanielArmstrong

Labels

bug
2 (Med Risk)
sufficient quality report
partial-50
duplicate-27

Awards

940.1462 USDC - $940.15

External Links

Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/MainHelper.sol#L127 https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/MainHelper.sol#L44

Vulnerability details

Impact

User cannot repay all debt because at least 1 wei of borrowShare remains. When repaying all debt with AaveHub.sol#paybackExactAmountETH, 1 wei of borrowShare remains.

Proof of Concept

AaveHub.sol#paybackExactAmountETH function which repays debt is as follows.

    function paybackExactAmountETH(
        uint256 _nftId
    )
        external
        payable
        nonReentrant
        returns (uint256)
    {
        _checkPositionLocked(
            _nftId
        );

        address aaveWrappedETH = aaveTokenAddress[
            WETH_ADDRESS
        ];

498     uint256 userBorrowShares = WISE_LENDING.getPositionBorrowShares(
            _nftId,
            aaveWrappedETH
        );

        WISE_LENDING.syncManually(
            aaveWrappedETH
        );

507     uint256 maxPaybackAmount = WISE_LENDING.paybackAmount(
            aaveWrappedETH,
            userBorrowShares
        );

        (
            uint256 paybackAmount,
            uint256 ethRefundAmount

        ) = _getInfoPayback(
            msg.value,
            maxPaybackAmount
        );

        _wrapETH(
            paybackAmount
        );

        uint256 actualAmountDeposit = _wrapAaveReturnValueDeposit(
            WETH_ADDRESS,
            paybackAmount,
            address(this)
        );

531     uint256 borrowSharesReduction = WISE_LENDING.paybackExactAmount(
            _nftId,
            aaveWrappedETH,
            actualAmountDeposit
        );

        if (ethRefundAmount > 0) {
            _sendValue(
                msg.sender,
                ethRefundAmount
            );
        }

        emit IsPaybackAave(
            _nftId,
            block.timestamp
        );

        return borrowSharesReduction;
    }

On L498, we say that userBorrowShare is 10. And on 507 the equation which calculates paybackAmount from borrowShare is userBorrowShare * pseudoTotalBorrowAmount / totalBorrowShares + 1. Here if we say pseudoTotalBorrowAmount = 100, totalBorrowShares = 50, maxPaybackAmount becomes 20 + 1. And we say actualAmountDeposit = paybackAmount = maxPaybackAmount. And the equation which calculates borrowShares from paybackAmount is maxPaybackAmount * totalBorrowShares / pseudoTotalBorrowAmount - 1. So borrowSharesReduction becomes (20 + 1) * 50 / 100 - 1 = 10 - 1.

Therefore, userBorrowShares cannot be removed and 1 wei of borrowShare remains.

Tools Used

Manual Review

We have to modify all processes of rounding up or down correctly. For example, mitigation steps for this problem are as follows.

  1. We have to modify MainHelper.sol#paybackAmount function as follows.
    function paybackAmount(
        address _poolToken,
        uint256 _shares
    )
        public
        view
        returns (uint256)
    {
        uint256 product = _shares
            * borrowPoolData[_poolToken].pseudoTotalBorrowAmount;

        uint256 totalBorrowShares = borrowPoolData[_poolToken].totalBorrowShares;

-       return product / totalBorrowShares + 1;
+       returnn (product + totalBorrowShares - 1) / totalBorrowShares;
    }
  1. We have to modify MainHelper.sol#_calculateShares function as follows.
    function _calculateShares(
        uint256 _product,
        uint256 _pseudo,
        bool _maxSharePrice
    )
        private
        pure
        returns (uint256)
    {
        return _maxSharePrice == true
-           ? _product / _pseudo + 1
-           : _product / _pseudo - 1;
+           ? (_product + _pseudo - 1) / _pseudo
+           : _product / _pseudo;
    }

Assessed type

Math

#0 - c4-pre-sort

2024-03-17T15:48:14Z

GalloDaSballo marked the issue as duplicate of #27

#1 - c4-pre-sort

2024-03-18T16:31:06Z

GalloDaSballo marked the issue as sufficient quality report

#2 - trust1995

2024-03-26T19:06:50Z

A much weaker version of the selected report.

#3 - c4-judge

2024-03-26T19:07:01Z

trust1995 marked the issue as partial-50

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter