xanoutas/solidity-security-findings huggingface dataset:
can not transfer voting tokens between `ERC20ProxyDelegator` contracts even if he supplied funds before
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Description

if a user wants to delegate voting power to a delegate he will call the function `delegateMulti` with `targets` array set to the address of the delegates he wants to delegate to, but the `targets` array is uint and not address type, so the user needs to cast the address to uint leaving the highest 12 bytes as 0.
but if the user by mistake sets any of these upper 12 bytes to anything other than 0, he wont be able to transfer voting power to another delegate throught the function `_processDelegation`.

## Impact

if the user sets the upper 12 bytes to anything other than 0 by mistake, he wont be able to transfer voting tokens directly between `ERC20ProxyDelegator` , he will need first to reimburse back the voting tokens then give them back with by calling `delegateMulti` with correct `targets` array.

## Proof of Concept

imagine a user Bob wants to delegate to Alice half of his voting tokens.

Address of Alice : 0x12f28e2106ce8fd8464885b80ea865e98b465149

- he calls `delegateMulti` with the `targets` set to the address of Alice, like so` targets = [0x000000100000000000742d35Cc6634C0532925a3b844Bc454e4438f44e]`
- the funtion `delegateMulti` correctly mints for him an erc1155 with `id = 0x000000100000000000742d35Cc6634C0532925a3b844Bc454e4438f44e`
- Bob after a while wants to transfer the voting tokens he delegated to Alice to another user Sam.
- he calls `delegateMulti` again with `sources = [0x000000100000000000742d35Cc6634C0532925a3b844Bc454e4438f44e]` and targets equal to the address of Sam.
- `delegateMulti` will call `_processDelegation` which will check for the balanceOf Bob, but the balance is checked by casting the lower 20Bytes in the sources array back to uint which will give this : `0x0x000000000000000000742d35Cc6634C0532925a3b844Bc454e4438f44e` Bob doesn't have this id so the assert will fail and he wont be able to transfer the voting tokens.
- Bob will need to reimburse his tokens before transfering them.

## Tools Used

vscode

## Recommended Mitigation Steps

remove the check of the balance of the user, because it will revert if he doesn't have the erc1155 with the correct id in `_burnbatch`. or use safeCast.


## Assessed type

Math
Loss of tokens because target address can be set to zero
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L124-L137
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L155-L161


# Vulnerability details

## Impact
Due to the possibility of target addresses being zero, tokens transferred to zero address will be lost forever, leading to loss of funds for the users of the protocol and that tokens being out of circulation forever (burned).

## Proof of Concept
Users can intentionally or unintentionally (which is the bigger problem) put zero addresses to an array of target addresses inside of the delegateMulti function. Then _processDelegation or createProxyDelegatorAndTransfer functions will transfer tokens to the zero address without any check, resulting in the loss of tokens.
A simple target address check inside of those functions will create a much better user experience and prevent the massive burn of tokens.

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

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


## Tools Used
Manual analysis

## Recommended Mitigation Steps
Add zero address checks for the target address at the start of _processDelegation and createProxyDelegatorAndTransfer functions. If the given target address happens to be zero, skip the rest of the function code.


## Assessed type

Token-Transfer
Approval Verification
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
There is a great need for approval check before commencement of token transfer and clearing of excess approvals after token transfer has been done, to prevent potential third-party exploitation that could drain funds from unsuspecting users via the `_reimburse` function.

## Proof of Concept
```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);
    }
```
In this code block and similar we can see the token transferFrom is used which indicates there would have been an approval before this function works.
Third-party attacker that could drain funds from unsuspecting users via the `_reimburse` function by just calling the `delegateMulti` with target array having less length than source array. This is because of the direct verified transfer of token from source to msg.sender in the `reimburse` function

## Tools Used
Manual Review
## Recommended Mitigation Steps
- Implement an approval verification prior to transfer to guarantee sufficient authorizations are in place for seamless token transactions between source and target.

- Deactivate any remaining approvals to prevent potential third-party exploitation that could drain funds from unsuspecting users via the `_reimburse` function.


## Assessed type

Token-Transfer
no sufficient tokens check for transferFrom() in  _reimburse() , createProxyDelegatorAndTransfer() and transferBetweenDelegators()
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
there is no sufficient tokens check in `_reimburse()` , `createProxyDelegatorAndTransfer()` and `transferBetweenDelegators()` for `transferFrom` which can lead to unexpected results

## Proof of Concept
from the OZ's ERC1155.sol:https://github.com/OpenZeppelin/openzeppelin-contracts/blob/793d92a3331538d126033cbacb1ee5b8a7d95adc/contracts/token/ERC1155/ERC1155.sol#L221
`from` must have a balance of tokens of type `id` of at least `value` amount.
the below functions in `ERC20MultiDelegate` contract is not checking for above criteria before `transferFrom` . 
```
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);
    }
```
```
function createProxyDelegatorAndTransfer(
        address target,
        uint256 amount
    ) internal {
        address proxyAddress = deployProxyDelegatorIfNeeded(target);
        token.transferFrom(msg.sender, proxyAddress, amount);
    }
```
```
function transferBetweenDelegators(
        address from,
        address to,
        uint256 amount
    ) internal {
        address proxyAddressFrom = retrieveProxyContractAddress(token, from);
        address proxyAddressTo = retrieveProxyContractAddress(token, to);
        token.transferFrom(proxyAddressFrom, proxyAddressTo, amount);
    }
```
these functions are directly transferring tokens without checking whether the `from` have a balance of tokens of type `id` of at least `value` amount. and the functions using/invoking this functions will be affected.


## Tools Used
Manual Review

## Recommended Mitigation Steps
implement function requirement's check before directly using it


## Assessed type

Token-Transfer
Same multiple values in source result in wrong withdrawl
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

If by mistake same delegate value is given multiple times to sources array then withdrawal is affected

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

In test related to "Should be able to withdraw fully" if same values given to source array like here bob is given 3 times then withdraw test is reverting.

      describe('withdraw', () => {
      it('should be able to withdraw fully', async () => {
      const delegatorTokenAmount = await token.balanceOf(deployer);
      // const customAmount = ethers.utils.parseEther('10000000.0'); // ens

      // give allowance to multi delegate contract
      await token.approve(multiDelegate.address, delegatorTokenAmount);
      // delegate multiple delegates
      const delegates = [bob, bob, bob];
      const amounts = delegates.map(() =>
        delegatorTokenAmount.div(delegates.length)
      );

      await multiDelegate.delegateMulti([], delegates, amounts);

      const delegatorTokenAmountAfterDeposit = await token.balanceOf(deployer);
      expect(delegatorTokenAmountAfterDeposit.toString()).to.equal('0');

      await multiDelegate.delegateMulti(delegates, [], amounts);

      const delegatorTokenAmountAfterWithdraw = await token.balanceOf(deployer);
      expect(delegatorTokenAmountAfterWithdraw.toString()).to.equal(
        delegatorTokenAmount.toString()
      );
    });

## Tools Used
Manually

## Recommended Mitigation Steps
Apply check to avoid same multiple values in sources.


## Assessed type

Error
Anyone can force reimbursement from source
Audit: 2023-10-ens-findings
Severity: MEDIUM

# 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#L103
https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L148


# Vulnerability details

## Impact
The `_reimburse` function is invoked when the index is less than the source length. This typically occurs when the target length exceeds the source array length, resulting in the reimbursement of the index number beyond the minimum from the source to the msg.sender.

## Proof of Concept
Consider an example where the target array length is 5, and both the source and amount lengths are 7. It's important to note that there is no validation process for msg.sender to receive tokens from the source, allowing for any arbitrary source selection.

Lastly, the final two sources would have the last two amounts deducted from their wallets, provided the source has outstanding token approval on the ERC20MultiDelegate contract.

## Tools Used
Manual Review
## Recommended Mitigation Steps
The reimbursement process should incorporate a validation check not only on the caller but also on the receiver of the source amounts. This measure is to ensure that external callers do not inadvertently receive unmerited tokens.


## Assessed type

Token-Transfer
Delegate balance in `getBalanceForDelegate` can be manipulated by delegate sending ERC1155 tokens to the contract directly
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The contract is an ERC1155 contract that can receive tokens from delegates which might affect core protocol invariants through `getBalanceForDelegate`

## Proof of Concept
   ```solidity
      function getBalanceForDelegate(
        address delegate
    ) internal view returns (uint256) {
        return ERC1155(this).balanceOf(msg.sender, uint256(uint160(delegate)));
    }
```
function checks the delegate balance which can be manipulated by delegate sending tokens to the contract directly and this might have extended consequences for the protocol

## Tools Used
Manual Review

## Recommended Mitigation Steps
Restrict the ability of contract to accept tokens


## Assessed type

Access Control
Use of uint for addresses may result in fund loss for user . 
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

# Proof of Concept : 
The `Multidelegate` contract takes input of addresses as uint256 which is quite unique . Each target address is inputted as uint256 and casted down to uint160 .
```solidity 
 for ( 
            uint transferIndex = 0;
            transferIndex < Math.max(sourcesLength, targetsLength);
            transferIndex++
        ) {
            address source = transferIndex < sourcesLength
                ? address(uint160(sources[transferIndex])) //IF array ends then source address will be zero 
                : address(0);
            address target = transferIndex < targetsLength
                ? address(uint160(targets[transferIndex])) //IF array ends then target  address will be zero 
                : address(0);
            uint256 amount = amounts[transferIndex]; 
```
And ERC1155's `_mintBatch`  & `_burnBatch` is used to ensure access control . Where sources/targets refer to unique tokenID of the proxydelegator .
```solidity 
 _burnBatch(msg.sender, sources, amounts[:sourcesLength]);
 _mintBatch(msg.sender, targets, amounts[:targetsLength], "");
```

It is could be assumed that totalsupply of a tokenID(sources/targets here) in  `ERC20MultiDelegate` contract is equal to the voting token  amount hold by the proxyDelegator . 

In this design ensuring the validity of input solely depends on the user .  However this may get problematic if sources/targets array input is bigger than `type(160).max `,  which is invalid  . There are no checks to handle this scenario . In this case,  the transaction will still execute succesfully if the user is trying to delegate . 

Other impacts of this scenario  could be : 
 The casted down uint160 from larger uint256 may match with existing delegatee and relevent proxydelegator  contract . In that case , funds will be sent to that proxydelegator if the user is trying to delegate . ERC1155 will mint tokens to the user where tokenID will be the non-casted uint256 . Now , two different tokenID in the `ERC20MultiDelegate` contract referring to the same proxydelegator contract  . 
This breaks the invariant of totalsupply(of a tokenID in `ERC20MultiDelegate` contract ) to amount of tokens hold by the delegatee's proxydelegator . 

Also ,There's risk of  a delegatee  using  those Mistakenly sent tokens before the delegator claims them back by frontrunning  .   
# recommendation :
The mitigation is pretty simple : 
All of the input array for addresses should be type of `uint160[] ` .



## Assessed type

Invalid Validation
Front-Running during Proxy Creation
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
An ill-intentioned party might front run every proxy contracts creation and deploy one first causing the transaction for every user to revert. It is worth noting that for this attack to succeed, the attacker would need to get their transaction mined in the small window between when the user's transaction checks bytecodeSize and when it actually creates the proxy. Also, this attack works once per new proxy creation.
Even if the likelihood of this happening is low, the attacker can target every new proxy creation and disrupt the creation process which can lead to some frustration and higher gas fees.

- Gas Fees: Even if it's a one-time occurrence per user/new proxy creation, it still results in users having to pay additional gas fees, which could be particularly problematic if gas prices are high.
- Trust: Repeated instances of failed transactions (even if they can be re-run successfully) might erode trust in the platform. Users might feel that the platform is not secure or reliable. Especially for users who want to create many proxies.
- Wider Impact: If the platform has a large user base, and many users experience this issue, it could result in a notable disruption and potentially become a reputational issue for the platform.

This is medium because long/medium term impact is high and likelihood is low.

## Proof of Concept
1. A user attempts to create a proxy.
2. An attacker observes the transaction in the mempool and sends a similar transaction with a higher gas price. The bot make sure that the transaction is mined in the small window between when the user's transaction checks bytecodeSize and when it actually creates the proxy.
3. The attacker’s transaction is mined first, causing the user’s transaction to fail.

The impacted code is :
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L179-L189

## Tools Used

- Code review

## Recommended Mitigation Steps
- During the contract address creation, make the salt a variable that gets increased whenever the function `deployProxyDelegatorIfNeeded` is called


## Assessed type

DoS
User is unable to undelegate their votes from a ProxyDelegator
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L14-L21
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L98-L107
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L124-L137
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L155-L161
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L190


# Vulnerability details

## Impact

The `delegate()` is used to delegate sender's votes to someone else without the need to actually send the tokens. Here is a quick intro for the function: 

delegate(address delegatee)
This function allows msg.sender to delegate their voting power to delegatee and the delegatee will vote on behalf of msg.sender. Note that the tokens are not transferred to delegatee, only the voting power. Delegation can be changed or revoked at any time by calling delegate again with a different delegatee or with address(0).

**Caution** on the last statement `Delegation can be changed or revoked at any time by calling delegate again with a different delegatee or with address(0).` If a voter doesn't want to delegate his votes anymore to anyone he calls the function with address(0). But here in this contract, that actually is possible only for 1 voter, others will be permanently locked out of this functionality of revoking votes.


## Proof of Concept
In `ERC20MultiDelegate.sol` there is the contract `ERC20ProxyDelegator` **!!!** This `ERC20ProxyDelegator` is only created if a voter wants to delegate his votes to an **uninitialized** `to` or `target` address, meaning only if there is no `ERC20ProxyDelegator` for that `to` or `target` address.

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

So the actual issue here is that whenever a voter initiates a `delegateMulti`, this calls `_delegateMulti`:


```
function _delegateMulti(
        uint256[] calldata sources,
        uint256[] calldata targets,
        uint256[] calldata amounts
    ) internal {
        
        //CODE...

        // Iterate until all source and target delegates have been processed.

        for (
            uint transferIndex = 0;
            transferIndex < Math.max(sourcesLength, targetsLength);
            transferIndex++
        ) {

            address source = transferIndex < sourcesLength
                ? address(uint160(sources[transferIndex]))
                : address(0);

            address target = transferIndex < targetsLength
                ? address(uint160(targets[transferIndex]))
                : address(0);

            uint256 amount
Needs a secure modifier
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
The ERC20ProxyDelegator constructor appears to be used for delegating voting rights to a delegate by allowing them to call the delegate function of an ERC20Votes contract. To secure this function, you should consider the following measures:

Access Control:
Limit who can deploy the ERC20ProxyDelegator contract. You can use an access control mechanism, such as OpenZeppelin's Ownable, to ensure that only trusted addresses can deploy this contract.

## Proof of Concept
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L15-#L20

## Tools Used
Manual Audit

## Recommended Mitigation Steps
import "@openzeppelin/contracts/access/Ownable.sol";

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


## Assessed type

Access Control