Platform: Code4rena
Start Date: 17/07/2023
Pot Size: $85,500 USDC
Total HM: 11
Participants: 26
Period: 14 days
Judge: Picodes
Total Solo HM: 1
Id: 263
League: ETH
Rank: 13/26
Findings: 1
Award: $31.38
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: MiloTruck
Also found by: 0xAnah, AlexCzm, Bughunter101, BugzyVonBuggernaut, DavidGiladi, Emmanuel, Iurii3, Kaysoft, MohammedRizwan, Prestige, Rolezn, Sathish9098, Stormreckson, adeolu, descharre, evmboi32, fatherOfBlocks, ginlee, ihtishamsudo, juancito, mrudenko, tnquanghuy0512
31.3772 USDC - $31.38
ID | Finding | Instances |
---|---|---|
L-01 | isContract() can be bypassed | 1 |
L-02 | TOKEN_GUARDIAN_COOLDOWN should be a constant of 7 days | 1 |
ID | Finding | Instances |
---|---|---|
N-01 | Remove block.timestamp and block.number from events | - |
N-02 | Use storage variable instead of retrieving it from library | - |
N-03 | Don't write to memory when a variable is only used once | - |
The LensProfiles modifier checks if the contract is an EOA. It uses the isContract
function from the OpenZeppelin Adress library.
modifier onlyEOA() { if (msg.sender.isContract()) { revert Errors.NotEOA(); } _; }
function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; }
If an address is a contract then the size of code stored at the address will be greater than 0. However if the function is called from the constructor, the code size will still be 0. The check .isContract() will think it's an EOA when in reality it's a contract.
Instead of checking account.code.length > 0
. Do msg.sender != tx.origin
TOKEN_GUARDIAN_COOLDOWN
should be a constant of 7 daysThe interface documentation of the function DANGER__disableTokenGuardian
says it has a 7 day cooldown. However the `` is a immutable variable that's assigned to in the constructor. This is prone to errors and should be avoided. Since it's immutable there is no chance to change it afterwards. It's better to make it a constant with a value of 7 days so no mistake can be made by the admin.
ILensProfiles.sol#L8-L15
/** * @notice DANGER: Triggers disabling the profile protection mechanism for the msg.sender, which will allow * transfers or approvals over profiles held by it. * Disabling the mechanism will have a 7-day timelock before it becomes effective, allowing the owner to re-enable * the protection back in case of being under attack. * The protection layer only applies to EOA wallets. */ function DANGER__disableTokenGuardian() external;
uint256 internal immutable TOKEN_GUARDIAN_COOLDOWN; constructor(address moduleGlobals, uint256 tokenGuardianCooldown) { MODULE_GLOBALS = IModuleGlobals(moduleGlobals); TOKEN_GUARDIAN_COOLDOWN = tokenGuardianCooldown; }
block.timestamp and block.number are added to event information by default so adding them manually is unnecessary and a gas waster.
This is used in almost all of the emit statements.
getProfile()
returns a profile in storage. When this is already retrieved from the library, it should be reused instead of calling getProfile()
again.
Types.Profile storage _profile = StorageLib.getProfile(profileId); _profile.imageURI = createProfileParams.imageURI; bytes memory followModuleReturnData; if (createProfileParams.followModule != address(0)) { // Load the follow module to be used in the next assembly block. address followModule = createProfileParams.followModule; - StorageLib.getProfile(profileId).followModule = followModule; + _profile.followModule = followModule; }
When a storage variable or a parameter is only used once, it's unnecessary to assign it to a memory variable.
if (configNumber == nextAvailableConfigNumber) { // The next configuration available is being changed, it must be marked. // Otherwise, on a profile transfer, the next owner can inherit a used/dirty configuration. _delegatedExecutorsConfig.maxConfigNumberSet = nextAvailableConfigNumber; - configSwitched = switchToGivenConfig; - if (configSwitched) { + if (switchToGivenConfig) { // The configuration is being switched, previous and current configuration numbers must be updated. _delegatedExecutorsConfig.prevConfigNumber = _delegatedExecutorsConfig.configNumber; _delegatedExecutorsConfig.configNumber = nextAvailableConfigNumber; } } else {
#0 - c4-judge
2023-08-28T20:54:18Z
Picodes marked the issue as grade-b