xanoutas/solidity-security-findings huggingface dataset:
Profits generated from a gauge could be maliciously stolen 
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/governance/ProfitManager.sol#L409-L436


# Vulnerability details

## Impact
The `ProfitManager` contract manages profits generated in the system and how it is distributed between the various stakeholders.

When a loan generates profit (interests) . This profit should have been distributed proportionally to GUILD holders who  have voted for the gauges, but a flaw in the `claimGaugeRewards` function could allow the profit to be stolen by malicious attackers.

## Proof of Concept
The claimGaugeRewards function code of the ProfitManager contract is as follows:
```solidity
function claimGaugeRewards(
    address user,
    address gauge
) public returns (uint256 creditEarned) {
    uint256 _userGaugeWeight = uint256(
        GuildToken(guild).getUserGaugeWeight(user, gauge)
    );
    if (_userGaugeWeight == 0) {
        return 0;
    }
    uint256 _gaugeProfitIndex = gaugeProfitIndex[gauge];
    uint256 _userGaugeProfitIndex = userGaugeProfitIndex[user][gauge];
    if (_gaugeProfitIndex == 0) {
        _gaugeProfitIndex = 1e18;
    }
    if (_userGaugeProfitIndex == 0) {
        _userGaugeProfitIndex = 1e18;
    }
    uint256 deltaIndex = _gaugeProfitIndex - _userGaugeProfitIndex;
    if (deltaIndex != 0) {
        creditEarned = (_userGaugeWeight * deltaIndex) / 1e18;
        userGaugeProfitIndex[user][gauge] = _gaugeProfitIndex;
    }
    if (creditEarned != 0) {
        emit ClaimRewards(block.timestamp, user, gauge, creditEarned);
        CreditToken(credit).transfer(user, creditEarned);
    }
}
```
The claimGaugeRewards function can be called by any user to claim  reward from a gauge, pay attention to the code:
```solidity
    if (_userGaugeWeight == 0) {
        return 0;
    }
```
When `_userGaugeWeight=0`, the `claimGaugeRewards` function will not continue executing and will not revert.

Suppose the following scenario:
1. There are 3 GUILD holders in the system, with user A, user B, and user C holding 100 GUILD each.
2. User A and user B have voted for a gauge with 50 tokens, but user A did not vote for this gauge.
3. After a period of time, some loans were issued and generated profit in the system. Assume that gauge has generated 10 credit tokens that have not yet been distributed.
4. User A observes that there is a profit to be claimed, so he calls the `GuildToken` contract's `incrementGauge` function to temporarily vote 100 GUILDs to this gauge.
5. We noticed that there is a statement `ProfitManager(profitManager).claimGaugeRewards(user, gauge);` inside the `_incrementGaugeWeight` function, which forces the user to claim rewards. However, please note that at this time, user A's `_userGaugeWeight=0`, so he can bypass the statement for state updates, making his voting weight in the gauge become 100 votes.
6. Finally, user A calls the `claimGaugeRewards` function again.  Note the statement: `creditEarned = (_userGaugeWei
Lenders can prevent losses by frontrunning bad debts.
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The auction mechanism take 30 minutes. Once it reaches the midpoint (650 seconds), there will be a loss and the varibale `creditMultiplier` will decrease. At this point, lenders/whales can frontrun the liquidation transaction to avoid losses. As a result, small depositors will bear the majority of the losses.
Same for the `LendingTerm::forgive()` function, lenders/whales can frontrun this function to avoid losses.

## Proof of Concept
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/governance/ProfitManager.sol#L332


## Tools Used
VSCode

## Recommended Mitigation Steps
Implement a cooldown or lock period for withdrawals.


## Assessed type

Context
GuildToken transfers are not allowed if any of the initial gauges of the user has a loss that has not been applied to the user
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/tokens/ERC20Gauges.sol#L500


# Vulnerability details

## Impact
In GuildToken.sol a comment says "Even when a loss occur, users can still transfer tokens with which they vote for gauges that did not suffer a loss" which is perfectly logical thing to expect. Other additional comments [here](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/tokens/GuildToken.sol#L29) mean that the user should be able to transfer Guild tokens which have not been used to add weight to a loss-suffering gauge. 

But this is not true for one case. 

The GuildToken.transfer function calls ERC20Gauges._decrementWeightUntilFree function to free up gauge weights from all his gauges (starting from the first entry in the userGauges list) until sufficient weight has been freed such that the desired guild tokens can be transferred (provided more than the desired amount are actually existing as free from any gauge, or put in non-slashed gauges so that they can be withdrawn, or free from debtCeiling constraints : essentially freeweights).

Suppose user has 1000 GUILD tokens and he allocates them to 10 different gauges A, B, C..... ... these gauges are added to the userGauges list sequentially as the user incrementGauge for them and exist in that particular order until user decides to remove entire weight from one of them( or gets losses to a gauge) and that gauge is then removed from the list.

So right now, A, B, C all have 100 weight from the user and there are zero freeWeights. Now assume that gauge B suffers a loss so that 100 guild token will be locked. Now other 900 guild token should be accessible to transfer etc. because they have not suffered any losses, so user should be free to free/reallocate/transfer them. 

But if user wants to transfer 200 Guild tokens (basically anything > 100), then it will fail. This causes revert on the entire transfer txn and most of the user's guild tokens may get locked for arbitrary time

This is because in the transfer, _decrementWeightUntilFree is called which starts freeing gauge weights from the user using _decrementGaugeWeight for each gauge starting from the first gauge in the user list (which have been put into the list sequentially as the user entered those gauges). In i = 0 , 100 gauge weights gets freed, so totalFreed = 100. Now in i = 1, _decrementGaugeWeight tries to free weights from gauge B (where a loss has been notified but loss has not been applied to) which will fail due to the require statement [here](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/tokens/GuildToken.sol#L214)

But user still has 900 total guild tokens which he should be able to transfer/free but the intermediate gauges in the list that might have suffered losses cause transfers to fail. So even though the user has t
It is possible to front-run notifyPnL with a call to incrementGauge to claim distributed rewards.
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/tokens/ERC20Gauges.sol#L219-L226
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/governance/ProfitManager.sol#L292-L405
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/governance/ProfitManager.sol#L409-L436


# Vulnerability details

## Impact
The function `incrementGauge` allows user to vote for a particular term with their GUILD tokens:
```solidity
function incrementGauge(
        address gauge,
        uint256 weight
    ) public virtual returns (uint256 newUserWeight) {
        require(isGauge(gauge), "ERC20Gauges: invalid gauge");
        _incrementGaugeWeight(msg.sender, gauge, weight);
        return _incrementUserAndGlobalWeights(msg.sender, weight);
    }
```
When there is profit in a term it is distributed through call to `ProfitManager.notifyPnL()`, which increases the profitIndex of a term.
```solidity 
if (_gaugeWeight != 0) {
                    uint256 _gaugeProfitIndex = gaugeProfitIndex[gauge];
                    if (_gaugeProfitIndex == 0) {
                        _gaugeProfitIndex = 1e18;
                    }
                    gaugeProfitIndex[gauge] =
                        _gaugeProfitIndex +
                        (amountForGuild * 1e18) /
                        _gaugeWeight;
                }
```
In order to claim rewards, a user has to call `ProftiManager.claimGaugeRewards()`:
```solidity
function claimGaugeRewards(
        address user,
        address gauge
    ) public returns (uint256 creditEarned) {
        uint256 _userGaugeWeight = uint256(
            GuildToken(guild).getUserGaugeWeight(user, gauge)
        );
        if (_userGaugeWeight == 0) {
            return 0;
        }
        uint256 _gaugeProfitIndex = gaugeProfitIndex[gauge];
        uint256 _userGaugeProfitIndex = userGaugeProfitIndex[user][gauge];
        if (_gaugeProfitIndex == 0) {
            _gaugeProfitIndex = 1e18;
        }
        if (_userGaugeProfitIndex == 0) {
            _userGaugeProfitIndex = 1e18;
        }
        uint256 deltaIndex = _gaugeProfitIndex - _userGaugeProfitIndex;
        if (deltaIndex != 0) {
            creditEarned = (_userGaugeWeight * deltaIndex) / 1e18;
            userGaugeProfitIndex[user][gauge] = _gaugeProfitIndex;
        }
        if (creditEarned != 0) {
            emit ClaimRewards(block.timestamp, user, gauge, creditEarned);
            CreditToken(credit).transfer(user, creditEarned);
        }
    }
```
Currently it is possible to front-run profit distribution incrementing gaugeWeight, claiming rewards and then immediately decrementing GaugeWeight, which is a risk-free activity for a user, because he is not risking his guild tokens to get slashed.
## Proof of Concept
The reason it is possible is because the amount of rewards is not distributed linearly
Missing checks for transferability when transferring CreditTokens
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/8f439544b8a634e8f3c3db718416a39752cd471e/src/tokens/GuildToken.sol#L182
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/8f439544b8a634e8f3c3db718416a39752cd471e/src/tokens/GuildToken.sol#L288
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/8f439544b8a634e8f3c3db718416a39752cd471e/src/tokens/GuildToken.sol#L302
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/8f439544b8a634e8f3c3db718416a39752cd471e/src/tokens/GuildToken.sol#L169
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/8f439544b8a634e8f3c3db718416a39752cd471e/src/tokens/GuildToken.sol#L175


# Vulnerability details

## Impact
Users can transfer GuildTokens even if transferable is not enabled. Without checks for `CreditToken` transferability, users can bypass the `CoreRoles.GUILD_MINTER` role and mint tokens to themselves.

## Proof of Concept

The documentation says that at deployment, tokens are not transferable (can only mint/burn). `GuildToken::_beforeTokenTransfer()` is a function that check whether transfers are enable on GuildTokens or not.

However `GuildToken::_beforeTokenTransfer()` is not called by any of `GuildToken` transfer functions, enabling users to bypass this rule.

Place the code for the following test function in `test/unit/tokens/GuildToken.t.sol`.

```solidity
function testRevert_IfTransferNotEnabled() public {
    address userFrom = address("user_from");
    address userTo = address("user_to");
    
    vm.startPrank(governor);
    core.createRole(CoreRoles.GUILD_MINTER, CoreRoles.GOVERNOR);
    core.grantRole(CoreRoles.GUILD_MINTER, address(this));
    vm.stopPrank();

    token.mint(userFrom, 5 * 10 ** 18);
    vm.expectRevert("GuildToken: transfers not enabled");
    vm.prank(userFrom);
    token.transfer(userTo, 5 * 10 ** 18);

    vm.prank(governor);
    token.enableTransfer();
    vm.prank(userFrom);
    token.transfer(userTo, 5 * 10 ** 18);

    assertEq(token.balanceOf(userFrom), 0);
    assertEq(token.balanceOf(userTo), 5 * 10 ** 18);
}

function testRevert_IfTransferFromNotEnabled() public {
    address userFrom = address("user_from");
    address userSpender = address("user_spender");
    address userTo = address("user_to");
    
    vm.startPrank(governor);
    core.createRole(CoreRoles.GUILD_MINTER, CoreRoles.GOVERNOR);
    core.grantRole(CoreRoles.GUILD_MINTER, address(this));
    vm.stopPrank();

    address userFrom = address("user_from");
    address userSpender = address("user_spender");
    address userTo = address("user_to");
    token.mint(userOwner, 5 * 10 ** 18);
    vm.prank(userFrom);
    token.approve(userSpender, 5 * 10 ** 18);
    vm.expectRevert("GuildToken: transfers not enabled");
    vm.prank(userSpender);
    token.transferFrom(userFrom, userTo, 5 * 10 ** 18);

    vm.prank(governor);
    token.enableTransfer();
    vm.prank(userSpender);
    token.transfer(userFrom, userTo, 5 * 10 ** 18);

    ass
After a gauge loss has occured, staking in that gauge through the SurplusGuildMinter will result in a loss of the staked CREDIT
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/SurplusGuildMinter.sol#L229


# Vulnerability details

The `SurplusGuildMinter.getRewards` function compares the last gauge loss for the specified lending term to the last gauge loss that was applied to the user's SGM stake. If the last gauge loss is more recent than the last gauge loss applied to the user's SGM stake, the user's SGM stake is slashed (https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/SurplusGuildMinter.sol#L229):

```solidity
        lastGaugeLoss = GuildToken(guild).lastGaugeLoss(term);
        if (lastGaugeLoss > uint256(userStake.lastGaugeLoss)) {
            slashed = true;
        }
```

However, because the `userStake` struct is declared as a named return variable in the function and not re-assigned before the comparison, it is still initialized with its default value when the comparison takes place. Consequently, the comparison always checks if `lastGaugeLoss` is greater than 0 which is always the case after the first loss was registered for the gauge/term.

## Impact

Anyone who stakes CREDIT in a gauge through the SGM after a loss has been registered for that gauge, will lose their stake whenever `getRewards` is called for the user and the gauge, which also happens when the user calls `unstake` (https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/SurplusGuildMinter.sol#L160).

In short, staking CREDIT through the SGM in a gauge that had a loss in the past, will always result in a full loss of the staked CREDIT.

## Proof of Concept

A test case that demonstrates the issue can be added to the [SGM unit test](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/test/unit/loan/SurplusGuildMinter.t.sol):

```solidity
    function testRepeatedSlashing() public {
        // setup
        address alice = address(0xa11ce);
        address bob = address(0xb0b);

        credit.mint(address(alice), 300e18);
        credit.mint(address(bob), 150e18);

        // Alice stakes 150e18 credit in the SGM
        vm.startPrank(alice);
        credit.approve(address(sgm), 150e18);
        sgm.stake(term, 150e18);
        vm.stopPrank();

        assertEq(credit.balanceOf(alice), 150e18);
        assertEq(credit.balanceOf(bob), 150e18);
        assertEq(profitManager.surplusBuffer(), 0);
        assertEq(profitManager.termSurplusBuffer(term), 150e18);
        assertEq(guild.balanceOf(address(sgm)), 300e18);
        assertEq(guild.getGaugeWeight(term), 350e18);
        assertEq(sgm.getUserStake(alice, term).credit, 150e18);

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

        // there is a loss in the LT
        profitManager.notifyPnL(term, -1e18);
        uint256 firstGaugeLoss = block.timestamp;
        assertEq(guild.lastGaugeLoss(term), firstGaugeLoss);

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

        // Alice's
Auctions can continuously create bad debt for the protocol in volatile market conditions
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/AuctionHouse.sol#L133-L161


# Vulnerability details

## Impact
```solidity
// first phase of the auction, where more and more collateral is offered
        if (block.timestamp < _startTime + midPoint) {
            // ask for the full debt
            creditAsked = auctions[loanId].callDebt;

            // compute amount of collateral received
            uint256 elapsed = block.timestamp - _startTime; // [0, midPoint[
            uint256 _collateralAmount = auctions[loanId].collateralAmount; // SLOAD
            collateralReceived = (_collateralAmount * elapsed) / midPoint; 
        }
```
Phase 1 of the auction offers less collateral for the full debt amount, which can be problematic in volatile market conditions. If the price of the collateral is dropping rapidly, waiting for phase 1 to finish could cause problems. Liquidators must wait for phase 2 to liquidate all collateral. Until that time, the price of the collateral tokens may continue to decrease, which can cause liquidators to not bid on the auctions and result in bad debt for the protocol.

## Proof of Concept
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/AuctionHouse.sol#L133-L161

## Tools Used
Manual Review
## Recommended Mitigation Steps



## Assessed type

Other
Not decrementing the gauge Weight of SuprlusGuildMinter contract can cause stakers to receive less rewards than they should receive.
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/SurplusGuildMinter.sol#L114
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/SurplusGuildMinter.sol#L216
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/governance/ProfitManager.sol#L392
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/governance/ProfitManager.sol#L409


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
If a credit token holder wants to stake the credit tokens so as to earn rewards in terms of credit tokens and Guild tokens,the user transfers the credit tokens to the SurplusGuildMinter contract and the guild tokens are minted to the SuprlusGuildMinter contract which on the behalf of the credit token holder increases the gauge weight of that gauge which user inputs. Now if the user is slashed then its stakes are reduced to zero whereas the gauge weight which the SurplusGuildMinter contract increased is not decreased immediately.Now a user can only be slashed if a gauge is depreciated i.e it is off boarded and it has suffered a loss.Now a depreciated can again be added using on boarding mechanism,so now again new users can stake the tokens and can earn rewards if that gauge earns profits.Issue is if the gauge weight contributed by the surplus minter is not decreased immediately after a user gets slashed it can cause the gauge weight to remain same instead it should decrease because some users were slashed so their wights should be decreased from the gauge weight, if it is not done than it can cause the rewards to the current stakers to be reduced than the actual value.And if the weights increased by the suprlusGuild minter contract are reduced after the gauge has been onbarded again it can cause the weights to reduce by more than the required value and cause more rewards been allocated to the users.
## Proof of Concept
Lets start step by step 
1.User calls the stake function
```
function stake(address term, uint256 amount) external whenNotPaused {
        // apply pending rewards
        (uint256 lastGaugeLoss, UserStake memory userStake, ) = getRewards(
            msg.sender,
            term
        );

        require(
            lastGaugeLoss != block.timestamp,
            "SurplusGuildMinter: loss in block"
        );
        require(amount >= MIN_STAKE, "SurplusGuildMinter: min stake");

        // pull CREDIT from user & transfer it to surplus buffer
        CreditToken(credit).transferFrom(msg.sender, address(this), amount);
        CreditToken(credit).approve(address(profitManager), amount);
        ProfitManager(profitManager).donateToTermSurplusBuffer(term, amount);

        // self-mint GUILD tokens
        uint256 _mintRatio = mintRatio;
        uint256 guildAmount = (_min