Drips Protocol contest - Rolezn'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: 14/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-09

Awards

131.9758 USDC - $131.98

External Links

Summary<a name="Summary">

Gas Optimizations

IssueContextsEstimated Gas Saved
GAS‑1abi.encode() is less efficient than abi.encodepacked()4400
GAS‑2Setting the constructor to payable791
GAS‑3Using fixed bytes is cheaper than using string1-
GAS‑4Using delete statement can save gas11-
GAS‑5Use hardcoded address instead address(this)6-
GAS‑6internal functions only called once can be inlined to save gas23506
GAS‑7Multiple Address Mappings Can Be Combined Into A Single Mapping Of An Address To A Struct, Where Appropriate2-
GAS‑8Optimize names to save gas8176
GAS‑9<x> += <y> Costs More Gas Than <x> = <x> + <y> For State Variables21-
GAS‑10Public Functions To External42-
GAS‑11Help The Optimizer By Saving A Storage Variable’s Reference Instead Of Repeatedly Fetching It15-
GAS‑12Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead53-
GAS‑13++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- And While-loops15525
GAS‑14Using unchecked blocks to save gas1136
GAS‑15Using storage instead of memory saves gas103500
GAS‑16Use uint256(1)/uint256(2) instead for true and false boolean states315000

Total: 222 contexts over 16 issues

Gas Optimizations

<a href="#Summary">[GAS‑1]</a><a name="GAS&#x2011;1"> abi.encode() is less efficient than abi.encodepacked()

See for more information: https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison

<ins>Proof Of Concept</ins>
176: abi.encode(

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L176

842: return keccak256(abi.encode(receivers));

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L842

858: return keccak256(abi.encode(oldDripsHistoryHash, dripsHash, updateTime, maxEnd));

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L858

278: return keccak256(abi.encode(receivers));

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L278

<a href="#Summary">[GAS‑2]</a><a name="GAS&#x2011;2"> Setting the constructor to payable

Saves ~13 gas per instance

<ins>Proof Of Concept</ins>
30: constructor(DripsHub _dripsHub, address forwarder, uint32 _driverId)
        ERC2771Context(forwarder)

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L30

219: constructor(uint32 cycleSecs, bytes32 dripsStorageSlot)

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L219

109: constructor(uint32 cycleSecs_)
        Drips(cycleSecs_, _erc1967Slot("eip1967.drips.storage"))
        Splits(_erc1967Slot("eip1967.splits.storage"))

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L109

28: constructor(DripsHub _dripsHub, uint32 _driverId)

https://github.com/code-423n4/2023-01-drips/tree/main/src/ImmutableSplitsDriver.sol#L28

158: constructor(Managed logic, address admin) ERC1967Proxy(address(logic), new bytes(0))

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L158

32: constructor(DripsHub _dripsHub, address forwarder, uint32 _driverId)
        ERC2771Context(forwarder)
        ERC721("DripsHub identity", "DHI")

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L32

94: constructor(bytes32 splitsStorageSlot)

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L94

<a href="#Summary">[GAS‑3]</a><a name="GAS&#x2011;3"> Using fixed bytes is cheaper than using string

As a rule of thumb, use bytes for arbitrary-length raw byte data and string for arbitrary-length string (UTF-8) data. If you can limit the length to a certain number of bytes, always use one of bytes1 to bytes32 because they are much cheaper.

<ins>Proof Of Concept</ins>
82: string internal constant CALL_SIGNED_TYPE_NAME = "CallSigned("

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L82

<a href="#Summary">[GAS‑4]</a><a name="GAS&#x2011;4"> Using delete statement can save gas

<ins>Proof Of Concept</ins>
478: uint256 idx = 0;
489: uint256 amt = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L478

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L489

744: uint256 spent = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L744

880: uint256 currIdx = 0;
881: uint256 newIdx = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L880-L881

60: uint256 weightSum = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/ImmutableSplitsDriver.sol#L60

126: uint32 splitsWeight = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L126

156: balance.splittable = 0;
157: uint32 splitsWeight = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L156-L157

188: balance.collectable = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L188

228: uint64 totalWeight = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L228

<a href="#Summary">[GAS‑5]</a><a name="GAS&#x2011;5"> Use hardcode address instead address(this)

Instead of using address(this), it is more gas-efficient to pre-calculate and use the hardcoded address. Foundry's script.sol and solmate's LibRlp.sol contracts can help achieve this.

References: https://book.getfoundry.sh/reference/forge-std/compute-create-address

https://twitter.com/transmissions11/status/1518507047943245824

<ins>Proof Of Concept</ins>
171: erc20.safeTransferFrom(_msgSender(), address(this), amt);
173: if (erc20.allowance(address(this), address(dripsHub)) == 0) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L171

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L173

416: erc20.safeTransferFrom(msg.sender, address(this), amt);

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L416

533: erc20.safeTransferFrom(msg.sender, address(this), uint128(realBalanceDelta));

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L533

286: erc20.safeTransferFrom(_msgSender(), address(this), amt);
288: if (erc20.allowance(address(this), address(dripsHub)) == 0) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L286

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L288

<ins>Recommended Mitigation Steps</ins>

Use hardcoded address

<a href="#Summary">[GAS‑6]</a><a name="GAS&#x2011;6"> internal functions only called once can be inlined to save gas

<ins>Proof Of Concept</ins>
73: function dripId

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L73

83: function start

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L83

88: function duration

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L88

233: function _receiveDrips
270: function _receiveDrips

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L233

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L270

342: function _squeezeDrips
392: function _squeezeDrips

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L342

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L392

610: function _setDrips

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L610

136: function _erc1967Slot

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L136

151: function _authorizeUpgrade

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L151

300: function _msgData

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L300

106: function _splittable

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L106

106: function _split
117: function _split
143: function _split
262: function _split
283: function _split

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L106

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L117

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L143

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L262

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L283

176: function _collectable

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L176

176: function _collect
185: function _collect

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L176

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L185

199: function _give

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L199

212: function _setSplits

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L212

262: function _splitsHash

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L262

<a href="#Summary">[GAS‑7]</a><a name="GAS&#x2011;7"> Multiple Address Mappings Can Be Combined Into A Single Mapping Of An Address To A Struct, Where Appropriate

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot.

<ins>Proof Of Concept</ins>
88: mapping(address => EnumerableSet.AddressSet) internal _authorized;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L88

90: mapping(address => uint256) public nonce;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L90

<a href="#Summary">[GAS‑8]</a><a name="GAS&#x2011;8"> Optimize names to save gas

Contracts most called functions could simply save gas by function ordering via Method ID. Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because 22 gas are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.

See more <a href="https://medium.com/joyso/solidity-how-does-function-name-affect-gas-consumption-in-smart-contract-47d270d8ac92">here</a>

<ins>Proof Of Concept</ins>

All in-scope contracts

<ins>Recommended Mitigation Steps</ins>

Find a lower method ID name for the most called functions for example Call() vs. Call1() is cheaper by 22 gas For example, the function IDs in the Gauge.sol contract will be the most used; A lower method ID may be given.

<a href="#Summary">[GAS‑9]</a><a name="GAS&#x2011;9"> <x> += <y> Costs More Gas Than <x> = <x> + <y> For State Variables

<ins>Proof Of Concept</ins>
253: amtDeltas[toCycle].thisCycle += finalAmtPerCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L253

284: toCycle -= receivableCycles;
288: amtPerCycle += state.amtDeltas[cycle].thisCycle;
289: receivedAmt += uint128(amtPerCycle);
290: amtPerCycle += state.amtDeltas[cycle].nextCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L284

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L288

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L289

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L290

429: amt += _squeezedAmt(userId, drips, squeezeStartCap, squeezeEndCap);

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L429

495: amt += _drippedAmt(receiver.config.amtPerSec(), start, end);

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L495

572: balance -= uint128(_drippedAmt(receiver.config.amtPerSec(), start, end));

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L572

755: spent += _drippedAmt(amtPerSec, start, end);

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L755

1053: amtDelta.thisCycle += int128(fullCycle - nextCycle);
1054: amtDelta.nextCycle += int128(nextCycle);

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L1053-L1054

632: totalBalances[erc20] += amt;

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L632

636: _dripsHubStorage().totalBalances[erc20] -= amt;

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L636

62: weightSum += receivers[i].weight;

https://github.com/code-423n4/2023-01-drips/tree/main/src/ImmutableSplitsDriver.sol#L62

99: _splitsStorage().splitsStates[userId].balances[assetId].splittable += amt;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L99

128: splitsWeight += currReceivers[i].weight;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L128

159: splitsWeight += currReceivers[i].weight;
162: splitAmt += currSplitAmt;
167: collectableAmt -= splitAmt;
168: balance.collectable += collectableAmt;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L159

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L162

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L167

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L168

235: totalWeight += weight;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L235

<a href="#Summary">[GAS‑10]</a><a name="GAS&#x2011;10"> Public Functions To External

The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.

<ins>Proof Of Concept</ins>
function calcUserId(address userAddr) public view returns (uint256 userId) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L40

function collect(IERC20 erc20, address transferTo) public whenNotPaused returns (uint128 amt) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L60

function give(uint256 receiver, IERC20 erc20, uint128 amt) public whenNotPaused {

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L76

function setDrips(
        IERC20 erc20,
        DripsReceiver[] calldata currReceivers,
        int128 balanceDelta,
        DripsReceiver[] calldata newReceivers,
        
        uint32 maxEndHint1,
        uint32 maxEndHint2,
        address transferTo
    ) public whenNotPaused returns (int128 realBalanceDelta) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L122

function setSplits(SplitsReceiver[] calldata receivers) public whenNotPaused {

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L158

function emitUserMetadata(UserMetadata[] calldata userMetadata) public whenNotPaused {

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L166

function authorize(address user) public {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L106

function unauthorize(address user) public {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L114

function isAuthorized(address sender, address user) public view returns (bool authorized) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L124

function allAuthorized(address sender) public view returns (address[] memory authorized) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L132

function callSigned(
        address sender,
        address to,
        bytes memory data,
        uint256 deadline,
        bytes32 r,
        bytes32 sv
    ) public payable returns (bytes memory returnData) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L164

function callBatched(Call[] memory calls) public payable returns (bytes[] memory returnData) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L193

function registerDriver(address driverAddr) public whenNotPaused returns (uint32 driverId) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L134

function driverAddress(uint32 driverId) public view returns (address driverAddr) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L145

function updateDriverAddress(uint32 driverId, address newDriverAddr) public whenNotPaused {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L152

function nextDriverId() public view returns (uint32 driverId) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L160

function cycleSecs() public view returns (uint32 cycleSecs_) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L169

function totalBalance(IERC20 erc20) public view returns (uint256 balance) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L181

function squeezeDrips(
        uint256 userId,
        IERC20 erc20,
        uint256 senderId,
        bytes32 historyHash,
        DripsHistory[] memory dripsHistory
    ) public whenNotPaused returns (uint128 amt) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L270

function squeezeDripsResult(
        uint256 userId,
        IERC20 erc20,
        uint256 senderId,
        bytes32 historyHash,
        DripsHistory[] memory dripsHistory
    ) public view returns (uint128 amt) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L297

function splittable(uint256 userId, IERC20 erc20) public view returns (uint128 amt) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L317

function collectable(uint256 userId, IERC20 erc20) public view returns (uint128 amt) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L372

function balanceAt(
        uint256 userId,
        IERC20 erc20,
        DripsReceiver[] memory receivers,
        uint32 timestamp
    ) public view returns (uint128 balance) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L460

function setDrips(
        uint256 userId,
        IERC20 erc20,
        DripsReceiver[] memory currReceivers,
        int128 balanceDelta,
        DripsReceiver[] memory newReceivers,
        
        uint32 maxEndHint1,
        uint32 maxEndHint2
    ) public whenNotPaused onlyDriver(userId) returns (int128 realBalanceDelta) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L510

function hashDrips(DripsReceiver[] memory receivers) public pure returns (bytes32 dripsHash) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L546

function hashDripsHistory(
        bytes32 oldDripsHistoryHash,
        bytes32 dripsHash,
        uint32 updateTime,
        uint32 maxEnd
    ) public pure returns (bytes32 dripsHistoryHash) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L557

function splitsHash(uint256 userId) public view returns (bytes32 currSplitsHash) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L587

function nextUserId() public view returns (uint256 userId) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/ImmutableSplitsDriver.sol#L36

function admin() public view returns (address) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L78

function changeAdmin(address newAdmin) public onlyAdmin {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L84

function grantPauser(address pauser) public onlyAdmin {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L90

function revokePauser(address pauser) public onlyAdmin {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L97

function isPauser(address pauser) public view returns (bool isAddrPauser) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L105

function allPausers() public view returns (address[] memory pausersList) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L112

function paused() public view returns (bool isPaused) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L117

function pause() public onlyAdminOrPauser whenNotPaused {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L122

function unpause() public onlyAdminOrPauser whenPaused {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L128

function nextTokenId() public view returns (uint256 tokenId) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L50

function setDrips(
        uint256 tokenId,
        IERC20 erc20,
        DripsReceiver[] calldata currReceivers,
        int128 balanceDelta,
        DripsReceiver[] calldata newReceivers,
        
        uint32 maxEndHint1,
        uint32 maxEndHint2,
        address transferTo
    ) public whenNotPaused onlyHolder(tokenId) returns (int128 realBalanceDelta) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L186

function burn(uint256 tokenId) public override whenNotPaused {

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L244

function approve(address to, uint256 tokenId) public override whenNotPaused {

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L249

function setApprovalForAll(address operator, bool approved) public override whenNotPaused {

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L272

<a href="#Summary">[GAS‑11]</a><a name="GAS&#x2011;11"> Help The Optimizer By Saving A Storage Variable's Reference Instead Of Repeatedly Fetching It

To help the optimizer, declare a storage type variable and use it instead of repeatedly fetching the reference in a map or an array. The effect can be quite significant. As an example, instead of repeatedly calling someMap[someIndex], save its reference like this: SomeStruct storage someStruct = someMap[someIndex] and use it.

<ins>Proof Of Concept</ins>
244: DripsState storage state = _dripsStorage().states[assetId][userId];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L244

248: delete amtDeltas[cycle];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L248

253: amtDeltas[toCycle].thisCycle += finalAmtPerCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L253

286: DripsState storage state = _dripsStorage().states[assetId][userId];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L286

288: amtPerCycle += state.amtDeltas[cycle].thisCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L288

290: amtPerCycle += state.amtDeltas[cycle].nextCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L290

356: DripsState storage state = _dripsStorage().states[assetId][userId];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L356

357: uint32[2 ** 32] storage nextSqueezed = state.nextSqueezed[senderId];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L357

410: DripsState storage sender = _dripsStorage().states[assetId][senderId];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L410

420: _dripsStorage().states[assetId][userId].nextSqueezed[senderId];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L420

481: if (receivers[idxMid].userId < userId) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L481

491: DripsReceiver memory receiver = receivers[idx];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L491

620: DripsState storage state = _dripsStorage().states[assetId][userId];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L620

639: _dripsStorage().states[assetId],

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L639

149: SplitsBalance storage balance = splitsStates[userId].balances[assetId];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L149

<a href="#Summary">[GAS‑12]</a><a name="GAS&#x2011;12"> Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract's gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.

https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Each operation involving a uint8 costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8) as compared to ones involving uint256, due to the compiler having to clear the higher bits of the memory word before operating on the uint8, as well as the associated stack operations of doing so. Use a larger size then downcast where needed

<ins>Proof Of Concept</ins>
25: uint32 public immutable driverId;

https://github.com/code-423n4/2023-01-drips/tree/main/src/AddressDriver.sol#L25

191: uint32 updateTime;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L191

193: uint32 maxEnd;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L193

108: uint8 internal constant _AMT_PER_SEC_EXTRA_DECIMALS = 9;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L108

117: uint32 internal immutable _cycleSecs;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L117

189: uint32 nextReceivableCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L189

195: uint128 balance;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L195

197: uint32 currCycleConfigs;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L197

208: int128 thisCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L208

210: int128 nextCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L210

237: uint32 receivableCycles;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L237

238: uint32 fromCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L238

239: uint32 toCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L239

240: int128 finalAmtPerCycle;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L240

357: uint32[2 ** 32] storage nextSqueezed = state.nextSqueezed[senderId];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L357

365: uint32 cycleStart = _currCycleStart();

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L365

421: uint32 squeezeEndCap = _currTimestamp();

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L421

425: uint32 squeezeStartCap = nextSqueezed[currCycleConfigs - i];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L425

487: uint32 updateTime = dripsHistory.updateTime;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L487

488: uint32 maxEnd = dripsHistory.maxEnd;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L488

623: uint32 lastUpdate = state.updateTime;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L623

624: uint128 newBalance;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L624

625: uint32 newMaxEnd;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L625

627: uint32 currMaxEnd = state.maxEnd;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L627

923: uint32 currStartCycle = _cycleOf(currStart);

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L923

924: uint32 newStartCycle = _cycleOf(newStart);

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L924

949: uint32 startCycle = _cycleOf(start);

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L949

997: uint40 end = uint40(start) + receiver.config.duration();

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L997

1135: uint32 currTimestamp = _currTimestamp();

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L1135

58: uint8 public constant AMT_PER_SEC_EXTRA_DECIMALS = _AMT_PER_SEC_EXTRA_DECIMALS;

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L58

64: uint32 public constant TOTAL_SPLITS_WEIGHT = _TOTAL_SPLITS_WEIGHT;

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L64

97: uint32 nextDriverId;

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L97

119: uint32 driverId = uint32(userId >> DRIVER_ID_OFFSET);

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L119

15: uint32 public immutable driverId;

https://github.com/code-423n4/2023-01-drips/tree/main/src/ImmutableSplitsDriver.sol#L15

17: uint32 public immutable totalSplitsWeight;

https://github.com/code-423n4/2023-01-drips/tree/main/src/ImmutableSplitsDriver.sol#L17

25: uint32 public immutable driverId;

https://github.com/code-423n4/2023-01-drips/tree/main/src/NFTDriver.sol#L25

11: uint32 weight;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L11

22: uint32 internal constant _TOTAL_SPLITS_WEIGHT = 1_000_000;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L22

88: uint128 splittable;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L88

90: uint128 collectable;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L90

157: uint32 splitsWeight = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L157

161: uint128((uint160(collectableAmt) * splitsWeight) / _TOTAL_SPLITS_WEIGHT - splitAmt);

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L161

228: uint64 totalWeight = 0;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L228

233: uint32 weight = receiver.weight;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L233

<a href="#Summary">[GAS‑13]</a><a name="GAS&#x2011;13"> ++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- And While-loops

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas PER LOOP

<ins>Proof Of Concept</ins>
196: for (uint256 i = 0; i < calls.length; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L196

247: for (uint32 cycle = fromCycle; cycle < toCycle; cycle++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L247

287: for (uint32 cycle = fromCycle; cycle < toCycle; cycle++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L287

358: for (uint256 i = 0; i < squeezedNum; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L358

422: for (uint256 i = 1; i <= dripsHistory.length && i <= currCycleConfigs; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L422

450: for (uint256 i = 0; i < dripsHistory.length; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L450

479: for (uint256 idxCap = receivers.length; idx < idxCap;) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L479

490: for (; idx < receivers.length; idx++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L490

563: for (uint256 i = 0; i < receivers.length; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L563

664: for (uint256 i = 0; i < newReceivers.length; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L664

613: for (uint256 i = 0; i < userMetadata.length; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/DripsHub.sol#L613

61: for (uint256 i = 0; i < receivers.length; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/ImmutableSplitsDriver.sol#L61

127: for (uint256 i = 0; i < currReceivers.length; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L127

158: for (uint256 i = 0; i < currReceivers.length; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L158

231: for (uint256 i = 0; i < receivers.length; i++) {

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L231

<a href="#Summary">[GAS‑14]</a><a name="GAS&#x2011;14"> Using unchecked blocks to save gas

Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn’t possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block

<ins>Proof Of Concept</ins>
780: require(_isOrdered(receivers[i - 1], receiver), "Receivers not sorted");

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L780

<a href="#Summary">[GAS‑15]</a><a name="GAS&#x2011;15"> Using storage instead of memory saves gas

When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD rather than a cheap stack read. Instead of declearing the variable with the memory keyword, declaring the variable with the storage keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time it makes sense to read the whole struct/array into a memory variable, is if the full struct/array is being returned by the function, is being passed to a function that requires memory, or if the array/struct is being read from another memory array/struct

<ins>Proof Of Concept</ins>
197: Call memory call = calls[i];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L197

423: DripsHistory memory drips = dripsHistory[dripsHistory.length - i];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L423

451: DripsHistory memory drips = dripsHistory[i];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L451

491: DripsReceiver memory receiver = receivers[idx];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L491

564: DripsReceiver memory receiver = receivers[i];
778: DripsReceiver memory receiver = receivers[i];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L564

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L778

665: DripsReceiver memory receiver = newReceivers[i];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L665

564: DripsReceiver memory receiver = receivers[i];
778: DripsReceiver memory receiver = receivers[i];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L564

https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L778

232: SplitsReceiver memory receiver = receivers[i];

https://github.com/code-423n4/2023-01-drips/tree/main/src/Splits.sol#L232

<a href="#Summary">[GAS‑16]</a><a name="GAS&#x2011;16"> Use uint256(1)/uint256(2) instead for true and false boolean states

If you don't use boolean for storage you will avoid Gwarmaccess 100 gas. In addition, state changes of boolean from true to false can cost up to ~20000 gas rather than uint256(2) to uint256(1) that would cost significantly less.

<ins>Proof Of Concept</ins>
    struct ManagedStorage {
        bool isPaused;
        EnumerableSet.AddressSet pausers;
    }

https://github.com/code-423n4/2023-01-drips/blob/main/src/Managed.sol#L40-L43

74: _managedStorage().isPaused = true;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L74

123: _managedStorage().isPaused = true;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L123

129: _managedStorage().isPaused = false;

https://github.com/code-423n4/2023-01-drips/tree/main/src/Managed.sol#L129

#0 - c4-judge

2023-02-24T11:02:05Z

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