PoolTogether - ReyAdmirado's results

A protocol for no-loss prize savings

General Information

Platform: Code4rena

Start Date: 07/07/2023

Pot Size: $121,650 USDC

Total HM: 36

Participants: 111

Period: 7 days

Judge: Picodes

Total Solo HM: 13

Id: 258

League: ETH

PoolTogether

Findings Distribution

Researcher Performance

Rank: 70/111

Findings: 1

Award: $24.30

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Awards

24.2984 USDC - $24.30

Labels

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

External Links

1. State variables only set in the constructor should be declared immutable.

Avoids a Gsset (20000 gas) in the constructor, and replaces each Gwarmaccess (100 gas) with a PUSH32 (3 gas). While strings are not value types, and therefore cannot be immutable/constant if not hard-coded outside of the constructor, the same behavior can be achieved by making the current contract abstract with virtual functions for the string accessors, and having a child contract override the functions with the hard-coded implementation-specific values.

2. state variables can be packed into fewer storage slots

If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables are also cheaper.

drawManager#L222 which is 20 bytes can be put together with claimCount#L240 and canaryClaimCount#L243 and largestTierClaimed#L246 which will be less than 32 bytes so they will only use one slot instead of current 2. also its used in the same function with all 3 once which will reduce gas cost as well

3. Multiple address/ID mappings can be combined into a single mapping of an address/ID 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. Finally, 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.

all 3 mappings can be a mapping to a struct and also in that struct mapping of #L36 and #L42 also can be a mapping to another struct

4. state variables should be cached in stack variables rather than re-reading them from storage (ones not found in c4 audit)

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replace each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

prizeTokenPerShare should be cached before #L337 because its read from storage in #L364 as well and the second use is inside a for loop as well so caching this state var will save lots of gas

prizeTokenPerShare can be cached before #L529 so we can use the cached version there and in #L540

if we change the #L538 from using -= to using just subtraction we can save gas if we had _reserve cached. so we can cache _reserve before #L535 and save gas

same logic as last one for _reserve but here even if the first condition reverts we save 100 gas. cache before the if statement #L336

same as above for claimerRewards[msg.sender]

same logic as above for _yieldFeeTotalSupply

lastClosedDrawId should be cached before #611 and use the cached version for that line and #L618 to save 100 gas

if the condition be true we will have a extra SLOAD for _liquidationPair so we can cache it before and risk losing only 3 gas but save 100

if the condition be true we will have a extra SLOAD for _claimer so we can cache it before and risk losing only 3 gas but save 100

cache answer to _currentExchangeRate() and use it in the #L1111 emit

5. can make the variable outside the loop to save gas

make the variable outside the loop and only give the value to variable inside

6. it costs more gas to initialize non-constant/non-immutable variables to zero than to let the default of zero be applied

7. require() or revert() statements that are cheaper should be at the top of the function

Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.

the first if statement for revert is the most expensive to run so we can do it last

8. Ternary over if ... else

Using ternary operator instead of the if else statement saves gas.

9. Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier or require such as onlyOwner-admin is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2) which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost.

10. 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 here. you can use this tool to get the optimized version for function and properties signitures

11. Use hardcoded 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. - Reference

#0 - c4-judge

2023-07-18T19:00:19Z

Picodes marked the issue as grade-b

#1 - PierrickGT

2023-09-07T23:19:30Z

1 - Has been removed 2 - Has been fixed 3 - We would lose in code legibility 4, 5 - Has been fixed 6 - We would lose in code legibility 7, 8 - Has been fixed 9 - These functions are not payable so they shouldn't be marked as such 10 - We would lose in code legibility 11 - Does not save any gas

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