xanoutas/solidity-security-findings huggingface dataset:
There is a risk of DoS attacks due to rate limits imposed on minting tokens
Audit: 2023-12-ethereumcreditguild-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/LendingTerm.sol#L418
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/rate-limits/RateLimitedMinter.sol#L53
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/utils/RateLimitedV2.sol#L93
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/utils/RateLimitedV2.sol#L83


# Vulnerability details

## Impact

Due to the rate limit on minting of CREDIT tokens imposed on the operation of `_borrow` in `LendingTerm.sol`, malicious users could launch denial-of-service attacks. A malicious user could call _borrow and use up the entire buffer imposed by `RateLimitedMinter.sol` resetting it to 0 for that block as described below (and potentially do this continuously block after block to prevent anyone else from borrowing, by repaying in the next block and immediately borrowing a new loan, etc). Note that you cannot open a loan and repay it in the same block, so you couldn't use a flash loan to do this. But remember that the buffer may not be a massive amount, especially after not much time has elapsed since it was used up.

When the malicious user borrows the entire amount available, the _depleteBuffer function will reset `bufferStored` to 0 and, if another user comes to try to borrow in that same block, they will not be able to because the lesser of `bufferCap` and `bufferStored` + rate limit * seconds elapsed will be 0 (see `buffer()` and `_depleteBuffer()` functions below). 

Another way they could do it is to watch the mempool for any call to `borrow` from a third party and front run any call with their own call to `borrow`, preventing that third party from being able to borrow.  

This isn't a very likely attack given that you would need sufficient collateral to borrow the entire buffer, but the buffer may be small at certain points in time.

## Proof of Concept

The _borrow function calls mint in RateLimitedMinter.sol [link to code](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/loan/LendingTerm.sol#L418):
```

        // mint debt to the borrower
        RateLimitedMinter(refs.creditMinter).mint(borrower, borrowAmount);

        // pull the collateral from the borrower
        IERC20(params.collateralToken).safeTransferFrom(
            borrower,
            address(this),
            collateralAmount
        );

```

The mint function in RateLimitedMinter.sol is subject to _depleteBuffer [link to code](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/rate-limits/RateLimitedMinter.sol#L53):
```
function mint(
        address to,
        uint256 amount
    ) external onlyCoreRole(role) whenNotPaused {
        _depleteBuffer(amount); /// che
An attacker can sandwich transactions where profit is notified to profit from gauge rewards
Audit: 2023-12-ethereumcreditguild-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

Whenever a user calls `partialRepay()` or `repay()` where profit is notified, a MEV bot can front-run that transaction and increase the gauge weight where profit is about to be notified.

After the profit is notified the attacker will remove the gauge weight and claim the rewards that he received because of the `gaugeProfitIndex` increase. This allows the attacker to sandwich transactions where profit is notified and profit from this.


## Impact

The attacker will profit from this and receive credit tokens or guild tokens when using the SurplusGuildMinter. The reward for everyone else can then be smaller and users will receive less rewards. 


## Proof of Concept

Add this to `ProfitManager.t.sol` and import `"@forge-std/console.sol";`

As you can see the attacker will be able to profit from the `gaugeProfitIndex` increase. He can receive even more rewards when using the SurplusGuildMinter.


```solidity
function testSandwichAttack() public {
        address attacker = makeAddr("attacker");

        vm.startPrank(governor);
        core.grantRole(CoreRoles.GOVERNOR, address(this));
        core.grantRole(CoreRoles.CREDIT_MINTER, address(this));
        core.grantRole(CoreRoles.GUILD_MINTER, address(this));
        core.grantRole(CoreRoles.GAUGE_ADD, address(this));
        core.grantRole(CoreRoles.GAUGE_PARAMETERS, address(this));
        core.grantRole(CoreRoles.GAUGE_PNL_NOTIFIER, address(this));
        vm.stopPrank();

        vm.prank(governor);
        profitManager.setProfitSharingConfig(
            0, // surplusBufferSplit
            0.5e18, // creditSplit
            0.5e18, // guildSplit
            0, // otherSplit
            address(0) // otherRecipient
        );
        guild.setMaxGauges(1);
        guild.addGauge(1, gauge1);
        guild.mint(attacker, 500e18);
   
        //FRONT-RUN
        vm.prank(attacker);
        guild.incrementGauge(gauge1, 500e18);
        
        vm.warp(block.timestamp + 1);

        credit.mint(address(profitManager), 200e18);
        profitManager.notifyPnL(gauge1, 200e18);

        vm.warp(block.timestamp + 1);

        //BACK-RUN
        profitManager.claimGaugeRewards(attacker,gauge1);
        vm.prank(attacker);
        guild.decrementGauge(gauge1, 500e18);

        console.log("Attackers profit: %s CREDIT", credit.balanceOf(attacker) / 1e18);
    }

```


## Tools Used

Foundry

## Recommended Mitigation Steps

Add a cooldown period, although it might not solve this problem fully, MEV bots wont be incentivised to do this because their guild tokens can be slashed while locked. 


## Assessed type

MEV
_delegate function doesn't check for whitelisted address which will cause the function to act unexpectedly for the delegator.
Audit: 2023-12-ethereumcreditguild-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/tokens/ERC20MultiVotes.sol#L290-L315


# Vulnerability details

## Impact
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/tokens/ERC20MultiVotes.sol#L297

In `ERC20MultiVotes.sol`, the `_delegate` function is used to add a delegate to the delegator. But the function checks `require(count < 2, "ERC20MultiVotes: delegation error");` and doesn't check is the delegator is a whitelisted address to exceed maximum delegates. 

## Proof of Concept
- A user with [`canContractExceedMaxDelegates`](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/tokens/ERC20MultiVotes.sol#L144) set to true calls [delegate function](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/tokens/ERC20MultiVotes.sol#L144)

- The function would undelegate the first delegated user of the delegator and then delegate the `newDelegatee` as shown in the code.

- Which is not supposed to happen to whitelisted addresses. So there should be a check for whitelisted addresses.

## Tools Used
VS Code

## Recommended Mitigation Steps
Change to :

    require(count < 2 || canContractExceedMaxDelegates[delegator], 
    "ERC20MultiVotes: delegation error");


## Assessed type

call/delegatecall
Rebasing Tokens Cause Incorrect Balance Calculations and Unexpected Behavior in safeTransferFrom Function
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://gitlab.com/thorchain/thornode/-/blob/develop/chain/ethereum/contracts/THORChain_Router.sol#L167


# Vulnerability details

## Impact
Tokens that rebase periodically adjust their supply, which affects balances independently of transfers. The current implementation may not account for these adjustments properly, potentially leading to incorrect balance calculations and unexpected behavior.

## Proof of Concept
Rebasing tokens adjust the user's balance over time. The current code assumes that the balance change between `_startBal` and `_endBal` is only due to the transfer, which may not be true for rebasing tokens. This can lead to incorrect handling of such tokens in functions like `safeTransferFrom`.

### Lines of Code

```solidity
function safeTransferFrom(address _asset, uint _amount) internal returns(uint amount) {
    uint _startBal = iERC20(_asset).balanceOf(address(this));
    (bool success, bytes memory data) = _asset.call(abi.encodeWithSignature("transferFrom(address,address,uint256)", msg.sender, address(this), _amount));
    require(success, "Transfer failed");
    if (data.length > 0) {
        require(abi.decode(data, (bool)), "ERC20 transfer failed");
    }
    uint _endBal = iERC20(_asset).balanceOf(address(this));
    require(_endBal >= _startBal, "Transfer amount exceeds balance");
    uint transferredAmount = _endBal - _startBal;
    require(transferredAmount <= _amount, "Transferred amount exceeds requested amount");
    return transferredAmount;
}
```

## Tools Used
Manual

## Recommended Mitigation Steps
Consider implementing additional logic that can account for balance adjustments that are not directly related to transfers. One approach could be to add support for whitelisting known rebasing tokens and adjusting balance calculations accordingly.


## Assessed type

ERC20
`_transferOutAndCallV5` transfers gas assets to a wrong address when the swap fails, resulting in the loss of user funds
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L324


# Vulnerability details

## Vulnerability details
https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L304C12-L328
```solidity
  function _transferOutAndCallV5(
    TransferOutAndCallData calldata aggregationPayload
  ) private {
    if (aggregationPayload.fromAsset == address(0)) {
      // call swapOutV5 with ether
      (bool swapOutSuccess, ) = aggregationPayload.target.call{
        value: msg.value
      }(
        abi.encodeWithSignature(
          "swapOutV5(address,uint256,address,address,uint256,bytes,string)",
          aggregationPayload.fromAsset,
          aggregationPayload.fromAmount,
          aggregationPayload.toAsset,
          aggregationPayload.recipient,
          aggregationPayload.amountOutMin,
          aggregationPayload.payload,
          aggregationPayload.originAddress
        )
      );
      if (!swapOutSuccess) {
->        bool sendSuccess = payable(aggregationPayload.target).send(msg.value); // If can't swap, just send the recipient the gas asset
       
      snip...
```

When the `transferOutAndCallV5` or `batchTransferOutV5` function is called by a THORChain vault, the `_transferOutAndCallV5` internal function is triggered.

`_transferOutAndCallV5` first tries to call the aggregator to execute a swap. If the swap fails (e.g. when the actual amountOut is less than the specified `amountOutMin`), the control flow goes to the `if (!swapOutSuccess) {}` path.

> If can't swap, just send the recipient the gas asset

As indicated by the comment, in the above scenario, the gas assets should be sent directly to the recipient. However, instead of sending them to `aggregationPayload.recipient`, the code sends them to `aggregationPayload.target`, which is the address of the aggregator.

As a result, the asset are sent to the wrong address, preventing the recipient from receiving their rightful funds.


## Impact
Loss of funds, recipient not receiving the rightful funds.

## Tools Used
Manual review.

## Recommended Mitigation Steps
Fixing the wrong receiving address in `_transferOutAndCallV5`:
```diff
if (!swapOutSuccess) {
-         bool sendSuccess = payable(aggregationPayload.target).send(msg.value);
+         bool sendSuccess = payable(aggregationPayload.recipient).send(msg.value);
```


## Assessed type

ETH-Transfer
The THORChain Router is not compatible with ERC20 tokens that modify balances outside of transfers
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L156-L157


# Vulnerability details

## Vulnerability details

Some tokens may make arbitrary balance modifications outside of transfers (e.g. Ampleforth style rebasing tokens, Compound style airdrops of governance tokens, mintable/burnable tokens).

The THORChain Router contract uses the `_vaultAllowance` mapping to track token balances (i.e. vault allowances). Thus, arbitrary modifications to underlying token balances can mean that the contract is operating with outdated information.

If the actual token balance in the Router is less than the value tracked in `_vaultAllowance`, then `transferAllowance`, `transferOut`, `transferOutV5`, `transferOutAndCall`, `transferOutAndCallV5` could fail when the vault has enough allowance but the underlying token balance in the Router is insufficient for the transaction.

## Impact
ERC20 tokens that modify balances outside of transfers lead to broken functionality in the Router contract.

## Proof of Concept

The [Ampleforth](https://gitlab.com/thorchain/thornode/-/blob/develop/common/tokenlist/ethtokens/eth_mainnet_latest.json?ref_type=heads#L1250-1257) token is such a rebase token in the whitelist provided on the contest page.

After the user deposit the Ampleforth token, the transferred amount is recorded in `_vaultAllowance`. The Ampleforth token could then rebase negatively (see the [rebase history](https://www.coin-tools.com/ampl/ampl-rebase-history/), resulting in the underlying Ampleforth balance in the Router to be less than the amount stored in `_vaultAllowance`. Consequently, some vault allowance in the Router is unbacked by the underlying token, preventing the vaults from using their full allowance to transfer funds out.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Exclude ERC20 tokens that modify balances outside of transfers from the token whitelist.


## Assessed type

ERC20
`transferOutAndCallV5` and `batchTransferOutAndCallV5` emit the `TransferOutAndCallV5` event, which is not handled in Bifrost
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L304-L389
https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L391-L395
https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L397-L403
https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/bifrost/pkg/chainclients/shared/evm/smartcontract_log_parser.go#L166-L343


# Vulnerability details

## Impact
Unlike the `transferOutAndCall` functions, which emit the `TransferOutAndCall` event, The `transferOutAndCallV5` and `batchTransferOutAndCallV5` functions emit the `TransferOutAndCallV5` event.

However, the `TransferOutAndCallV5` event is not handled in the `GetTxInItem` function of the `smartcontract_log_parser.go` file (there's no case to handle it in the switch statement).
As a result, when a THORChain vault uses the `transferOutAndCallV5` or `batchTransferOutAndCallV5` function, it spend its allowance in the Router without the THORChain network acknowledging the outbound transfers.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Emit `TransferOutAndCall` instead of `TransferOutAndCallV5` in `transferOutAndCallV5` and `batchTransferOutAndCallV5`


## Assessed type

Other
`_transferOutAndCallV5` does not revert when the swap in aggregator fails, leading to loss of recipient's funds
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L362-L375


# Vulnerability details

## Impact
```solidity
  function _transferOutAndCallV5(
    TransferOutAndCallData calldata aggregationPayload
  ) private {

...snip...

    } else {
      _vaultAllowance[msg.sender][
        aggregationPayload.fromAsset
      ] -= aggregationPayload.fromAmount; // Reduce allowance

      // send ERC20 to aggregator contract so it can do its thing
      (bool transferSuccess, bytes memory data) = aggregationPayload
        .fromAsset
        .call(
          abi.encodeWithSignature(
            "transfer(address,uint256)",
            aggregationPayload.target,
            aggregationPayload.fromAmount
          )
        );

      require(
        transferSuccess && (data.length == 0 || abi.decode(data, (bool))),
        "Failed to transfer token before dex agg call"
      );

      // add test case if aggregator fails, it should not revert the whole transaction (transferOutAndCallV5 call succeeds)
      // call swapOutV5 with erc20. if the aggregator fails, the transaction should not revert
->      (bool _dexAggSuccess, ) = aggregationPayload.target.call{value: 0}(
        abi.encodeWithSignature(
          "swapOutV5(address,uint256,address,address,uint256,bytes,string)",
          aggregationPayload.fromAsset,
          aggregationPayload.fromAmount,
          aggregationPayload.toAsset,
          aggregationPayload.recipient,
          aggregationPayload.amountOutMin,
          aggregationPayload.payload,
          aggregationPayload.originAddress
        )
      );

...snip...
```

In the `_transferOutAndCallV5` function, the `_dexAggSuccess` return value from the call to aggregator's swapOutV5 is not checked.

Note that the `_vaultAllowance` is reduced and the `aggregationPayload.fromAsset` is transferred to the aggregator before the swap call.

Thus, if the swap in aggregator fails (e.g. the amountOut is less than the specified `amountOutMin`), the funds are left in the aggregator, and the recipient receives nothing.

Moreover, in the above scenario, an attacker can directly use the aggregator's swapOutV5 function to move the left funds out, effectively stealing the funds meant for ﻿`aggregationPayload.recipient`.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Check the `_dexAggSuccess` return value, and revert the transaction if `_dexAggSuccess` is false.


## Assessed type

call/delegatecall
`TransferOutAndCall` event is still emitted for failed `transferOutAndCall`, which will be observed by Bifrost
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L261-L293
https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L209-L238
https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L185-L207
https://github.com/code-423n4/2024-06-thorchain/blob/e5ae503d0dc2394a82242be6860eb538345152a1/ethereum/contracts/THORChain_Router.sol#L304-L341


# Vulnerability details

## Impact
```solidity
  function transferOutAndCall(
    address payable aggregator,
    address finalToken,
    address to,
    uint256 amountOutMin,
    string memory memo
  ) public payable nonReentrant {
    uint256 _safeAmount = msg.value;
    (bool erc20Success, ) = aggregator.call{value: _safeAmount}(
      abi.encodeWithSignature(
        "swapOut(address,address,uint256)",
        finalToken,
        to,
        amountOutMin
      )
    );
    if (!erc20Success) {
      bool ethSuccess = payable(to).send(_safeAmount); // If can't swap, just send the recipient the ETH
      if (!ethSuccess) {
->        payable(address(msg.sender)).transfer(_safeAmount); // For failure, bounce back to vault & continue.
      }
    }

->    emit TransferOutAndCall(
      msg.sender,
      aggregator,
      _safeAmount,
      finalToken,
      to,
      amountOutMin,
      memo
    );
  }
```

In the `transferOutAndCall` function, if the aggregator's swap fails and directly sending ETH to the recipient also fails (i.e. !erc20Success && !ethSuccess == true), the ETH is sent back to the vault.

In this case, the `transferOutAndCall` should be a no-op transaction, meaning it should have no side effects. 

However, it still emits a `TransferOutAndCall` event, which will be logged by the Bifrost. when Bifrost observes a `TransferOutAndCall` event, it can not tell whether the funds were transferred to the recipient or sent back to the vault. This could lead to a situation where the THORNChain network believes the recipient has received a fund when they were actually sent back to the vault, resulting in a loss of funds for the recipient.

The same issue also applies to the `transferOut` and `_transferOutV5` functions, which emit the `TransferOut` event even when ETH is sent back to the vault.
It could also apply to the `_transferOutAndCallV5` functions, which emit the `TransferOutAndCallV5` event when ETH is sent back to the vault.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Design a distinct event for the scenario where ETH is sent back to the vault, and handle it separately in Bifrost.


## Assessed type

Error
Send eth to the wrong person, which leads to errors.
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/main/ethereum/contracts/THORChain_Router.sol#L324


# Vulnerability details

## Impact
Send the eth to the aggregator instead of the user 'recipient'. In this way, the aggregator will have the eth and the user will not, this is not the desired behavior.

## Proof-of-concept
In the _transferOutAndCallV5() function, line 324, you send eth to the aggregator instead of the user.

    bool sendSuccess = payable(aggregationPayload.target).send(msg.value);

Unlike in the transferOutAndCall() function, line 278 where you send the eth to the user when swapOutV5() function fails:

    bool ethSuccess = payable(to).send(_safeAmount);


## Tools Used
Manual review

## Recommended Mitigation Steps
Change the line 324:

    -bool sendSuccess = payable(aggregationPayload.target).send(msg.value);
    +bool sendSuccess = payable(aggregationPayload.recipient).send(msg.value);














## Assessed type

Context
If calling `swapOutV5` would fail on target contract this could lead to loss of funds
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/e3fd3c75ff994dce50d6eb66eb290d467bd494f5/ethereum/contracts/THORChain_Router.sol#L324


# Vulnerability details

## Impact
Inside transferOutAndCallV5 there's a call to target contract with the intention to swap ETH/token to some other token, in case of ETH if swap would fail - router contract would try to send ETH to target contract, if contract has receive() function this would result in loss of funds.

## Proof of Concept
Example:
1. Alice calls `transferOutAndCallV5' with the intention of converting ETH to USDT and sending it to Bob.
2. Router calls swapOutV5 function on destination contract.
3. Swap fails due to slippage.
4. Router sends all of Alice's ETH to the target contract.
If contract has `receive` function implementation - Alice would lose all ETH she sent.

## Tools Used
Manual review.

## Recommended Mitigation Steps
Revert in case of unsuccessful swap or return ETH to recipient/msg.sender instead.


## Assessed type

ETH-Transfer