#41876 [SC-Insight] User may receive boosted values which are non-concave

Submitted on Mar 19th 2025 at 03:58:15 UTC by @h2134 for Audit Comp | Yeet

  • Report ID: #41876

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/Yeet.sol

  • Impacts:

    • Contract fails to deliver promised returns, but doesn't lose value

Description

Brief/Intro

The boosted values user receives by specifying nft tokens when yeets are non-concave, this lead to user receive unexpected amount of reward tokens, and the the power of big NFT holders is not properly limited.

Vulnerability Details

The Official Document states that a concave curve is applied to the calculation of boosted value, holding greater numbers of NFTs applies increased boost but the marginal benefit should be non-increasing.

A concave curve is applied to this boost in order to limit the power of big holders

To achieve this, nftBoostLookup is defined as the NFT boost lookup table.

https://github.com/immunefi-team/audit-comp-yeet/blob/da15231cdefd8f385fcdb85c27258b5f0d0cc270/src/Yeet.sol#L187-L214

    uint256[26] public nftBoostLookup = [
        0,
        345,
        540,
        675,
        765,
        840,
        900,
        960,
        1005,
        1050,
        1080,
        1100,
        1155,
        1185,
        1215,
        1245,
        1275,
        1305,
        1335,
        1365,
        1380,
        1400,
        1440,
        1455,
        1470,
        1500
    ];

However, when we draw the curve, we can find the curve is not always concave and there are infection points where the curve becomes convex.

For example, from 10 to 11, the increased weight ratio is 1.018 (1100 / 1080), however from 11 to 12, the increased ratio 1.05 (1155 / 1100), therefore there is an infection point at 11 makes the curve convex.

Please find the curve image in the attachment.

Impact Details

The concave curve is applied to limit the power of big holders, fail to do so results in user receives unexpected amount of reward tokens, when the curve becomes convex, a big holder can receive more reward tokens than expected as they gain more boosted values by providing more NFTs, whereas the small holders receive less rewards.

References

https://github.com/immunefi-team/audit-comp-yeet/blob/da15231cdefd8f385fcdb85c27258b5f0d0cc270/src/Yeet.sol#L187-L214

Proof of Concept

Proof of Concept

Take the example of points [10, 11, 12], because the curve is expected to be concave, then the boosted values gained from 10 and 12 should be no larger than the 2x value of the boosted value gained from 11, however the POC show otherwise:

Please run forge test --mt testAudit_NFTBoost_LookUp -vv in Yeet.Test.sol:

    function testAudit_NFTBoost_LookUp() public {
        address alice = makeAddr("Alice");
        nft.mintAmount(alice, 25);

        uint256[] memory aliceTokenIds1 = new uint256[](10);
        for (uint i = 0; i < aliceTokenIds1.length; ++i) {
            aliceTokenIds1[i] = i;
        }
        uint256 aliceBoostedValue1 = yeet.getBoostedValue(alice, 0.1 ether, aliceTokenIds1);
        console.log("Alice boostedValue1:", aliceBoostedValue1);

        uint256[] memory aliceTokenIds2 = new uint256[](12);
        for (uint i = 0; i < aliceTokenIds2.length; ++i) {
            aliceTokenIds2[i] = i;
        }
        uint256 aliceBoostedValue2 = yeet.getBoostedValue(alice, 0.1 ether, aliceTokenIds2);
        console.log("Alice boostedValue2:", aliceBoostedValue2);

        uint256 aliceTotalBoostedValue = aliceBoostedValue1 + aliceBoostedValue2;
        console.log("Alice total boostedValue:", aliceTotalBoostedValue);

        console.log();

        address bob = makeAddr("Bob");
        nft.mintAmount(bob, 25);

        uint256[] memory bobTokenIds1 = new uint256[](11);
        for (uint i = 0; i < bobTokenIds1.length; ++i) {
            bobTokenIds1[i] = i;
        }
        uint256 bobBoostedValue1 = yeet.getBoostedValue(bob, 0.1 ether, bobTokenIds1);
        console.log("Bob boostedValue1:", bobBoostedValue1);

        uint256[] memory bobTokenIds2 = new uint256[](11);
        for (uint i = 0; i < bobTokenIds2.length; ++i) {
            bobTokenIds2[i] = i;
        }
        uint256 bobBoostedValue2 = yeet.getBoostedValue(bob, 0.1 ether, bobTokenIds2);
        console.log("Bob boostedValue2:", bobBoostedValue2);

        uint256 bobTotalBoostedValue = bobBoostedValue1 + bobBoostedValue2;
        console.log("Bob total boostedValue:", bobTotalBoostedValue);

        // The cruve is non-concave at points [10, 11, 12]
        assertLt(bobTotalBoostedValue, aliceTotalBoostedValue);
    }

Was this helpful?