xanoutas/solidity-security-findings huggingface dataset:
Un-sanitized input arrays to delegateMulti can lead to unintended results, errors, and potential vulnerabilites
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In the **delegateMulti** flow, when the **_processDelegation** logic path is followed, there is a wrong assumption that just because the condition **Math.max(sourcesLength, targetsLength) == amountsLength** is checked for, that the input arrays of sources and targets will be in sequentially one-to-one order element-wise with other input array against which they will be processed alongside (ex. when  **_reimburse(source, amount)** is called, the current element source of the sources array blindly expects the current element amount of the amounts array to be the correct value it should correspond to), with any valid elements in the amounts array starting at index 0 and ending at index amounts.length. Similar effects can occur in the case of the **_reimburse** and **createProxyDelegatorAndTransfer** logic paths within the **delegateMulti** function

When delegating or re-delegating, the targets array is front- or back-padded with zero addresses and amounts array is front- or back-padded with extra values, where **targets.length > sources.length** and **sources.length > 0** and **targets.length == amounts.length**.

When running the **delegateMulti** flow, the logic does not take into account the input arrays may be sloppily-organized elements. In the case illustrated in the test I wrote to exploit this issue, targets array has one (or more) elements at the back of the array that are invalid, and amounts array has one (or more) elements at the front of the array that are superfluous. 

Because of these offsets and the logic not doing inputs sanitization to shift-and-resize input arrays in such a case or remove invalid values from the input arrays, which can happen if an honest actors makes a mistake creating input arrays or if a bad actor calls the function with faulty input arrays to cause problems in the contract flow or trigger some exploit.

Being that **delegateMulti** is an external method, anyone can call this method and there’s no guarantee that they prepare their input parameters properly. Intentional or unintentional addition of elements with invalid values, elements in one input array “not lined up correctly” with a complementary input array due to shifted and/or invalid values, and other potential problems can arise from non-thorough input processing in an external function.
## Proof of Concept
In such a case, as in the following test I’ve added:

**FAIL: Re-delegating multiple delegates with targets back-padded with zero addresses and amounts front-padded with extra values should exceed balance**

the resulting error is:

**Error: VM Exception while processing transaction: reverted with reason string 'ERC20: insufficient allowance'**

when the logic flow eventually goes from **ERC20MultiDelegate._processDelegation -> ERC20MultiDelegate.transferBetweenDelegators -> ENSToken.transferFrom -> ENSToken
Internal `_mintBatch` and `_burnBatch` may expose reentrancy risk.
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L110-L112
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L113-L115
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L7


# Vulnerability details

## Impact

`_mintBatch` and `_burnBatch` calls could be vulnerable to reentrancy attacks if the ERC1155 implementation they rely on is not reentrancy-safe.

## Proof of Concept

These internal ERC1155 functions are used to mint and burn tokens in bulk as part of the delegation transfer process. 

The `_mintBatch` and `_burnBatch` calls occur on:

**[ERC20MultiDelegate.sol#_burnBatch](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L110-L112)**

**[ERC20MultiDelegate.sol#_mintBatch](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L113-L115)**

```solidity
if (sourcesLength > 0) {
  _burnBatch(msg.sender, sources, amounts[:sourcesLength]); 
}


if (targetsLength > 0) {
  _mintBatch(msg.sender, targets, amounts[:targetsLength], "");
}
```

The risk is that if the underlying ERC1155 implementation is not reentrancy-safe, an attacker could exploit these to trigger reentrancy and drain funds. 

For example, by calling `delegateMulti` with malicious inputs, an attacker could recursively call back into `delegateMulti` via `_mintBatch` before the state has been updated.

The `_mintBatch` and `_burnBatch` functions are internal functions inherited from the OpenZeppelin ERC1155 contract implementation that is imported here: **[Line 7](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L7)**

```solidity
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
```

These ERC1155 functions allow minting and burning multiple tokens in a single call, avoiding the need to loop token by token. 

Here is the relevant code from OpenZeppelin's ERC1155:

```solidity
// @openzeppelin/contracts/token/ERC1155/ERC1155.sol

function _mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal virtual {

  require(to != address(0), "ERC1155: mint to the zero address");

  address operator = _msgSender();

  _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

  for (uint256 i = 0; i < ids.length; ++i) {
    _balances[ids[i]][to] += amounts[i];
  }

  emit TransferBatch(operator, address(0), to, ids, amounts);

  _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
}

function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual {

  require(from != address(0), "ERC1155: burn from the zero address");

  address operato
Using `extcodesize` can falsely deploy proxies for regular EOA accounts.
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L179-L182
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L185-L188
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L190
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L179-L182


# Vulnerability details

## Impact

Logic issue In `deployProxyDelegatorIfNeeded`, it checks if a proxy contract has already been deployed by checking the code size. However, this could falsely return 0 if the account is a regular EOA. 

## Proof of Concept

The code size check occurs on:

**[ERC20MultiDelegate.sol#uint bytecodeSize](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L179-L182)**

**[ERC20MultiDelegate.sol#if (bytecodeSize == 0)](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L185-L188)**

```solidity

uint bytecodeSize;
assembly {
  bytecodeSize := extcodesize(proxyAddress)  
}


if (bytecodeSize == 0) {
  // deploy proxy
}
```

It is checking the code size at the computed proxy contract address to see if it contains any bytecode. 

The issue is that this would return 0 even if `proxyAddress` resolves to a regular EOA account without code.

Let me walk through it in more detail:

**[The deployProxyDelegatorIfNeeded function](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L190)** is intended to check if a proxy contract has already been deployed for a particular delegate address. If not, it will deploy a new ERC20ProxyDelegator contract for that delegate.

It does this check by:

1. Computing the expected address for the proxy contract based on the delegate address, contract address, and a salt.

2. Checking the code size at that address using assembly: **[#Line 179 - 181](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L179-L182)**

```solidity
// Inside deployProxyDelegatorIfNeeded

uint bytecodeSize; 
assembly {
  bytecodeSize := extcodesize(proxyAddress)
}
```

3. If the code size is 0, it assumes no contract is deployed and proceeds to deploy one.

The issue is that extcodesize can return 0 even if proxyAddress is just a regular EOA account. This means it could end up deploying proxy contracts for EOAs that aren't actually delegatees.

For example:

- User's EOA address is 0x1234 
- Computed proxy address resolves to 0x1234
- extcodesize(0x1234) returns 0 (it's just a user's account)
- Contract assumes nothing deployed and deploys a new proxy

Here is some code that shows how relying only on `extcodesi
Missing array length validation in `_delegateMulti` could lead to errors.
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

 ## Impact

There is no validation on the source, target, and amounts arrays in `_delegateMulti()`. This could lead to errors if they are different lengths.

## Proof of Concept

The lack of validation on the arrays passed to **[ERC20MultiDelegate.sol#_delegateMulti()](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L65-L69)**

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

  // ... no validation on sources, targets, amounts arrays

}
```

Specifically, there is no check that the `sources`, `targets`, and `amounts` arrays are the same length before looping through them.

This could lead to errors like:

```solidity
_delegateMulti([1,2], [3,4,5], [6,7])

// sources length = 2
// targets length = 3 
// amounts length = 2

// Will index out of bounds on targets array
```

To understand why this is an issue, we need to look at the context of `_delegateMulti()`:

- It is handling the core delegation transfer logic in the contract.

- It loops through the `sources`, `targets`, and `amounts` arrays in lockstep to move tokens.

- The array lengths must match, otherwise it will read from incorrect array indexes.

For example:

```solidity
_delegateMulti(
  [1, 2], 
  [3, 4, 5],
  [6, 7]
)

// sources length = 2 
// targets length = 3
// amounts length = 2

// On second iteration, will index targets[2] out of bounds!
```

Here is proof that the lack of array length validation in `_delegateMulti()` can lead to errors:

Let's look at a simple example:

```solidity

uint256[] memory sources = [1, 2]; 
uint256[] memory targets = [3, 4, 5];
uint256[] memory amounts = [6, 7];

_delegateMulti(sources, targets, amounts);

// sources length = 2
// targets length = 3
// amounts length = 2 

// Looping through arrays:

i = 0 // OK - sources[0] targets[0] amounts[0]
i = 1 // OK - sources[1] targets[1] amounts[1] 

i = 2 // ERROR - sources[2] doesn't exist!
// But targets[2] and amounts[2] do exist 
// -> Index out of bounds

```

This shows how passing arrays of different lengths can lead to out-of-bounds errors.

**[The issue occurs because of missing validation](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L65-L116)**:

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

  // No length validation

}
```

## Tools Used

## Recommended Mitigation Steps

Some ways to add validation would be:

```solidity
// Require arrays are same length
require(
Lack of validation in `transferBetweenDelegators` may lead to transfer failures.
Audit: 2023-10-ens-findings
Severity: MEDIUM

# 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#L168-L169


# Vulnerability details

 ## Impact

The `transferBetweenDelegators()` function lacks checking that the proxy contracts exist for the from/to addresses.

## Proof of Concept

The lack of validation in **[transferBetweenDelegators()](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L163-L171)**

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

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

  // ...lacks checks that proxyAddressFrom and proxyAddressTo are valid contracts

}
```

Specifically, there are no checks after computing `proxyAddressFrom` and `proxyAddressTo` that verify these addresses point to deployed proxy contracts.

This could lead to errors if `from` or `to` do not have associated proxy contracts, such as:

```solidity
transferBetweenDelegators(0x123, 0x456, 100)

// 0x123 and 0x456 don't have proxy contracts deployed

// proxyAddressFrom and proxyAddressTo will be empty addresses
// Leading to failed transfer or worse - sending to empty address
```

After computing `proxyAddressFrom` and `proxyAddressTo`, there are no checks that these addresses point to valid deployed proxy contracts.

To understand why this is a problem, we need to look at the context:

- `transferBetweenDelegators()` is used to transfer tokens between source and target delegate proxies.

- It first retrieves the proxy addresses using `from` and `to`.

- But it assumes these proxy contracts exist, without verifying.

This can be an issue if `from` or `to` do not have associated proxy contracts:

```solidity
transferBetweenDelegators(0x123, 0x456, 100)

// 0x123 and 0x456 don't have proxies deployed!

// proxyAddressFrom and proxyAddressTo will be zero addresses
// Causing the transfer to fail or send to empty address 
```

The solution is to add validation on the retrieved proxy addresses:

```solidity 
require(extcodesize(proxyAddressFrom) > 0, "No proxy deployed for source"); 
require(extcodehash(proxyAddressFrom) == expectedHash, "Invalid proxy for source");

// And same checks for proxyAddressTo
```

This would prevent failures and errors from passing invalid proxy addresses.

Let's look at a simple example for proof that `transferBetweenDelegators()` lacks proper validation on the proxy addresses:

```solidity
address from = 0x123; // No associated proxy
address to = 0x456; // No associated proxy 

transferBetweenDelegators(from, to, 100);

// proxyAddressFrom = 0x0 (from has no proxy) 
// proxyAddressTo = 0x0 (to has no proxy)

// transferFrom will fail or se
Lack of Success Checks and Error Handling in Token Transfers: Impact on User Experience and DAO Security
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L148
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L160
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L170


# Vulnerability details

## Impact
Failing to implement proper success checks and error handling when using token.transferFrom may lead to inconvenience for users and the DAO. This oversight could result in ambiguous transaction failures, unclear user feedback, and the potential for unexpected issues, adversely affecting the user experience and contract security.

## Proof of Concept

L148
    token.transferFrom(proxyAddressFrom, msg.sender, amount);

L160 
    token.transferFrom(msg.sender, proxyAddress, amount);

L170
      token.transferFrom(proxyAddressFrom, proxyAddressTo, amount);

   

## Tools Used
Manual review 

## Recommended Mitigation Steps

Using `safeTransfer` is a good practice when transferring tokens to handle a wide range of potential issues. It's especially useful when transferring tokens to contracts. `safeTransfer` includes additional checks and error handling to enhance security and prevent issues that may arise during token transfers.

Here's why using `safeTransfer` is advantageous:

1. **Safety Checks:** `safeTransfer` checks if the recipient is a contract and if it implements the expected token standard (e.g., ERC-20 or ERC-721). If the recipient doesn't meet these criteria, the transfer is reverted, preventing potential token loss.

2. **Preventing Token Loss:** By using `safeTransfer`, you can avoid sending tokens to contracts that don't support token transfers, which could result in token loss.

3. **Error Handling:** If the transfer fails due to a non-compliant recipient, `safeTransfer` reverts the transaction with a clear error message. This helps users understand why the transaction failed and how to rectify the issue.

Here's an example of how to use `safeTransfer`:

```solidity
bool success = token.safeTransferFrom(msg.sender, to, amount);
require(success, "Token transfer failed: Check recipient contract's compatibility.");
```

By using `safeTransfer`, you provide a higher level of protection against potential issues, especially when interacting with external contracts that may not be token-aware. It's a recommended best practice for transferring tokens in a secure and user-friendly manner.




## Assessed type

Token-Transfer
ERC20ProxyDelegator Contract - Incorrect Approval Bug
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details



##  Description
The `ERC20ProxyDelegator` contract has a bug in the `constructor` function, where the delegate is not being properly approved to spend tokens. This bug may lead to the delegate failing to execute desired actions on behalf of the original delegator, potentially causing unexpected behavior or loss of funds.

## Impact
This bug could have varying degrees of impact depending on how the contract is used. If the delegate is expected to perform actions that require spending tokens on behalf of the delegator, such as transferring tokens or interacting with other contracts, the bug can lead to the delegate being blocked from completing those actions. The severity of this bug depends on the specific use case of the contract and the potential ramifications of failed or unauthorized actions.




## Proof of Concept:
1. Deploy the ERC20ProxyDelegator contract through a migration or a suitable deployment script.
2. Observe the code in the constructor:


     constructor(ERC20Votes _token, address _delegate) {
         _token.approve(msg.sender, type(uint256).max);
         _token.delegate(_delegate);
     }

3. Notice that the call to `approve` approves `msg.sender` rather than the intended `_delegate` address.

## Recommendation

To address this bug, the `approve()` function should be called on the `_delegate` address instead of `msg.sender`. This will ensure that the delegate is properly approved to spend tokens on behalf of the original delegator. Additionally, it is recommended to implement error checking after the `approve()` function call to handle any potential errors or return values that may arise.

## Suggested Fix:

     constructor(ERC20Votes _token, address _delegate) {
         _token.approve(_delegate, type(uint256).max);
         _token.delegate(_delegate);
     }


By implementing this fix, the `_delegate` address will be correctly approved to spend the necessary amount of tokens on behalf of the original delegator, mitigating the bug and ensuring the proper execution of desired actions.

## Tools Used 
Manual Review



## Assessed type

Invalid Validation
A smart contract user delegator not following the ERC 1155 specification might encounter issues
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
When a delegator is using a smart contract as an interaction, which is likely rare, but it can happen as they are many users using the ENS protocol, this without knowing what their actions might lead to unintended consequences

Some reason why a user who wants to delegate might decide to utilize the smart contract route is maybe flexibility, the ability to easily utilize and delegate votes through smart contracts may sound appealing to some users.

## Proof of Concept
But users who want to take that route must implement what the OZ implementation instructions said in the contracts about receiving the tokens of the id of the delegate, it states “If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value” this means the user must implement what the instructions said, or may lead to unintended issues, like tokens not getting implemented correctly or balances not getting updated correctly in the ERC 1155 contracts

If a user Is using a smart Contract as a delegator and they do not Implement what the ERC1155 specification implements in the _mintBatch Function may Face Unintended behaviors

```solidity
 * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
```
As seen the Specification is more than A Note it is More of A requirement, which will make the minting of tokens to work as intended

## Tools Used
Manual Analysis

## Recommended Mitigation Steps

Users Who want to use their smart contracts as a way to delegate their votes, should be made aware of the specification and requirements that using a smart contract entails and the type of issues that could pop up. 
Another full proof Mitigation is to out rightly tell ENS Community members that, They should 100% Not Use Smart Contracts at all and Always Use a EOA anytime they want to delegate their votes to delegates.



## Assessed type

DoS