code-423n4 / 2022-03-paladin-findings

0 stars 0 forks source link

Gas Optimizations #9

Open code423n4 opened 2 years ago

code423n4 commented 2 years ago

Several tricks can be applied here, but because of the contract size limit, it's better to prioritize based on what would make the most sense. (probably 3. example 2 saves the most! that's why I also opened a demo PR for it)

  1. There are lots of places while are reading struct from the storage, it keeps the variable as "storage", but if we don't need to write it and just need the value, (and want to read it multiple times) copying them to memory will make it cheaper:

For example:

  1. In function _updateDropPerSecond, it's worth to cache the 2 important variables in the memory which were used multiple times throughout the calculation: endDropPerSecond and currentDropPerSecond. Each will save 200 gas from warm SLOAD.

(this is not the highest priority since drop will only update once a month)

  1. batch variables: if 2 variables are always updated (and read) together, it's better to batch them into 1 single storage slot.

Example 1: in _updateDropPerSecond, we can batch currentDropPerSecond and lastDropUpdate by changing the both types to uint128. This will saves around 1000 gas because it saves 1 SSTORE. ( But this function will only benefit once per month, so not good enough)

Example 2: this is gonna make things a lot better: there are 2 maps that always got read / write together, that are userRewardIndex, claimableRewards and rewardLastUpdate. By defining a new struct, packing all 3 variables into 1 single uint256, and create a map to link user address => new struct, you save tons of gas because we only do 1 SLOAD and SSTORE instead of 3 in all these functions. More importantly, this directly influence the function _updateUserRewards, which is used heavily in almost all other functions. By running the test report, this optimization along will save about 15000 gas on single updateUserReward, and can save up to 60K in transfer and transferFrom.

I opened this PR: https://github.com/antoncoding/2022-03-paladin/pull/1 to show the gas difference and the changes.

  1. final small thing: bonusLockVoteRatio should be defined as constant, but i only affect view functions so also not a big deal.

Gotta say, the code is in really good quality and already very optimized, I hope this help your team get to the next level!

Kogaroshi commented 2 years ago

QA & gas optimizations changes are done in the PR: https://github.com/PaladinFinance/Paladin-Tokenomics/pull/6 (some changes/tips were implemented, others are noted but won't be applied)

Kogaroshi commented 2 years ago

Changes in the reward state storage (as proposed in 3. example2) were partially applied (see commit https://github.com/PaladinFinance/Paladin-Tokenomics/pull/6/commits/6677a2a07fe85fd05258c3fe4249ca55c69c2155) => Instead of the proposed Struct, we use a Struct combining the reward index, and the last update timestamp, so that Struct can be used both for the global reward state, and the user reward states.