Platform: Code4rena
Start Date: 18/11/2021
Pot Size: $50,000 USDC
Total HM: 18
Participants: 26
Period: 7 days
Judge: leastwood
Total Solo HM: 12
Id: 54
League: ETH
Rank: 9/26
Findings: 2
Award: $880.39
🌟 Selected for report: 2
🚀 Solo Findings: 0
🌟 Selected for report: elprofesor
Also found by: 0x0x0x, harleythedog, loop, pauliax
52.2858 USDC - $52.29
loop
When upgrading a lock the implementation is taken from _publicLockImpls[version]
. The upgraded implementation first has to be added with the function addLockTemplate(address impl, uint16 version)
, which puts the new implementation at index version
of the _publicLockImpls
mapping.
When calling upgradeLock
there is no check whether this has actually happened for the version
the lock manager tries to update to. Assuming the user is lock manager, has given the correct version and the proxyAdmin is set, it is possible for the lock template at the version
index to not be set yet. This will result in the lock being upgraded to the zero address and likely no longer be usable until upgraded to the correct template.
Upgrade process: https://github.com/code-423n4/2021-11-unlock/blob/main/smart-contracts/contracts/Unlock.sol#L249-L251
Adding a lock template: https://github.com/code-423n4/2021-11-unlock/blob/main/smart-contracts/contracts/Unlock.sol#L187-L193
Add something along the lines of require(_publicLockImpls[version] != address(0), "Template not found")
.
#0 - julien51
2022-01-03T11:52:35Z
That's a good one! Let's get that one fixed @clemsos ?
#1 - 0xleastwood
2022-01-16T09:39:48Z
Don't think this is a super serious issue as the transaction simply reverts, requiring the lock owner to reference a different implementation.
#2 - 0xleastwood
2022-01-16T22:44:55Z
Duplicate of #133
🌟 Selected for report: loop
398.4587 USDC - $398.46
loop
The function tokenOfOwnerByIndex
has a requirement for _index
to be 0
. This is different from the specification, which states:
/// @dev Throws if `_index` >= `balanceOf(_keyOwner)` or if /// `_keyOwner` is the zero address, representing invalid NFTs.
It also seems to be a bit unnecessary as _index
is not even used in the following call getTokenIdFor(_keyOwner)
.
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other https://github.com/code-423n4/2021-11-unlock/blob/main/smart-contracts/contracts/mixins/MixinERC721Enumerable.sol#L44-L59
Change require statement to something according to specification:
require(_index < balanceOf(_keyOwner) && _keyOwner != address(0));
#0 - julien51
2022-01-03T11:44:28Z
This is indeed an issue! Let's get it fixed @clemsos
🌟 Selected for report: GiveMeTestEther
Also found by: loop
loop
The function updateKeyPricing
in MixinLockCore.sol and _initializeMixinFunds
in MixinFunds.sol have an invalid token check which can be moved to the start of the function. If an invalid token is given as input it will revert before trying to perform the action(s) which are currently done before the check. Since the check does not depend on these actions being completed it will save some gas if an invalid token is given as input if the check is done first.
Move reuire statements to start of the function.
#0 - 0xleastwood
2022-01-17T09:11:30Z
Duplicate of #60
loop
Some functions that are currently set to public are not called internally and can be external to save some gas when invoking these functions.
Slither
#0 - 0xleastwood
2022-01-17T08:52:05Z
Duplicate of #196
🌟 Selected for report: loop
398.4587 USDC - $398.46
loop
If recordKeyPurchase
is invoked with an ERC-20 token which does not have a supported uniswap oracle maxTokens
will perform a division by 0
if grossNetworkProduct
has not been increased yet. This is due to valueInEth
not being set if no supported oracle has been found, as the if condition is not met. As a result grossNetworkProduct
will also not be increased in grossNetworkProduct = grossNetworkProduct + valueInETH;
. Assuming the udtOracle
has been set and _referrer
is not address(0)
, the computation of maxtokens
can be a division by 0 as grossNetworkProduct
has not increased.
Alternatively, if grossNetworkProduct
has been increased before, but there is no supported oracle, a lot of unnecessary computation is done because maxTokens
will always be 0
due to valueInEth
being 0
. As a result, tokensToDistribute
will also be 0
and nothing really happens except for a lot of computation, and thus gas cost.
Changing the if(address(oracle) != address(0))
to a require statement will revert the function if no supported oracle is found resulting in no 0 division or unnecessary computation.
Check for supported oracle: https://github.com/code-423n4/2021-11-unlock/blob/main/smart-contracts/contracts/Unlock.sol#L302-L304
Increase of gNP: https://github.com/code-423n4/2021-11-unlock/blob/main/smart-contracts/contracts/Unlock.sol#L311
Computation of maxTokens
:
https://github.com/code-423n4/2021-11-unlock/blob/main/smart-contracts/contracts/Unlock.sol#L328-L337
If maxTokens
< tokensToDistribute
:
https://github.com/code-423n4/2021-11-unlock/blob/main/smart-contracts/contracts/Unlock.sol#L340-L345
Change if(address(oracle) != address(0))
to something along the lines of require(address(oracle) != address(0), "No supported oracle");