Caviar Private Pools - ck's results

A fully on-chain NFT AMM that allows you to trade every NFT in a collection.

General Information

Platform: Code4rena

Start Date: 07/04/2023

Pot Size: $47,000 USDC

Total HM: 20

Participants: 120

Period: 6 days

Judge: GalloDaSballo

Total Solo HM: 4

Id: 230

League: ETH

Caviar

Findings Distribution

Researcher Performance

Rank: 117/120

Findings: 1

Award: $5.98

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

5.9827 USDC - $5.98

Labels

bug
2 (Med Risk)
high quality report
satisfactory
duplicate-858

External Links

Lines of code

https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L385-L417 https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c/src/PrivatePool.sol#L731-L738

Vulnerability details

Impact

When the PrivatePool::change() is called, fees are calculated using the changeFeeQuote function.

    function change(
        uint256[] memory inputTokenIds,
        uint256[] memory inputTokenWeights,
        MerkleMultiProof memory inputProof,
        IStolenNftOracle.Message[] memory stolenNftProofs,
        uint256[] memory outputTokenIds,
        uint256[] memory outputTokenWeights,
        MerkleMultiProof memory outputProof
    ) public payable returns (uint256 feeAmount, uint256 protocolFeeAmount) {
        // ~~~ Checks ~~~ //

        // check that the caller sent 0 ETH if base token is not ETH
        if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount();

        // check that NFTs are not stolen
        if (useStolenNftOracle) {
            IStolenNftOracle(stolenNftOracle).validateTokensAreNotStolen(nft, inputTokenIds, stolenNftProofs);
        }

        // fix stack too deep
        {
            // calculate the sum of weights for the input nfts
            uint256 inputWeightSum = sumWeightsAndValidateProof(inputTokenIds, inputTokenWeights, inputProof);

            // calculate the sum of weights for the output nfts
            uint256 outputWeightSum = sumWeightsAndValidateProof(outputTokenIds, outputTokenWeights, outputProof);

            // check that the input weights are greater than or equal to the output weights
            if (inputWeightSum < outputWeightSum) revert InsufficientInputWeight();

            // calculate the fee amount
            (feeAmount, protocolFeeAmount) = changeFeeQuote(inputWeightSum);
        }
    function changeFeeQuote(uint256 inputAmount) public view returns (uint256 feeAmount, uint256 protocolFeeAmount) {
        // multiply the changeFee to get the fee per NFT (4 decimals of accuracy)
        uint256 exponent = baseToken == address(0) ? 18 - 4 : ERC20(baseToken).decimals() - 4;
        uint256 feePerNft = changeFee * 10 ** exponent;

        feeAmount = inputAmount * feePerNft / 1e18;
        protocolFeeAmount = feeAmount * Factory(factory).protocolFeeRate() / 10_000;
    }

The issue is that when calcuating the exponent in the changeFeeQuote function, the subtraction ERC20(baseToken).decimals() - 4; will result in an underflow for tokens with less than 4 decimals.

Note that some popular tokens such as EURS with 2 decimals would fail. This means that the protocol would break when a user attempts to perform a change with tokens with less than 4 decimals.

Proof of Concept

Modify the test/shared/ShibaInu.sol file to set the token to 3 decimals:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "solmate/tokens/ERC20.sol";

contract ShibaInu is ERC20 {
    constructor() ERC20("Shiba Inu", "SHIB", 3) {}
}

Then run the test:

forge test -vvv --ffi --match-path test/PrivatePool/Change.t.sol

This will revert with the following error:

├─ [1393] PrivatePool::changeFeeQuote(3000000000000000000) [staticcall] │ ├─ [293] ShibaInu::decimals() [staticcall] │ │ └─ ← 3 │ └─ ← "Arithmetic over/underflow" └─ ← "Arithmetic over/underflow"

Tools Used

It may be advisable to use a scaling factor in the protocol when dealing with tokens to ensure the underflow does not happen

#0 - c4-pre-sort

2023-04-19T07:53:58Z

0xSorryNotSorry marked the issue as high quality report

#1 - c4-pre-sort

2023-04-20T15:22:47Z

0xSorryNotSorry marked the issue as duplicate of #858

#2 - c4-judge

2023-05-01T07:14:46Z

GalloDaSballo marked the issue as satisfactory

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