Platform: Code4rena
Start Date: 10/02/2022
Pot Size: $100,000 USDC
Total HM: 13
Participants: 21
Period: 7 days
Judge: leastwood
Total Solo HM: 10
Id: 85
League: ETH
Rank: 2/21
Findings: 4
Award: $16,888.71
🌟 Selected for report: 3
🚀 Solo Findings: 2
3033.3069 USDC - $3,033.31
Base fee modules require minimum fixed fee amount to be at least BPS_MAX, which is hard coded to be 10000.
This turns out to be a functionality restricting requirement for some currencies.
For example, WBTC (https://etherscan.io/token/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599, #10 in ERC20 token rankings), has decimals of 8 and current market rate around $40k, i.e. if you want to use any WBTC based collect fee, it has to be at least $4 per collect or fee enabled follow.
Tether and USDC (https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7 and https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, #1 and #3) have decimals of 6, so it is at least $0.01 per collect/follow, which also looks a bit tight for a hard floor minimum.
BPS_MAX is a system wide constant, now 10000:
This is correct for any fees defined in basis point terms.
When it comes to the nominal amount, 10000 can be too loose or too tight depending on a currency used, as there can be various combinations of decimals and market rates.
The following base collect module implementations require fee amount to be at least BPS_MAX (initialization reverts when amount < BPS_MAX):
All collect module implementations use the same check:
FeeCollectModule:
LimitedFeeCollectModule:
TimedFeeCollectModule:
LimitedTimedFeeCollectModule:
FeeFollowModule also uses the same approach:
As a simplest solution consider adding a separate constant for minimum fee amount in nominal terms, say 1 or 10
#0 - Zer0dot
2022-03-21T23:01:38Z
Addressed in https://github.com/aave/lens-protocol/pull/74
#1 - 0xleastwood
2022-05-03T20:54:04Z
Good find! The warden has identified that the use of BPS_MAX
is too restrictive to handle tokens with small decimals.
🌟 Selected for report: hyh
6740.682 USDC - $6,740.68
Treasury fee can be zero, while collect modules do attempt to send it in such a case anyway as there is no check in place. Some ERC20 tokens do not allow zero value transfers, reverting such attempts.
This way, a combination of zero treasury fee and such a token set as a collect fee currency will revert any collect operations, rendering collect functionality unavailable
Treasury fee can be set to zero:
Treasury fee transfer attempts are now done uncoditionally in all the collect modules.
Namely, FeeCollectModule, LimitedFeeCollectModule, TimedFeeCollectModule and LimitedTimedFeeCollectModule do not check the treasury fee to be send, treasuryAmount
, before transferring:
The same happens in the FeeFollowModule:
Some ERC20 tokens revert on zero value transfers:
https://github.com/d-xo/weird-erc20#revert-on-zero-value-transfers
Consider checking the treasury fee amount and do transfer only when it is positive.
Now:
IERC20(currency).safeTransferFrom(follower, treasury, treasuryAmount);
To be:
if (treasuryAmount > 0) IERC20(currency).safeTransferFrom(follower, treasury, treasuryAmount);
#0 - donosonaumczuk
2022-03-22T15:55:36Z
This one makes sense to me
#1 - donosonaumczuk
2022-03-22T16:51:34Z
cc: @Zer0dot
#2 - Zer0dot
2022-03-22T17:19:49Z
I think it makes sense, will add!
#3 - Zer0dot
2022-03-22T17:45:59Z
Addressed in https://github.com/aave/lens-protocol/pull/77
🌟 Selected for report: hyh
6740.682 USDC - $6,740.68
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/LensHub.sol#L128-135
In case when zero collection module be white listed and then zero collection module set to a post (done by different actors), its functionality will be partially broken: every collecting and mirroring of it will be reverted with Errors.PublicationDoesNotExist, while getPubType will return its type as a Mirror.
Setting severity to Medium per 'function of the protocol or its availability could be impacted', as either the behavior of rejecting collects/mirrors is desired and to be implemented explicitly, or such a scenario shouldn't exist in a system, i.e. now zero address absence in white list cannot be guaranteed as its setting is manual and possible
Collect module isn't checked additionally on init, any white listed address can be set:
While zero address also can be white listed:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/LensHub.sol#L128-135
If zero address is white listed and then set as collectModule for a post, a getPointedIfMirror helper function called on it will revert with Errors.PublicationDoesNotExist:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/libraries/Helpers.sol#L47
This way all attempts to collect and mirror this post will also revert:
Also, getContentURI view will fail with the same error:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/LensHub.sol#L785
And getPubType will return Mirror as a publication type:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/LensHub.sol#L829
Consider prohibiting zero collection module to be white listed.
If rejecting collects/mirrors is desired for a special post type, the corresponding error path can be implemented
#0 - Zer0dot
2022-03-18T17:14:38Z
This implies governance is a bad actor, and despite it being a side effect, reverting on a zero collect module is acceptable. IMO this is not relevant, though technically the report is valid, we won't be changing anything.
#1 - Zer0dot
2022-05-13T01:57:26Z
In consistency with some other comments I made, I wonder if marking this as "low" severity makes more sense. Governance being compromised is within acceptable risk parameters.
#2 - 0xleastwood
2022-05-13T14:38:02Z
In consistency with some other comments I made, I wonder if marking this as "low" severity makes more sense. Governance being compromised is within acceptable risk parameters.
I think this is inline with C4's guidelines for a medium
severity issue. As such, I'd keep this as is.
#3 - 0xleastwood
2022-05-13T14:39:05Z
This is consistent with other governance related issues.
#4 - Zer0dot
2022-05-13T14:39:29Z
Sounds fair!
As different compiler versions have critical behavior specifics, if the contracts get accidentally deployed using another compiler version compared to one they tested with, the various types of undesired behavior can be introduced
pragma solidity ^0.8.0
is used in the following ERC721 contracts:
ERC721Enumerable ERC721Time IERC721Time
Consider fixing the version to 0.8.10 across all the codebase
An attacker can front-run the initiallization whenever it is run not atomically with the construction (and given the contract code alone it is not guaranteed in any way), setting a malicious set of core configuration variables
FollowNFT:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/FollowNFT.sol#L57
CollectNFT:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/CollectNFT.sol#L39
LensHub:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/LensHub.sol#L67
Consider adding the access controls to the initialization functions so that only deployer contract could run them
A token with empty name and symbol will not be convenient to manually operate, other contracts' malfunctions are possible
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/base/LensNFTBase.sol#L45
Add validity checks
A range of malfunctions is possible if core configuration parameters are set by mistake to any values that make little sense. It is also not always right away evident that such a mistake took place
LensHub constructor doesn't check the addresses supplied:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/LensHub.sol#L58-59
Addresses to be set are controlled in the deploy script, but as that's core immutable configuration consider adding second layer of control in the constructor
TokenDoesNotExist is emitted which is ambigous, ProfileDoesNotExist is actual error there.
InteractionLogic.follow reverts with Errors.TokenDoesNotExist() is profile can't be found by handle hash (profile wasn't created or was burned):
Given how many tokens exist in the system, this looks not clear enough.
Consider adding a dedicated error. This looks justified as equvalently rare situations already have particular errors (ProfileCreatorNotWhitelisted or HandleLengthInvalid, for example).
InitParamsInvalid error is emitted, which is ambigous as approve isn't related to initialization, the ArrayMismatch one looks to fit better.
ApprovalFollowModule.approve reverts with Errors.InitParamsInvalid if profileIds.length != followModuleDatas.length
:
Given that it's not an initialization, this doesn't look good enough.
Consider reverting here with Errors.ArrayMismatch
Address of recipient isn't checked to be non-zero, but a combination of zero recipient and currency that do not allow for transfers to a zero address will impact the protocol as all the attempts to collect will revert
FeeCollectModule, LimitedFeeCollectModule, TimedFeeCollectModule, LimitedTimedFeeCollectModule do not check the addresses on init:
The same in the FeeFollowModule:
https://github.com/d-xo/weird-erc20#revert-on-transfer-to-the-zero-address
Add the check for non-zero recipient address on the mentioned modules initialization (initializePublicationCollectModule for FeeCollectModule, as an example).
As this is one time action the additional gas cost is low and it's justified giving an enhancement of protocol stability
imageURI as tokenURI marked as temp:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/core/LensHub.sol#L843
While there looks to be no better option:
https://github.com/code-423n4/2022-02-aave-lens/blob/main/contracts/libraries/DataTypes.sol#L66-71
#0 - Zer0dot
2022-03-18T17:06:38Z
Most of this is not really relevant, and in particular the recipient != address(0) check is actually present in the linked examples.