Platform: Code4rena
Start Date: 21/11/2022
Pot Size: $90,500 USDC
Total HM: 18
Participants: 101
Period: 7 days
Judge: Picodes
Total Solo HM: 4
Id: 183
League: ETH
Rank: 91/101
Findings: 1
Award: $39.65
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: gzeon
Also found by: 0xPanda, 0xSmartContract, B2, Deivitto, Diana, JohnSmith, PaludoX0, Rahoz, RaymondFam, ReyAdmirado, Rolezn, Schlagatron, Secureverse, Tomio, __141345__, adriro, ajtra, aphak5010, c3phas, chaduke, codeislight, cryptonue, datapunk, dharma09, halden, karanctf, keccak123, oyc_109, pavankv, sakshamguruji, saneryee, unforgiven
39.6537 USDC - $39.65
Issue | Instances | |
---|---|---|
[G-001] | x += y or x -= y costs more gas than x = x + y or x = x -y for state variables | 6 |
[G-002] | storage pointer to a structure is cheaper than copying each value of the structure into memory , same for array and mapping | 1 |
[G-003] | Duplicated require()/revert() checks should be refactored to a modifier or function | 13 |
[G-004] | Functions guaranteed to revert when called by normal users can be marked payable | 19 |
[G-005] | Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate | 1 |
Using the addition operator instead of plus-equals saves 113 gas. Usually does not work with struct and mappings.
Total:6
361: producerState.rewardStates[rewardTokens[i]] += r;
85: balanceOf[msg.sender] -= amount;
124: balanceOf[to] += amount;
119: balanceOf[from] -= amount;
124: balanceOf[to] += amount;
src/vaults/PxGmxReward.sol#L95
95: rewardState += rewardAmount;
storage
pointer to a structure is cheaper than copying each value of the structure into memory
, same for array
and mapping
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.
Total:1
386: ERC20[] memory rewardTokens = p.rewardTokens;
require()/revert()
checks should be refactored to a modifier or functionDuplicated require()/revert()
checks should be refactored to a modifier or function.
Total:13
467: if (address(producerToken) == address(0)) revert ZeroAddress();
468: if (address(rewardToken) == address(0)) revert ZeroAddress();
441: if (recipient == address(0)) revert ZeroAddress();
467: if (address(producerToken) == address(0)) revert ZeroAddress();
375: if (user == address(0)) revert ZeroAddress();
466: if (lpContract.code.length == 0) revert NotContract();
830: if (amount == 0) revert ZeroAmount();
831: if (receiver == address(0)) revert ZeroAddress();
829: if (token == address(0)) revert ZeroAddress();
728: if (!gmxVault.whitelistedTokens(token)) revert InvalidToken(token);
131: if (_platform == address(0)) revert ZeroAddress();
421: if (receiver == address(0)) revert ZeroAddress();
419: if (minUsdg == 0) revert ZeroAmount();
153: if (_platform == address(0)) revert ZeroAddress();
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.
Total:19
93: function setProducer(address _producer) external onlyOwner {
465: ) external onlyOwner {
465: ) external onlyOwner {
272: function configureGmxState() external onlyOwner whenPaused {
300: function setFee(Fees f, uint256 fee) external onlyOwner {
828: ) external onlyPirexRewards {
865: ) external onlyOwner {
884: function setVoteDelegate(address voteDelegate) external onlyOwner {
895: function clearVoteDelegate() public onlyOwner {
909: function setPauseState(bool state) external onlyOwner {
94: function setWithdrawalPenalty(uint256 penalty) external onlyOwner {
106: function setPlatformFee(uint256 fee) external onlyOwner {
118: function setCompoundIncentive(uint256 incentive) external onlyOwner {
130: function setPlatform(address _platform) external onlyOwner {
104: function setPoolFee(uint24 _poolFee) external onlyOwner {
116: function setWithdrawalPenalty(uint256 penalty) external onlyOwner {
128: function setPlatformFee(uint256 fee) external onlyOwner {
140: function setCompoundIncentive(uint256 incentive) external onlyOwner {
152: function setPlatform(address _platform) external onlyOwner {
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.
Total:1
23: mapping(ERC20 => uint256) rewardStates; 24: mapping(address => mapping(ERC20 => address)) rewardRecipients;
#0 - c4-judge
2022-12-05T14:04:40Z
Picodes marked the issue as grade-b
#1 - drahrealm
2022-12-09T06:31:19Z
Most findings already exist on earlier confirmed findings, otherwise deemed invalid/minor
#2 - c4-sponsor
2022-12-09T06:31:23Z
drahrealm marked the issue as sponsor disputed