Wenwin contest - saneryee's results

The next generation of chance-based gaming.

General Information

Platform: Code4rena

Start Date: 06/03/2023

Pot Size: $36,500 USDC

Total HM: 8

Participants: 93

Period: 3 days

Judge: cccz

Total Solo HM: 3

Id: 218

League: ETH

Wenwin

Findings Distribution

Researcher Performance

Rank: 82/93

Findings: 1

Award: $12.72

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

12.7206 USDC - $12.72

Labels

bug
G (Gas Optimization)
grade-b
sponsor acknowledged
G-18

External Links

Gas Optimizations Report

IssueInstances
[G-001]x += y or x -= y costs more gas than x = x + y or x = x - y for state variables7
[G-002]storage pointer to a structure is cheaper than copying each value of the structure into memory, same for array and mapping2
[G-003]Functions guaranteed to revert when called by normal users can be marked payable4
[G-004]Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate1
[G-005]internal functions only called once can be inlined to save gas1
[G-006]Replace modifier with function3
[G-007]Expressions that cannot be overflowed can be unchecked2

[G-001] x += y or x -= y costs more gas than x = x + y or x = x - y for state variables

Impact

Using the addition operator instead of plus-equals saves 113 gas. Usually does not work with struct and mappings.

Findings

Total:7

src/staking/StakedTokenLock.sol#L43

43:    depositedBalance -= amount;

src/staking/StakedTokenLock.sol#L30

30:    depositedBalance += amount;

src/TicketUtils.sol#L96

96:    winTier += uint8(intersection & uint120(1));

src/TicketUtils.sol#L29

29:    ticketSize += (ticket & uint256(1));

src/LotteryMath.sol#L84

84:    bonusMulti += (excessPot * EXCESS_BONUS_ALLOCATION) / (ticketsSold * expectedPayout);

src/LotteryMath.sol#L55

55:    newProfit -= int256(expectedRewardsOut);

src/LotteryMath.sol#L64

64:    excessPotInt -= int256(fixedJackpotSize);

[G-002] storage pointer to a structure is cheaper than copying each value of the structure into memory, same for array and mapping

Impact

It may not be obvious, but every time you copy a storage struct/array/mapping to a memory variable, you are literally copying each member by reading it from storage, which is expensive. And when you use the storage keyword, you are just storing a pointer to the storage, which is much cheaper.

Findings

Total:2

src/TicketUtils.sol#L54

54:    uint8[] memory numbers = new uint8[](selectionSize);

src/TicketUtils.sol#L63

63:    bool[] memory selected = new bool[](selectionMax);

Recommendation

Using storage instead of memory

[G-003] Functions guaranteed to revert when called by normal users can be marked payable

Impact

If a function modifier such as onlyOwner 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.

Findings

Total:4

src/staking/StakedTokenLock.sol#L24

24:    function deposit(uint256 amount) external override onlyOwner {

src/staking/StakedTokenLock.sol#L37

37:    function withdraw(uint256 amount) external override onlyOwner {

src/RNSourceController.sol#L77

77:    function initSource(IRNSource rnSource) external override onlyOwner {

src/RNSourceController.sol#L89

89:    function swapSource(IRNSource newSource) external override onlyOwner {

Recommendation

Mark the function as payable.

[G-004] Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate

Impact

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.

Findings

Total:1

src/staking/Staking.sol#L19-L20

19:    mapping(address => uint256) public override userRewardPerTokenPaid;
20:        mapping(address => uint256) public override rewards;

[G-005] internal functions only called once can be inlined to save gas

Impact

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

Findings

Total:1

src/LotterySetup.sol#L160

160:    function _baseJackpot(uint256 _initialPot) internal view returns (uint256) {

[G-006] Replace modifier with function

Impact

Modifiers make code more elegant, but cost more than normal functions.

Findings

Total:3

src/LotterySetup.sol#L104

104:    modifier requireJackpotInitialized() {

src/Lottery.sol#L60

60:    modifier onlyWhenExecutingDraw() {

src/Lottery.sol#L52

52:    modifier whenNotExecutingDraw() {

[G-007] Expressions that cannot be overflowed can be unchecked

Impact

There are several cases that do not lead to overflow and can be unchecked.

  • a previous push in array
  • If an underflow occurs, the next statement will revert
  • There is a check in the previous code.

Findings

Total:2

src/LotterySetup.sol#L171

171:    if ((rewards[winTier] % divisor) != 0) {

src/TicketUtils.sol#L58

58:    numbers[i] = uint8(randomNumber % currentSelectionCount);

#0 - c4-judge

2023-03-12T14:26:24Z

thereksfour marked the issue as grade-b

#1 - c4-sponsor

2023-03-14T13:07:53Z

TutaRicky marked the issue as sponsor acknowledged

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