xanoutas/solidity-security-findings huggingface dataset:
`_reimburse()` bypasses 1155 accounting, allowing potential fund drainage.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L144-L149
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L148


# Vulnerability details

## Impact
The `_reimburse()` function allows the caller to withdraw any remaining source delegate balance back to themselves. This bypasses the 1155 token balances and could let the caller drain funds.

For example:
The `_reimburse()` function allows the caller to withdraw any leftover amount back to themselves.

```solidity
// User has 10 tokens in source delegate proxy contract

// Call _reimburse() and withdraw 15 tokens 
_reimburse(sourceDelegate, 15)

// Withdraws 10 tokens from proxy + 5 extra from user's own wallet
// Bypasses 1155 token accounting
```
This could let an attacker drain funds by inflating the withdrawal amount.

## Proof of Concept

**The concerning code for [the _reimburse() function is](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L144-L149).**
```solidity
function _reimburse(address source, uint256 amount) internal {

  // Transfer the remaining source amount or the full source amount
  // (if no remaining amount) to the delegator
  address proxyAddressFrom = retrieveProxyContractAddress(token, source);
  token.transferFrom(proxyAddressFrom, msg.sender, amount);

}
```

Specifically, the **[token.transferFrom()](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L148)** call on line 148 is the main issue. 

- `_reimburse()` is called if there are leftover source amounts after processing `_delegateMulti()`.

- It transfers the remaining source amount back to `msg.sender`.

- **However, it uses `token.transferFrom()` instead of updating 1155 balances.**

- **This lets `msg.sender` withdraw more than their delegate balance.**

For example:

```solidity
// User has 10 tokens delegated to SourceProxy 

// Call _reimburse(SourceProxy, 15)

// Drains 15 tokens from SourceProxy
// Bypasses 1155 accounting which would cap at 10 tokens
```

This allows the draining of excessive funds. A better design would be to integrate with the 1155 token accounting.

> The core issue with `_reimburse()` occurs on line 148:

```solidity
token.transferFrom(proxyAddressFrom, msg.sender, amount);
```

This transfers tokens from the source proxy contract directly to `msg.sender`.

The full `_reimburse()` function is:

```solidity
function _reimburse(address source, uint256 amount) internal {

  address proxyAddressFrom = retrieveProxyContractAddress(token, source);
  
  token.transferFrom(proxyAddressFrom, msg.sender, amount); 

}
```
To understand why this is an issue, we need to look at the context:

- This contract inherits from ERC1155 to manage token balances based on the source/target delegate `tokenIds`.
Multi-delegate system opens exploits: vote amplification, collusion, rapid alternation, obscuring bribes.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L57-L63
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L65-L100


# Vulnerability details

## Impact

Allowing votes to be spread across multiple delegates does potentially open up new attack vectors if not handled carefully.

Some ways this could potentially be exploited:

- A `delegator` spreads their votes thin across many dummy delegates they control to obtain more voting power. 

- Attackers collude by delegating votes in circular loops to multiply their voting power.

- Malicious delegators artificially inflate voting power of certain delegates by splitting and delegating to themselves.

- `Delegators` quickly alternate delegates right before votes to double vote or confuse tallying. 

- `Delegators` split votes to many delegates to make vote buying/bribing more difficult to detect.

While the multi-delegate model provides flexibility, it could allow new vote manipulation tactics if precautions aren't taken.

## Proof of Concept

Here are lines of code that enable the multi-delegate functionality that could potentially be exploited:

The main entry point is the **[delegateMulti function](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L57-L63)**:

```solidity
function delegateMulti(
  uint256[] calldata sources,
  uint256[] calldata targets, 
  uint256[] calldata amounts
) external {
  _delegateMulti(sources, targets, amounts);
}
```
This allows passing in multiple source and target delegates.

The logic that enables delegating to multiple delegates is in **[_delegateMulti](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L65-L100)**:

```solidity 
function _delegateMulti(
  uint256[] calldata sources,
  uint256[] calldata targets,
  uint256[] calldata amounts
) internal {

  // Loop through source delegates
  for(uint i = 0; i < sources.length; i++) {
    
    // Transfer delegation for this source
    _processDelegation(sources[i], targets[i], amounts[i]) 

  }

  // Loop through target delegates
  for(uint i = 0; i < targets.length; i++) {
  
    // Transfer delegation for this target
    _processDelegation(sources[i], targets[i], amounts[i])
  
  }

}
```
This logic allows delegating each source to different targets in the same call. 

The risks stem from the lack of validation on the arrays of delegates passed in. Checks need to be added here to prevent the exploits mentioned in the impact.

This opens up vulnerabilities such as:

- A delegator passes a long list of delegate contracts they control to split their votes. This grants them amplified voting power.

- Attackers make circular delegations like A -> B, B -> C, C -> A to multiply voting power each cycle.

- A malicious delegator
Assertions and array length checks can be manipulated maliciously, causing issues.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L79-L82
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L62
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L57-L63
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L85-L89
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L110-L116
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L110-L116
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L110-L116


# Vulnerability details

## Impact

There are some assertions and requirements on array lengths to prevent mismatched data.

But assertions like require statements can potentially be used maliciously in some cases. 

These are a few ways a malicious user could try to exploit the asserts, because:

- They could purposefully pass in mismatching array lengths to trigger the asserts and repeatedly call `delegateMulti` to waste gas and block other transactions.

- They could pass very long arrays to hit gas limits, blocking transactions.

- If there is any unprotected arithmetic done on the array lengths, they could pass values that overflow/underflow causing unexpected side effects.

## Proof of Concept

Here are the lines related to the array length assertions that could be exploited:

**[Mismatched array lengths assert](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L79-L82)**:

```solidity
// Line 58
require(
    Math.max(sourcesLength, targetsLength) == amountsLength,
    "Delegate: The number of amounts must be equal to the greater of the number of sources or targets"
);
```

This could be triggered by passing mismatching source/target/amount array lengths.

Gas limit array length **[asserts](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L62)**:

```solidity
// Line 62
_delegateMulti(sources, targets, amounts); 
```

The **[delegateMulti](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L57-L63)** function calls `_delegateMulti` which contains the loop over the array lengths. Passing very long arrays here could cause gas limit issues.

**[Arithmetic on array lengths](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L85-L89)**:

```solidity
// Line 85 - 88 
for (
    uint transferIndex = 0;
    transferIndex < Math.max(sourcesLength, targetsLength);
Tokens could be stolen by specifying an unauthorized address.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L163-L171
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L170
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L163-L171


# Vulnerability details

## Impact

`transferBetweenDelegators()` transfers tokens between proxy contract addresses without any validation. Tokens could be stolen by specifying an unauthorized address.

## Proof of Concept

The issue with **[transferBetweenDelegators()](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L163-L171)** occurs here as we can see:

```solidity
function transferBetweenDelegators(
  address from,
  address to,
  uint256 amount  
) internal {

  address proxyAddressFrom = retrieveProxyContractAddress(token, from);
  address proxyAddressTo = retrieveProxyContractAddress(token, to);

  token.transferFrom(proxyAddressFrom, proxyAddressTo, amount);

}
```

Specifically this line: **[#L170](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L170)**
 
```solidity
token.transferFrom(proxyAddressFrom, proxyAddressTo, amount);
```

This transfers `amount` tokens between the `proxyAddressFrom` and `proxyAddressTo` contract addresses. 

The problem is there is no validation that `from` and `to` are authorized delegators for the `msg.sender`.

An attacker could call this with:

```solidity
from = attackerProxyAddress 
to = victimProxyAddress
amount = 1,000,000 tokens
```

And arbitrarily transfer tokens between any two proxy contract addresses without approval.

To clarify the security issue, the `transferBetweenDelegators()` function is used to transfer tokens between two proxy contract addresses that represent delegates. For example:

```solidity
Address A: Proxy contract for Delegate 1 
Address B: Proxy contract for Delegate 2

transferBetweenDelegators(A, B, 100);
// Transfers 100 tokens from Delegate 1 to Delegate 2
```

The problem occurs because **transferBetweenDelegators()** relies on the caller to specify the `from` and `to` addresses:

```solidity
function transferBetweenDelegators(
   address from, 
   address to,
   uint amount
) internal {

  address proxyFrom = getProxyAddress(from);
  address proxyTo = getProxyAddress(to);
  
  token.transferFrom(proxyFrom, proxyTo, amount);

}
```

There is no validation that the `from` and `to` addresses are authorized delegates belonging to the caller.

This means any user could call `transferBetweenDelegators()` with:

```solidity
from = AttackerProxyAddress
to = VictimProxyAddress 
amount = 1,000,000 tokens
```

And steal tokens from Victim's delegate and transfer `to` their own delegate, without approval.

> Proof is important to validate se
Unrestricted delegator contract deployment risks gas abuse; implement mitigations.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L188
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L186
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L206-L213
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L188


# Vulnerability details

## Impact

`deployProxyDelegatorIfNeeded()` deploys a new delegator contract if one does not exist. This could be abused to spam deploy many delegators and waste gas.

## Proof of Concept

The issue with potentially spam deploying many delegator contracts occurs in the **[deployProxyDelegatorIfNeeded()](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L188)** function:

```solidity
function deployProxyDelegatorIfNeeded(
    address delegate
  ) internal returns (address) {

  // ...

  // if the proxy contract has not been deployed, deploy it
  if (bytecodeSize == 0) {
    new ERC20ProxyDelegator{salt: 0}(token, delegate); 
    emit ProxyDeployed(delegate, proxyAddress);
  }

  // ...

}
```

Specifically on this line: **[#L186](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L186)**

```solidity 
new ERC20ProxyDelegator{salt: 0}(token, delegate);
```
This deploys a new `ERC20ProxyDelegator` contract if one does not already exist for the given delegate. 

> **The concern is that this function can be called repeatedly with unique delegate addresses, and it will deploy a new delegator contract each time. There is no limit on how many delegators can be deployed.**

An attacker could exploit this to spam deploy endless delegator contracts and waste gas.

The `deployProxyDelegatorIfNeeded()` function is used to deploy a new ERC20ProxyDelegator contract for a specified delegate address if one does not already exist. 

It checks if a contract has already been deployed by looking at the code size at the deterministic address calculated from the delegate address: https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L206-L213

```solidity
bytes32 hash = keccak256(
 abi.encodePacked(  
  bytes1(0xff),
  address(this), 
  uint256(0), // salt
  keccak256(bytecode)
  )
);
```

The fact that there is no limitation on how many unique delegate addresses can be passed to this function, and it will deploy a new delegator contract each time if one does not already exist.

An attacker could exploit this in the following way:

1. Generate a large number of unique delegate addresses 
2. Repeatedly call `deployProxyDelegatorIfNeeded()` with each delegate address
3. If a delegator does not exist for that
Lack of array length validation in `delegateMulti()` can lead to errors.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L65-L72


# Vulnerability details

## Impact

There is no validation that the `sources`, `targets`, and `amounts` arrays passed to `delegateMulti()` are the same length. This could cause issues with iterating through the arrays.

## Proof of Concept

**[In the delegateMulti() function](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L65-L72)**.

```solidity
function delegateMulti(
    uint256[] calldata sources,  
    uint256[] calldata targets,
    uint256[] calldata amounts
) external {

    // ...

    uint256 sourcesLength = sources.length;
    uint256 targetsLength = targets.length; 
    uint256 amountsLength = amounts.length;

    // ...
}
```

The `delegateMulti()` function accepts three array parameters - sources, targets, and amounts. These arrays represent the source delegates, target delegates, and token amounts for each delegation transfer. 

The function loops through and processes a delegation transfer for each index in the arrays up to the maximum length of `sourcesLength`, `targetsLength` and `amountsLength`.

The issue is there is no validation that `sourcesLength`, `targetsLength` and `amountsLength` are equal before looping.

For example:

- sources = [1, 2, 3]  (length = 3)
- targets = [4, 5] (length = 2) 
- amounts = [100, 200, 300] (length = 3)

In this case, `sourcesLength` is 3, `targetsLength` is 2, and `amountsLength` is 3.

When looping through the delegation transfers, it will attempt to process 3 iterations because `Math.max(3, 2) = 3`.

On the 2nd loop, it will try to access `targets[2]` to get the target delegate address. But targets array only has 2 elements. So this would cause an out of bounds error and invalid memory access.

Similarly on the 3rd loop, there would be no target delegate address, leading to unexpected behavior.

See this example that would cause an issue:

```solidity
sources = [1, 2]  // length = 2 
targets = [3] // length = 1
amounts = [100, 200] // length = 2

When looping:

  i = 0 // OK
  i = 1 // Out of bounds on targets[1]
```

This shows how having different array lengths passed in can lead to out of bounds errors when iterating.

We can see the lack of array length validation in `delegateMulti()` combined with a provided example of mismatched arrays confirms there is a high risk of errors during processing.

## Tools Used

Manual Review

## Recommended Mitigation Steps

There should be validation added here to check that the sources, targets, and amounts arrays are the same length before processing further. For example:

```solidity
require(sourcesLength == targetsLength && targetsLength == amountsLength, "Array length mismatch"); 
```

This will prevent potential errors from iterating through arrays of different lengths in the loop below. Adding validation of array lengths being e
Unbounded approval in `ERC20ProxyDelegator` allows potential token theft; limit approvals.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L16-L18


# Vulnerability details

## Impact

The proxy contract `ERC20ProxyDelegator` approves maximum possible tokens to the `ERC20MultiDelegate` contract. This could allow the `ERC20MultiDelegate` to steal all tokens from the proxy contracts. The approval amount should be limited to only what is required.

## Proof of Concept

In the `ERC20ProxyDelegator` constructor on **[lines 15-18](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L16-L18)**

```solidity
constructor(ERC20Votes _token, address _delegate) {

    // Approve maximum uint256 tokens
  _token.approve(msg.sender, type(uint256).max);
  
  _token.delegate(_delegate);

}
```

Specifically, line 17 is approving the `ERC20MultiDelegate` contract (`msg.sender`) for the maximum `uint256` amount of tokens.

This allows `ERC20MultiDelegate` to drain all tokens from the `ERC20ProxyDelegator` contract at any time using `transferFrom`. 

Here are the key details:

The core issue is in the `ERC20ProxyDelegator` constructor:

```solidity 
constructor(ERC20Votes _token, address _delegate) {

  // Approve maximum uint256 tokens
  _token.approve(msg.sender, type(uint256).max); 
  
  _token.delegate(_delegate);

}
```

Specifically, line 17 that approves the maximum uint256 amount of tokens to the `ERC20MultiDelegate` contract (`msg.sender`). 

When `ERC20ProxyDelegator` is deployed, it sets `ERC20MultiDelegate` as an approved spender for the maximum possible tokens.

This allows `ERC20MultiDelegate` to drain the entire token balance from any `ERC20ProxyDelegator` at any time using `transferFrom`.

In summary:

- Constructor approves max tokens to `ERC20MultiDelegate` 
- Allows theft of any amount via `transferFrom`
- Approval amount should be limited to delegated amount

You can see how the unbounded approval enables theft of all tokens in any `ERC20ProxyDelegator`. 

## Tools Used

Manual Review

## Recommended Mitigation Steps

A better approach would be to set the approval amount equal to the delegation amount:

```solidity
constructor(ERC20Votes _token, address _delegate, uint _amount) {

  _token.approve(msg.sender, _amount);
  
  // ...

}
```
And pass the delegation amount when deploying `ERC20ProxyDelegator`.

This restricts the approved amount to only what is needed for the delegation.


## Assessed type

ERC20
Possibility of losing users funds due to not checking for zero address (address 0x0)
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L62
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L74-L77


# Vulnerability details


## Impact

As there is no function for checking whether the addresses of the sources and targets are zero or not in the delegateMulti function it may cause issues for users 
Sending tokens to a zero address (address 0x0) is generally not a recommended practice in Ethereum, as it results in the destruction of those tokens, making them irrecoverable. In the context of the contract you've shared, which involves transferring tokens between delegates, if the contract doesn't have proper checks to prevent sending tokens to the zero address, it can lead to unintended token destruction.
This can cause loss of the users funds!!

## Proof of Concept

As you can see in the provided code there's no check for (address 0x0) in the delegateMulti and it passes the args to the _delegateMulti => _processDelegation
So this can lead to having a Zero address cause loss of the users funds

    function delegateMulti(
        uint256[] calldata sources,
        uint256[] calldata targets,
        uint256[] calldata amounts
    ) external {
        // doesnt check for address zero and balance
        _delegateMulti(sources, targets, amounts);
    }

Instead of using || we should use &&   
As in this statement there's no need to get true in both sides (true || true ) and having a ture in one side can satisfy the function. So this can lead to having a Zero address cause loss of the users funds

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L74-L77


            require(
            sourcesLength > 0 || targetsLength > 0,
            "Delegate: You should provide at least one source or one target delegate"
        );

   


## Tools Used

Manual review 

## Recommended Mitigation Steps

we should use a require function in the delegateMulti function to check if the address provided by the user are valid or not with a good error handling and if everything was Ok call the _delegateMulti

    function _delegateMulti(
        uint256[] calldata sources,
        uint256[] calldata targets,
        uint256[] calldata amounts
    ) internal {
        // Perform a check to prevent transfers to the zero address
        require(targets.length == 0 && !containsZeroAddress(targets), "Delegate: Invalid target address");

        // Rest of the delegation transfer logic...
    }


and ...

            require(
            sourcesLength > 0 && targetsLength > 0,
            "Delegate: You should provide at least one source or one target delegate"
        );



## Assessed type

Other