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
Rank: 14/26
Findings: 1
Award: $131.98
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: descharre
Also found by: 0xA5DF, 0xSmartContract, Aymen0909, Deivitto, NoamYakov, ReyAdmirado, Rolezn, chaduke, cryptostellar5, matrix_0wl
131.9758 USDC - $131.98
Issue | Contexts | Estimated Gas Saved | |
---|---|---|---|
GAS‑1 | abi.encode() is less efficient than abi.encodepacked() | 4 | 400 |
GAS‑2 | Setting the constructor to payable | 7 | 91 |
GAS‑3 | Using fixed bytes is cheaper than using string | 1 | - |
GAS‑4 | Using delete statement can save gas | 11 | - |
GAS‑5 | Use hardcoded address instead address(this) | 6 | - |
GAS‑6 | internal functions only called once can be inlined to save gas | 23 | 506 |
GAS‑7 | Multiple Address Mappings Can Be Combined Into A Single Mapping Of An Address To A Struct, Where Appropriate | 2 | - |
GAS‑8 | Optimize names to save gas | 8 | 176 |
GAS‑9 | <x> += <y> Costs More Gas Than <x> = <x> + <y> For State Variables | 21 | - |
GAS‑10 | Public Functions To External | 42 | - |
GAS‑11 | Help The Optimizer By Saving A Storage Variable’s Reference Instead Of Repeatedly Fetching It | 15 | - |
GAS‑12 | Usage of uints /ints smaller than 32 bytes (256 bits) incurs overhead | 53 | - |
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-loops | 15 | 525 |
GAS‑14 | Using unchecked blocks to save gas | 1 | 136 |
GAS‑15 | Using storage instead of memory saves gas | 10 | 3500 |
GAS‑16 | Use uint256(1) /uint256(2) instead for true and false boolean states | 3 | 15000 |
Total: 222 contexts over 16 issues
abi.encode()
is less efficient than abi.encodepacked()
See for more information: https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison
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
constructor
to payable
Saves ~13 gas per instance
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
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.
82: string internal constant CALL_SIGNED_TYPE_NAME = "CallSigned("
https://github.com/code-423n4/2023-01-drips/tree/main/src/Caller.sol#L82
delete
statement can save gas478: 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
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
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
Use hardcoded address
internal
functions only called once can be inlined to save gas73: 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
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.
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
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>
All in-scope contracts
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.
<x> += <y>
Costs More Gas Than <x> = <x> + <y>
For State Variables253: 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
The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.
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
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.
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
uints
/ints
smaller than 32 bytes (256 bits) incurs overheadWhen 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
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
++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-loopsThe 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
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
unchecked
blocks to save gasSolidity 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
780: require(_isOrdered(receivers[i - 1], receiver), "Receivers not sorted");
https://github.com/code-423n4/2023-01-drips/tree/main/src/Drips.sol#L780
storage
instead of memory
saves gasWhen 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
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
uint256(1)
/uint256(2)
instead for true
and false
boolean statesIf 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.
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