xanoutas/solidity-security-findings huggingface dataset:
`_reimburse` and `createProxyDelegatorAndTransfer` uses `ERC20Votes.transferFrom` with user as msg.sender, but allowance is given for ERC20MultiDelegate contract.
Audit: 2023-10-ens-findings
Severity: HIGH

# 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#L170


# Vulnerability details

## Description
The `delegateMulti` external method is called by user. This function call some internal functions like `_reimburse` and `transferBetweenDelegators`, which doesn't change the `msg.sender` because they are internal. So, when these functions try to use `ERC20Votes.transferFrom`, it needs `msg.sender` to have allowance, but `ERC20ProxyDelegator` gives allowance only to `ERC20MultiDelegate`. 

## Proof of Concept
1. `deployProxyDelegatorIfNeeded` creates the Proxy Delegators in the following snippet:
```solidity
        if (bytecodeSize == 0) {
            new ERC20ProxyDelegator{salt: 0}(token, delegate);
            emit ProxyDeployed(delegate, proxyAddress);
        }
```
2. Proxy Delegator's constructor uses `ERC20Votes.approve` for msg.sender, which is the Multi Delegate Contract.
```solidity
contract ERC20ProxyDelegator {
    constructor(ERC20Votes _token, address _delegate) {
        _token.approve(msg.sender, type(uint256).max);
        _token.delegate(_delegate);
    }
}
```

3. Now, user calls `delegateMulti` external method. It calls internal functions like `_reimburse` and `transferBetweenDelegators` (which doesn't change `msg.sender`, because internal function in assembly uses `JMP` to reach the bytecode function, not `CALL` opcode).
```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);
    }

    function transferBetweenDelegators(
        address from,
        address to,
        uint256 amount
    ) internal {
        address proxyAddressFrom = retrieveProxyContractAddress(token, from);
        address proxyAddressTo = retrieveProxyContractAddress(token, to);
        token.transferFrom(proxyAddressFrom, proxyAddressTo, amount);
    }
```

4. Both methods try to use `token.transferFrom` to get tokens out of a Proxy Delegator. However, as user is the `msg.sender`, he needs allowance to do this, but Proxy Delegator approved `ERC20MultiDelegate` in the constructor, not the user.

## Impact
User can be incapable of moving his funds out of Proxy Delegators. 

## Tools Used
Manual Review 

## Recommended Mitigation Steps
Approve the user for amount before each problematic method.


## Assessed type

ERC20
Missing validation lets users delegate more than they own.
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


# Vulnerability details

## Impact

The amounts array is not checked to ensure the sum is less than or equal to the user's overall token balance. This could allow someone to delegate more tokens than they own.

## Proof of Concept

This is another validation issue in the `delegateMulti()` function related to properly checking the amounts array.

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

The amounts array is passed to `_delegateMulti()` without first verifying that the sum of amounts is less than or equal to `msg.sender's` total token balance

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

  // missing check on amounts array

  _delegateMulti(sources, targets, amounts);
}
```
This could allow an attacker to specify an amounts array that delegates more tokens than they actually own, if the sum of amounts exceeds their balance. 

The lack of validation on the `amounts` array in `delegateMulti()` is an issue that could allow users to delegate more tokens than they own.

The key problem is that `delegateMulti()` accepts an `amounts` array specifying how many tokens to delegate from each source address:

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

  // No check on amounts array!

  _delegateMulti(sources, targets, amounts);
}
```

However, there is no check that the **sum** of all the `amounts` is less than or equal to `msg.sender`'s total token balance prior to calling `_delegateMulti()`. 

This means an attacker could construct an `amounts` array where:

```solidity
sum(amounts) > balanceOf(msg.sender)
``` 

And delegate more tokens than they actually own!

The proper validation that is missing is:

```solidity 
uint totalAmount;

for (uint i = 0; i < amounts.length; i++) {
   totalAmount += amounts[i];
}

require(totalAmount <= IERC20(token).balanceOf(msg.sender), "Insufficient balance");
```

This would prevent the overflow by checking the total delegated amount is within the user's balance.

Without this check, several risks arise:

- Attackers could illegally delegate excessive voting power they don't own
- Governance systems could be compromised by inflated voting weights
- Token supply could be temporarily inflated during delegation

So, the lack of a total amounts validation against msg.sender's balance is a critical issue that would allow attackers to illegitimately delegate more voting power than they rightfully own. Proper summation and balance checking is needed to prevent this high-severity vulnerability.

**An example of how this vulnerability can be exploited would be very helpfu
Permissive owner access allows misleading URI changes, impacting user trust.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

`setUri()` function is only restricted to the contract owner, this could allow the owner to update the URI and make changes that users did not anticipate.

The `setUri()` function allows the owner to update the URI to anything they want. For example, they could point it to metadata that displays a misleading name or image for the tokens. 

```solidity
// Existing URI
http://tokens.com/erc1155metadata.json

// Owner calls
setUri("http://malicious.com/fakemetadata.json")

// Now the URI points to malicious/fake metadata
```

## Proof of Concept

For the access control issue with `setUri()`, the problematic code is on: **[ERC20MultiDelegate.sol#function setUri](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L151-L153)**

```solidity
function setUri(string memory uri) external onlyOwner {
  _setURI(uri); 
}
```

The `onlyOwner` modifier restricts calling this function to only the contract owner. 

- This `setUri()` function allows the owner to update the ERC1155 token URI which points to the metadata.
- The metadata contains information like name and image that clients/wallets show to users.
- By only allowing the owner to update this, they could change the metadata in a way users do not expect.
- A better design would be to make this an admin role or require a timelock/delay.

The issue is that the sensitive `setUri()` function is too permissive by only requiring ownership instead of a more appropriate access control mechanism.

Here is an in-depth explanation of the `setUri()` access control issue, along with the code:

```solidity
function setUri(string memory uri) external onlyOwner {
  _setURI(uri); 
}
```

This function sets the URI for the ERC1155 token contract. The URI points to off-chain JSON metadata that contains information like the name, symbol, and image url for the tokens.

The problem is the owner has too much power here - they can set the URI to anything without restrictions. For example, they could point to malicious metadata, or suddenly change it without warning.

This can mislead users who rely on the metadata to display proper info about the tokens in their wallets.

To prove the owner has full control, here is a simple example:

```solidity
// Contract deployed, owner is Alice

// Original URI 
http://tokens.com/erc1155metadata.json

// Alice calls setUri()
setUri("http://malicious.com/fakemetadata.json")

// URI now points to malicious metadata that Alice fully controls
```

As you noted, this allows Alice to arbitrarily change the URI which could mislead users by pointing to incorrect or malicious metadata. 

## Tools Used

## Recommended Mitigation Steps

A better design would be to make `setUri()` a privileged role like "admin" instead of owner, or require a timelock
Assembly proxy creation lacks bytecode validation, enabling arbitrary contract deployment.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L186
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#L186
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L179-L182


# Vulnerability details

## Impact

The proxy contract creation uses assembly to compute the create2 address. But it does not verify the deployed bytecode matches expectations.

The use of assemble for proxy contract creation has potential risks if the `bytecode` is not validated. This could allow deployment of arbitrary contracts. 

```solidity
// Compute expectedProxyAddress

new ERC20ProxyDelegator{salt: 0}(token, delegate); 

// Deployed bytecode could be malicious contract instead of expected proxy
```

## Proof of Concept

The use of assembly for proxy contract creation that could be a concern is in the **[ERC20MultiDelegate.sol#deployProxyDelegatorIfNeeded()](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173-L186)** function starting on line 173:

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

  address proxyAddress = retrieveProxyContractAddress(token, delegate);

  // check if the proxy contract has already been deployed
  uint bytecodeSize;
  assembly {
    bytecodeSize := extcodesize(proxyAddress)
  }

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

```

Specifically, the use of assembly on **[lines 179-1182 to get the bytecode size](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L179-L182)**:

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

The concern is that after deploying the **[new ERC20ProxyDelegator proxy contract on line 186](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L186)**:

```solidity
    new ERC20ProxyDelegator{salt: 0}(token, delegate);
```

There is no validation that the deployed bytecode matches the expected proxy contract implementation.

This could allow an arbitrary/malicious contract to be deployed instead of the intended proxy contract.

Here's an in-depth explanation that is important for full understanding.

The main use of assembly for proxy contract creation is on **[lines#179-182](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L179-L182)**:  

```solidity
  uint bytecode
Deterministic proxy contract address with a hard-coded salt enables token theft.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

The `retrieveProxyContractAddress` function calculates the address of the proxy contract deterministically based only on the delegate address. An attacker could pre-compute proxy contract addresses for target delegates and steal tokens by transferring to their own proxy contract at that address.

The proxy contract address generation should incorporate something unique per `delegator` like their address.

## Proof of Concept

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

```solidity
function retrieveProxyContractAddress(
    ERC20Votes _token, 
    address _delegate
) private view returns (address) {

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

  return address(uint160(uint256(hash)));
}
```

The salt value used in generating the proxy contract address is hard coded to 0. 

This means the proxy contract address only depends on the `delegate` address passed in. An attacker could `pre-compute` the address for a target delegate, deploy their own contract there, and steal tokens.

When we dive deeper and provide more context around the exact vulnerable line of code. Here are the key details:

The core issue is on **[`retrieveProxyContractAddress`](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L198-L215)**:

```solidity
function retrieveProxyContractAddress(
  // ...
) private view returns (address) {

  // Vulnerable line
  uint256(0), // Use of hard-coded salt value

  // ...
}
```

By hard-coding the salt value to 0, the proxy contract address becomes deterministic and depends only on the `delegate` address.

This allows an attacker to pre-compute the proxy contract address for a target delegate as follows:

1. Attacker picks a target delegate address
2. Attacker calls `retrieveProxyContractAddress` locally with the target address 
3. The exact proxy contract address for that delegate is returned based on the code
4. Attacker deploys a malicious contract at the pre-computed address

Now when the actual delegator tries to delegate to the target, the tokens will go to the attacker's contract instead of the intended delegate's proxy.

In summary:

- Hard-coded salt of 0 makes proxy address deterministic
- Attacker can pre-compute addresses for any delegate
- Attacker receives victim's tokens meant for delegate

This shows how the deterministic address generation introduces a vulnerability.

## Tools Used

## Recomme
No access control on `_delegateMulti` allows unauthorized token delegation.
Audit: 2023-10-ens-findings
Severity: HIGH

# 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#L57-L63


# Vulnerability details

## Impact

There is no access control on the `_delegateMulti` function. This allows anyone to delegate tokens between accounts. The function should check that the caller is the token holder.

## Proof of Concept

The `_delegateMulti` function that allows anyone to delegate tokens: **[ERC20MultiDelegate.sol#function _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 {
  // ...
}
```

This function is marked as `internal` so it can only be called by other functions within the contract. However, the public `delegateMulti` function on line 61 calls `_delegateMulti` without any access control: **[ERC20MultiDelegate.sol#function delegateMulti](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); 
}
```

The `delegateMulti` function is `external` so anyone can call it. It then passes the data directly to `_delegateMulti` without checking that the caller is authorized to delegate those tokens.

The specific issue is that the **[delegateMulti](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L57-L63)** function does not check that the caller is authorized before calling **[_delegateMulti](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L65-L69)**:

```solidity
// Vulnerable delegateMulti function
function delegateMulti(
  // ...
) external {

  // Directly calls _delegateMulti without any access control 
  _delegateMulti(sources, targets, amounts);  
}
```

You can see the key problem is the `_delegateMulti`. By calling `_delegateMulti` without any access control, `delegateMulti` allows any external caller to delegate tokens between accounts. 

`_delegateMulti` is internal, so it can only be called by other functions in the contract. However, `delegateMulti` is defined as external, meaning any external caller can call it. 

By directly calling the internal `_delegateMulti` from the unprotected external `delegateMulti`, it effectively exposes `_delegateMulti` to the public without any access control.

This is a violation of the checks-effects-interactions pattern, where external calls should come after input validat
delegateMulti(...)  Griefing Attack
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
A call to delegateMulti(... ) with the right parameters will consume much gas and waste memory for Proxy Delegators
DoS costing the attacker very little.

## Proof of Concept
Calling delegateMulti with many unique targets and amounts of 0 consumes Proxy Delegators.
Amounts of 0 are not checked for and much code is executed at little cost to attacker.

Link to the test file that shows setup for a small scale of the problem
https://github.com/craig-iam-smith/2023-10-ens/blob/main/test/delegatezero.js
And the data setting part
https://github.com/craig-iam-smith/2023-10-ens/blob/3bc0a485ea102a3aa83d64d8a84e44f95cd56da6/test/delegatezero.js#L80C1-L92C2

in the for loop in _delegateMulti (with many targets) _processDelegation(..) is called without checking amount 
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L98-L100

in _processDelegation - no checking for amount not zero, call deployProxyDelegatorsIfNeeded(...) and transferBetweenDelegators(...) 
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L129C1-L134C59

in deployProxyDelegatorIfNeeded(... )  if none exist, deploys new ERC20ProxyDelegator() allocations
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L173C1-L189C29

in transferBetweenDelegators(...) transferFrom is called without checking for amount wasting gas
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L163C1-L171C6

## Tools Used
Used vscode and hardhat, but this was a result of seeing that the provided test was calling delegateMulti with 0's as the amounts in one of the tests and I went down that rabbit hole.

## Recommended Mitigation Steps
The most minimal and simple is to check for amount zero in _delegateMulti()  everything is internal at that point and there is no other attack vector
https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L96C1-L98C2





## Assessed type

DoS
Owner role's URI setting power can be maliciously exploited.
Audit: 2023-10-ens-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

The owner role has a lot of power here. Setting `URI` could be used maliciously.
Should consider limiting owner capabilities to only essential admin functions.

## Proof of Concept

The issue occurs **[in the setUri function](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L151-L153)**:

```solidity
function setUri(string memory uri) external onlyOwner {
  _setURI(uri);
}
```

Specifically on line 152 with the `_setURI` call. 

The problem is the owner role has a broad privilege to set the URI to any value they want. This could be abused to set a malicious URI that could trick users.

This is a sensitive function since the URI determines where token metadata is fetched from. The `setUri` function allows the contract owner to set the URI for the ERC1155 token metadata.

The issue stems from the fact that the only validation on the input URI is that msg.sender must be the owner. There are no other checks on the URI itself.

This means the owner can set the URI to any arbitrary value, including a malicious URI. For example, they could set:

```solidity
setUri("https://evil.com/fake-metadata")
``` 

And now all ERC1155 metadata fetches would be redirected to an evil site the owner controls.

This could be used to exploit users by:

- Serving fake metadata that appears valid to trick users
- Phishing for secrets or extracting privacy data from users
- Serving malware to users fetching metadata

The root cause is the unrestricted write access for the owner role. By default OpenZeppelin Ownable only validates sender, not input.

Here are a few ways I could demonstrate evidence of the unrestricted owner privileges:

Code snippets:

The **[specific code that shows broad owner permissions](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L151-L153)**:

```solidity
// Owner can set any URI 
function setUri(string memory uri) external onlyOwner {
  _setURI(uri); 
}

// No input validation
```

## Tools Used

Manual Review

## Recommended Mitigation Steps

Some ways to limit the power here:

- Remove `setUri` entirely if not needed
- Add a `URI_BASE` constant that gets prepended to any URI set
- Whitelist valid URI domains that can be set
- Require `timelock` for critical URI changes
- Break role apart so admin can't set malicious URIs

For example:

```solidity
// Only allow URI changes for trusted domains  
function setUri(string memory uri) external onlyOwner {
  require(isTrustedDomain(uri), "Untrusted domain"); 
  _setURI(uri);
}

function isTrustedDomain(string memory uri) internal returns (bool) {