Platform: Code4rena
Start Date: 05/10/2023
Pot Size: $33,050 USDC
Total HM: 1
Participants: 54
Period: 6 days
Judge: hansfriese
Id: 294
League: ETH
Rank: 27/54
Findings: 1
Award: $55.85
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: thekmj
Also found by: 0x3b, 33BYTEZZZ, Bauchibred, Chom, Dravee, J4X, Limbooo, Maroutis, MiloTruck, MrPotatoMagic, SBSecurity, Sathish9098, Tadev, ZanyBonzy, adam-idarrha, adriro, btk, hyh, lukejohn, nmirchev8, peakbolt, radev_sw, rvierdiiev
55.8454 USDC - $55.85
ID | Description | Severity |
---|---|---|
L-01 | Current design of delegateMulti() might limit its functionality | Low |
L-02 | Smart contract accounts might not be compatible with ERC20MultiDelegate | Low |
N-01 | Unnecessary external call in getBalanceForDelegate() | Non-Critical |
delegateMulti()
might limit its functionalityThe current implementation of delegateMulti()
performs three kinds of vote transfers:
sources[i]
and targets[i]
are both non-zero, it transfers votes between the source and target delegatee.sources[i]
is non-zero, it transfers vote tokens from the delegatee to the caller.targets[i]
is non-zero, it transfers vote tokens from the caller to the delegatee.This can be seen in its implementation:
ERC20MultiDelegate.sol#L98-L107
if (transferIndex < Math.min(sourcesLength, targetsLength)) { // Process the delegation transfer between the current source and target delegate pair. _processDelegation(source, target, amount); } else if (transferIndex < sourcesLength) { // Handle any remaining source amounts after the transfer process. _reimburse(source, amount); } else if (transferIndex < targetsLength) { // Handle any remaining target amounts after the transfer process. createProxyDelegatorAndTransfer(target, amount); }
However, such a design only allows caller - delegatee transfers after all delegatee - delegatee transfers have been performed.
This limits the functionality of the contract as vote delegations cannot be performed in certain orders. For example:
If a user wishes to perform the vote transfers above, he will be unable to do so due to the current design.
Consider refactoring the function's implementation, such that users can perform caller - delegatee transfers
ERC20MultiDelegate
The delegateMulti()
function calls _mintBatch()
to mint ERC-1155 tokens to the caller:
ERC20MultiDelegate.sol#L113-L115
if (targetsLength > 0) { _mintBatch(msg.sender, targets, amounts[:targetsLength], ""); }
_mintBatch()
performs an external callback, which calls onERC1155Received()
on the to
address if it is a contract:
_doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
This might cause delegateMulti()
to always revert for existing contracts that do not implement onERC1155Received()
, such as old smart contract wallets.
Consider overriding _mintBatch()
to remove the _doSafeBatchTransferAcceptanceCheck()
call.
getBalanceForDelegate()
getBalanceForDelegate()
performs an external call to the contract's balanceOf()
function:
ERC20MultiDelegate.sol#L192-L196
function getBalanceForDelegate( address delegate ) internal view returns (uint256) { return ERC1155(this).balanceOf(msg.sender, uint256(uint160(delegate))); }
However, this is unnecessary as balanceOf()
is declared as a public
function:
function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
Consider calling balanceOf()
directly:
ERC20MultiDelegate.sol#L192-L196
function getBalanceForDelegate( address delegate ) internal view returns (uint256) { - return ERC1155(this).balanceOf(msg.sender, uint256(uint160(delegate))); + return balanceOf(msg.sender, uint256(uint160(delegate))); }
#0 - 141345
2023-10-12T12:44:01Z
#1 - c4-pre-sort
2023-10-12T12:44:18Z
141345 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-24T15:44:05Z
hansfriese marked the issue as grade-a