xanoutas/solidity-security-findings huggingface dataset:
Assets can be stolen after a swap attempt in `_transferOutAndCallV5()`
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

1. Loss of user funds

2. Profit for a malicious party

## Proof of concept

The `_transferOutAndCallV5()` is an internal function that is used in `transferOutAndCallV5()` and `batchTransferOutAndCallV5()` which allows a THOR Vault to transfer an amount of native coin or ERC20 tokens to a recipient by first swapping the corresponding amount using an aggregator.

Here is the code snippet responsible for swapping 1 asset to another before sending the output asset to the recipient :

[https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L338-L375](https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L338-L375)

```solidity
// 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
    )
);
```

Before the swap, the contract transfers the corresponding amount of tokens to the aggregator using a low-level call to `transfer()`.

*Now the aggregator holds the tokens.*

Once this call is successful, a low-level call to `swapOutV5()` is made on the aggregator. Because it is a low-level call, in case it fails, the transaction will not revert, which is intended as stated in the comments above.

*The transaction can revert for multiple reasons such as a lack of gas, the input and output assets being the same, insufficient amount of tokens due to fee-on-transfer...*

The issue here is that the tokens are still being held by the aggregator if the swap failed and a malicious party is now able to call the `swapOutV5()` and specify its own address as `recipient` to effectively steal the tokens that were intended by another user.

Even though the aggregator is not in the scope of the audit, the root cause of the vulnerability takes place in the `THORChain_Router` contract.

## Tools u
ETH is sent to the wrong address
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Inside the `_transferOutAndCallV5()`, if the function enters the first branch and the `swapOutV5` call fails, on line 324, a `send()` to the `aggregationPayload.target` address will be performed.

However, this address is the aggregator called in the place. The aggregator has a `receive()` so the call will not fail and the funds will be stuck inside the aggregator. If another user/ bot observes this and is faster than the user they can call themselves `swapOutV5` aggregator contract, which received those new funds, and get a free swap(minus the gas fees)

## Proof of Concept
1. User calls `transferOutAndCallV5()` in order to make a swap
2. The tx reverts inside the aggregator for some reason (the actual amount received after the swap is < `amountOutMin` param)
3. A call is made on line 324 to the `aggregationPayload.target` address. This is however not the intended recipient.
4. Another user or a bot observes this transfer of ETH and can easily call `swapOutV5` inside the aggregator and make a free swap

## Tools Used
Mannual review

## Recommended Mitigation Steps
Replace `payable(aggregationPayload.target)` with `payable(aggregationPayload.recipient)`


## Assessed type

ETH-Transfer
Native token is sent to the wrong address in `_transferOutAndCallV5()` leading to the theft of these assets
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

1. Loss of funds

2. Potential profit for a malicious party

## Proof of concept

The `_transferOutAndCallV5()` allows a Vault to swap native ETH to any ERC20 token supported by the protocol through the `swapOutV5()` function called on an aggregator.

[https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L304-L328](https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L304-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
            if (!sendSuccess) {
                payable(address(msg.sender)).transfer(msg.value); // For failure, bounce back to vault & continue.
            }
        }
```

In case the swap fails (out-of-gas or some obscure reasons), the protocol implements a "fallback" mechanism which consists of "just send the **recipient** the gas asset" as stated by the comment. 

In reality, the gas asset is transferred to the aggregator `aggregationPayload.target` not the recipient, which would be `aggregationPayload.recipient`.

If we abstract from the developers comment and the actual intention was to send the gas asset to the aggregator, another issue occurs : the gas assets will remain on the aggregator contract.

This allows anyone to call the `swapOutV5()` function on the aggregator to swap the gas assets to another token resulting in these being effectively stolen from the legitimate user it belonged to.

In both scenarios, the current implementation introduces a vulnerability for the users of the protocol.

## Tools used

Manual review

## Recommended mitigation steps

Send the gas asset to `aggregationPayload.recipient` rather than `aggregationPayload.target`.

Doing so will result in the legitimate user to receive its funds even if the swap failed.



## Assessed type

ETH-Transfer
When dealing with native coin, the `TransferOut*` events are still triggered on error
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L196
https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L213
https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L280
https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L326


# Vulnerability details

## Impact

The **Bifrost** might intercept events while nothing actually happened on the blockchain, no state change whatsoever.

Actions might be overtaken by **Bifrost** once it intercepts such a `TransferOut*` event, which could mess up internal accounting or lead to unexpected behaviors.

## Proof of concept

The file `smartcontract_log_parser.go` is responsible for intercepting events emitted by `THORChain_Router.sol`, notably `TransferOut()` (corresponding to the `transferOutEvent` variable in the `go` file) and `TransferOutAndCall()` (corresponding to the `transferOutAndCallEvent` variable in the `go` file) .

These events are parsed later on in a `switch` statement which triggers internal mechanisms to take it into account.

There is a scenario in which nothing actually happens on the blockchain but the event is still emitted, which could lead to unexpected behaviors.

Consider the `transferOutAndCall()` function and the following scenario :

[https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L261-L293](https://github.com/code-423n4/2024-06-thorchain/blob/main/chain/ethereum/contracts/THORChain_Router.sol#L261-L293)

- a low-level call to the `swapOut()` function of the aggregator is made to swap native coin to an ERC20 before sending them to the recipient


```solidity
uint256 _safeAmount = msg.value;
(bool erc20Success, ) = aggregator.call{value: _safeAmount}(
    abi.encodeWithSignature(
        "swapOut(address,address,uint256)",
        finalToken,
        to,
        amountOutMin
    )
);
```

- the previous fails (for some reason) and as a fallback, the native coin is sent directly to the recipient

```solidity
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.
    }
}
```

- the native coin transfer also fails (for some other reason) so these coins are sent back to the caller (e.g. the vault)

```solidity
if (!ethSuccess) {
    payable(address(msg.sender)).transfer(_safeAmount); // For failure, bounce back to vault & continue.
}
```

- at this point, the state of the blockchain has not changed, there was only a native coin transfer from the vault to the router contract which sent these native coin back to the vault again

- because the transaction did not revert, the `TransferOutAndCall()` event is still t
Incorrect Event Emission due to Ether Transfer Failure in THORChain Router Contract
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/e3fd3c75ff994dce50d6eb66eb290d467bd494f5/chain/ethereum/contracts/THORChain_Router.sol#L185-L207
https://github.com/code-423n4/2024-06-thorchain/blob/e3fd3c75ff994dce50d6eb66eb290d467bd494f5/chain/ethereum/contracts/THORChain_Router.sol#L209-L229
https://github.com/code-423n4/2024-06-thorchain/blob/e3fd3c75ff994dce50d6eb66eb290d467bd494f5/chain/ethereum/contracts/THORChain_Router.sol#L261-L293
https://github.com/code-423n4/2024-06-thorchain/blob/e3fd3c75ff994dce50d6eb66eb290d467bd494f5/chain/ethereum/contracts/THORChain_Router.sol#L304-L340


# Vulnerability details

The `THORChain_Router` contract has several instances where Ether transfer failures are not correctly handled, resulting in the emission of incorrect events. This can cause downstream systems, such as `smartcontract_log_parser.go`, to incorrectly process these events.

## Impact
The incorrect emission of events due to Ether transfer failures can lead to significant issues in downstream processing. Specifically, the `smartcontract_log_parser.go` component, which is designed to parse and take appropriate actions based on these events, will process the wrong events. This can result in incorrect accounting within the Thorchain ecosystem.

## Proof of Concept
The issue occurs in the following instances within the `THORChain_Router` contract:

1. `transferOut()`

```solidity
    function transferOut(
      address payable to,
      address asset,
      uint amount,
      string memory memo
    ) public payable nonReentrant {
      uint safeAmount;
      if (asset == address(0)) {
        safeAmount = msg.value;
        bool success = to.send(safeAmount); // Send ETH.
        if (!success) {
  @>>     payable(address(msg.sender)).transfer(safeAmount); // For failure, bounce back to vault & continue.
        }
      } else {
      // <!-- SKIP -->
      }
  @>> emit TransferOut(msg.sender, to, asset, safeAmount, memo);
    }
```

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

2. `_transferOutV5()`

```solidity
    function _transferOutV5(TransferOutData memory transferOutPayload) private {
      if (transferOutPayload.asset == address(0)) {
        bool success = transferOutPayload.to.send(transferOutPayload.amount); // Send ETH.
        if (!success) {
  @>>     payable(address(msg.sender)).transfer(transferOutPayload.amount); // For failure, bounce back to vault & continue.
        }
      } else {
        // <!-- SKIP -->
      }

  @>> emit TransferOut(
        msg.sender,
        transferOutPayload.to,
        transferOutPayload.asset,
        transferOutPayload.amount,
        transferOutPayload.memo
      );
    }
```

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

3. `transferOutAndCall()`

```solidity
`Fund stuck forever` in vault in case of multiple deposits to different vaults
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/main/bifrost/pkg/chainclients/shared/evm/smartcontract_log_parser.go#L182-L212


# Vulnerability details

## Description
While removing whitelisting will open the door for more composability with THORChain, the current `SmartContractLogParser::GetTxInItem` implementation seems `too rigid` to leverage such composability, at least in regard to the `Deposit event`, which seems to warrant `High` severity as `user funds loss` could occurs.

Imagine a dApp that integrates with THORChainRouter (as possible in the future with no whitelisting) decides to make multiple deposits in a `single transaction` which would be as follow:
```
EOA --> DepositAggregatorDapp::depositInBatch --> THORChainRouter::depositWithExpiry(Vault1, Asset1, ...)
                                              \-> THORChainRouter::depositWithExpiry(Vault1, Asset2, ...)
                                              \-> THORChainRouter::depositWithExpiry(Vault2, Asset2, ...)
                                              \-> THORChainRouter::depositWithExpiry(Vault3, Asset3, ...)
```
1) This would fall into the new path, as `DepositAggregatorDapp` is a smart contract call, but not directly the router. And since those events are generated by the router, it would pass the [main gate](https://github.com/code-423n4/2024-06-thorchain/blob/main/bifrost/pkg/chainclients/shared/evm/smartcontract_log_parser.go#L177).
```
		case evm.IsSmartContractCall(tx, receipt):
			// Tx to a different contract, attempt to parse with max allowable logs
			return e.getTxInFromSmartContract(tx, receipt, int64(e.cfg.MaxContractTxLogs))
```			


2) Inside `GetTxInItem`, the first deposit event would be processed properly (`depositWithExpiry(Vault1, Asset1, ...`), but the 2nd event processed would fail the following condition, which would cause the function to abort and return an error. This is due to the fact that in a same transaction, there would be multiple deposit event that goes to `different vaults`, which granted doesn't make sense in a whitelisted world, but seems to make sense in a non-whitelisted world. This would means that those deposits event would not be accounted for, thus not sent to THORChain, which is effectively is a `user funds loss` for the user as those funds would be stuck there forever, like a donation.
```
			if len(txInItem.To) > 0 && !strings.EqualFold(txInItem.To, depositEvt.To.String()) {
				return false, fmt.Errorf("multiple events in the same transaction, have different to addresses , ignore")
			}
```
			
```
func (scp *SmartContractLogParser) GetTxInItem(logs []*etypes.Log, txInItem *types.TxInItem) (bool, error) {
	if len(logs) == 0 {
		scp.logger.Info().Msg("tx logs are empty return nil")
		return false, nil
	} else if int(scp.maxLogs) > 0 && len(logs) > int(scp.maxLogs) {
		scp.logger.Info().Msgf("tx logs are too many, ignore")
		return false, nil
	}
	isVaultTransfer := false
	for _, item := range logs {
		//
`batchTransferOutV5` could emit multiple `TransferOut` events, but Bifrost Observation can handle only one per transaction.
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#L247-L253
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/bifrost/pkg/chainclients/shared/evm/smartcontract_log_parser.go#L166-L343


# Vulnerability details

## Impact
The `batchTransferOutV5` function is the batch version of the `transferOutV5` function. When provided with an array of `TransferOutData`, the `batchTransferOutV5` function can emit multiple `TransferOut` events in one transaction.

However, in the `GetTxInItem` function in `smartcontract_log_parser.go`, if there are multiple `transferOutEvent`s in the `logs` array, the information in `txInItem` is overwritten during the iteration of the last `transferOutEvent`. Consequently, only the last `transferOutEvent` is logged by Bifrost (its `txInItem` populated by `GetTxInItem`), and all previous ones are ignored.

As a result, when a THORChain vault uses `batchTransferOutV5`, their allowance could be spent without the THORChain network acknowledging the outbound from the vault, leading to a loss of funds for the THORChain vault.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Modify the event logging logic in `smartcontract_log_parser.go` to accommodate the use of `batchTransferOutV5`.


## Assessed type

Error
The `TransferOutAndCallV5` event is not caught by `smartcontract_log_parser.go`
Audit: 2024-06-thorchain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-thorchain/blob/main/bifrost/pkg/chainclients/shared/evm/smartcontract_log_parser.go#L22-L26
https://github.com/code-423n4/2024-06-thorchain/blob/main/bifrost/pkg/chainclients/shared/evm/smartcontract_log_parser.go#L295-L337


# Vulnerability details

## Impact

The contract and more precisely the events of the version 5 of the protocol are not integrated as expected.

Actions that should be undertaken by the **Bifrost** are ignored, leading to inconsistencies in the entire protocol such as accounting errors and potential loss of funds.

## Proof of concept

In the previous version of the protocol, the `smartcontract_log_parser.go` was responsible for listening to multiple events emitted by `THORChain_Router.sol` :

[https://github.com/code-423n4/2024-06-thorchain/blob/main/bifrost/pkg/chainclients/shared/evm/smartcontract_log_parser.go#L22-L26](https://github.com/code-423n4/2024-06-thorchain/blob/main/bifrost/pkg/chainclients/shared/evm/smartcontract_log_parser.go#L22-L26)

```go
const (
	NativeTokenAddr         = "0x0000000000000000000000000000000000000000"
	depositEvent            = "0xef519b7eb82aaf6ac376a6df2d793843ebfd593de5f1a0601d3cc6ab49ebb395" // cast keccak "Deposit(address,address,uint256,string)"
	transferOutEvent        = "0xa9cd03aa3c1b4515114539cd53d22085129d495cb9e9f9af77864526240f1bf7" // cast keccak "TransferOut(address,address,address,uint256,string)"
	transferAllowanceEvent  = "0x05b90458f953d3fcb2d7fb25616a2fddeca749d0c47cc5c9832d0266b5346eea" // cast keccak "TransferAllowance(address,address,address,uint256,string)"
	vaultTransferEvent      = "0x281daef48d91e5cd3d32db0784f6af69cd8d8d2e8c612a3568dca51ded51e08f" // cast keccak "VaultTransfer(address,address,(address,uint256)[],string)"
	transferOutAndCallEvent = "0x8e5841bcd195b858d53b38bcf91b38d47f3bc800469b6812d35451ab619c6f6c" // cast keccak "TransferOutAndCall(address,address,uint256,address,address,uint256,string)"
)
```

Once one of these events has been intercepted, it is parsed and actions are undertaken by **Bifrost** depending on the nature of the emission.

One of these actions is `transferOutAndCallEvent`, described [here](https://github.com/code-423n4/2024-06-thorchain/blob/main/bifrost/pkg/chainclients/shared/evm/smartcontract_log_parser.go#L295-L337) and is triggered in the `transferOutAndCall()` function of the smart contract.

The version 5 of the protocol introduced a similar event in a new function of the smart contract called `_transferOutAndCallV5()` which emits `TransferOutAndCallV5`.

As you can see, this particular event does not figure in the list of events in `smartcontract_log_parser.go` and is thus never intercepted nor processed while it should be.

## Tools used

Manual review

## Recommended mitigation steps

Define the corresponding event in `smartcontract_log_parser.go` and implement the actions to undertake when it is intercepted.



## Assessed type

Context