Drips Protocol contest - Deivitto's results

An Ethereum protocol for streaming and splitting funds.

General Information

Platform: Code4rena

Start Date: 25/01/2023

Pot Size: $90,500 USDC

Total HM: 3

Participants: 26

Period: 9 days

Judge: GalloDaSballo

Id: 209

League: ETH

Drips Protocol

Findings Distribution

Researcher Performance

Rank: 15/26

Findings: 1

Award: $131.98

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
G (Gas Optimization)
grade-b
G-02

Awards

131.9758 USDC - $131.98

External Links

Gas

Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate

if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key’s keccak256 hash (Gkeccak256 - 30 gas) and that calculation’s associated stack operations.

  • Caller.sol#L88 mapping(address => EnumerableSet.AddressSet) internal _authorized;

  • Caller.sol#L90 mapping(address => uint256) public nonce;

<x> += <y> costs more gas than <x> = <x> + <y> for state variables

Using the addition operator instead of plus-equals saves gas. Same goes for other operands like -=.

++i / i++ should be unchecked{++i} / unchecked{i++} when it is not possible for them to overflow, as is the case when used in for-loop and while-loops

In Solidity 0.8+, there’s a default overflow check on unsigned integers. It’s possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline.

require()/revert() strings longer than 32 bytes cost extra gas

NOTE: None of these findings where found by 4naly3er output - Gas

Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas

internal functions only called once can be inlined to save gas

Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.

duration

  • Drips.sol#L88 function duration(DripsConfig config) internal pure returns (uint32) {

bytes constants are more efficient than string constants

If data can fit into 32 bytes, then you should use bytes32 datatype rather than bytes or strings as it is cheaper in solidity.

  • Caller.sol#L82 string internal constant CALL_SIGNED_TYPE_NAME = "CallSigned("

abi.encode() is less gas efficient than abi.encodePacked()

Changing the abi.encode function to abi.encodePacked can save gas since the abi.encode function pads extra null bytes at the end of the call data, which is unnecessary. Also, in general, abi.encodePacked is more gas-efficient.

Duplicated require() check should be refactored

Duplicated require() / revert() checks should be refactored to a modifier or function to save gas. If it's been used for example require(addr != address(0)) or require(a > b), etc multiple times in the same contract. This can be refactored to a modifier like notZeroAddress(address a), function biggerThan(uint a, uint b), etc.

  • Drips.sol#L541 require(_hashDrips(receivers) == state.dripsHash, "Invalid current drips list");

  • Drips.sol#L622 require(_hashDrips(currReceivers) == state.dripsHash, "Invalid current drips list");

  • Caller.sol#L116 require(_authorized[sender].remove(user), "Address is not authorized");

  • Caller.sol#L149 require(isAuthorized(sender, _msgSender()), "Not authorized");

#0 - GalloDaSballo

2023-02-16T15:40:56Z

+= 500

++ 16* 20 320

820

Rest I disagree with

#1 - c4-judge

2023-02-24T11:02:06Z

GalloDaSballo marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter