Platform: Code4rena
Start Date: 30/05/2023
Pot Size: $300,500 USDC
Total HM: 79
Participants: 101
Period: about 1 month
Judge: Trust
Total Solo HM: 36
Id: 242
League: ETH
Rank: 44/101
Findings: 1
Award: $610.33
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Raihan
Also found by: 0x11singh99, 0xAnah, 0xSmartContract, 0xn006e7, Aymen0909, DavidGiladi, IllIllI, JCN, Jorgect, MohammedRizwan, Rageur, ReyAdmirado, Rickard, Rolezn, SAQ, SM3_SS, Sathish9098, TheSavageTeddy, hunter_w3b, kaveyjoe, lsaudit, matrix_0wl, naman1778, petrichor, shamsulhaq123, wahedtalash77
610.3258 USDC - $610.33
S.N. | Issues | Instances | Gas Savings |
---|---|---|---|
G-01 | Using XOR (^) and OR (|) bitwise equivalents | 1 | 73 |
G-02 | Use a positive conditional flow to save a NOT opcode | 4 | 12 |
G-03 | Emitting events can be rearranged to save gas | 9 | 2244 |
G-04 | Don't calculate constants | 4 | - |
G-05 | Gas savings can be achieved by changing the model for assigning value to the storage structure | 10 | - |
G-06 | Avoid caching the value of a particular key of a mapping/member of a struct when that cache is being used only once | 7 | 5409 |
G-07 | Avoid caching the result of a function call when that result is being used only once | 4 | 350 |
G-08 | Cache calldata/memory pointers for complex types to avoid complex offset calculations | 32 | - |
G-09 | Fail as early as possible | 15 | - |
G-10 | Save a storage variable reference instead of repeatedly fetching the value in a mapping or an array | 146 | 5840 |
G-11 | Use already instantiated storage reference rather than repeatedly fetching the value in a mapping or array | 5 | 200 |
^
) and OR (|
) bitwise equivalentsNote : No test has been provided for the function used in this instance from protocol's side(using which gas savings could be calculated).That's why Remix has been used to demonstrate gas savings via the given POC
On Remix, given only uint256 types, the following are logical equivalents, but don’t cost the same amount of gas:
(a != b || c != d || e != f) costs 571 ((a ^ b) | (c ^ d) | (e ^ f)) != 0 costs 498 // (saving 73 gas)
Logic POC
Given 4 variables a
, b
, c
and d
represented as such:
0 0 0 0 0 1 1 0 <- a 0 1 1 0 0 1 1 0 <- b 0 0 0 0 0 0 0 0 <- c 1 1 1 1 1 1 1 1 <- d
To have a == b
means that every 0
and 1
match on both variables. Meaning that a XOR (operator ^
) would evaluate to 0 ((a ^ b) == 0)
, as it excludes by definition any equalities.Now, if a != b
, this means that there’s at least somewhere a 1
and a 0
not matching between a and b, making (a ^ b) != 0
.
Both formulas are logically equivalent and using the XOR bitwise operator costs actually the same amount of gas:
function xOrEquivalence(uint a, uint b) external returns (bool) { //return a != b; //370 //return a ^ b != 0; //370 }
However, it is much cheaper to use the bitwise OR operator (|
) than comparing the truthy or falsy values:
function xOrOrEquivalence(uint a, uint b, uint c, uint d) external returns (bool) { //return (a != b || c != d); // 495 //return (a ^ b | c ^ d) != 0; // 442 }
These are logically equivalent too, as the OR
bitwise operator (|
) would result in a 1
somewhere if any value is not 0
between the XOR (^
) statements, meaning if any XOR (^
) statement verifies that its arguments are different.
Coded Proof of Concept :
This little POC also proves that the formulas are equivalent:
function test_XorEq(uint a, uint b, uint c, uint d, uint e, uint f) external { assert((a != b || c != d || e != f) == ((a ^ b) | (c ^ d) | (e ^ f)) != 0); }
Consider applying the suggested equivalence and add a comment mentioning what this is equivalent to, as this is less human-readable, but still understandable once it’s been taught.
1 instance in 1 file
File: src/ulysses-omnichain/BranchBridgeAgent.sol 813: if ( 814: _hTokens.length != _tokens.length || _tokens.length != _amounts.length 815: || _amounts.length != _deposits.length 816: ) revert InvalidInput();
recommended code :
813: if ( 814: ((_hTokens.length ^ _tokens.length) | ( _tokens.length ^ _amounts.length) | 815: | (_amounts.length ^ _deposits.length)) != 0 816: ) revert InvalidInput();
By switiching to a positive conditional flow in if
statement, a NOT opcode(costing 3 gas) can be saved
4 instances in 2 files
File: src/ulysses-omnichain/RootBridgeAgent.sol 1146: if (!executionHistory[fromChainId][uint32(bytes4(data[1:5]))]) { 1147: //Toggle Nonce as executed 1148: executionHistory[fromChainId][nonce] = true; 1149: 1150: //Retry failed fallback 1151: (success, result) = (false, ""); 1152: } else { 1153: _forceRevert(); 1154: //Return true to avoid triggering anyFallback in case of `_forceRevert()` failure 1155: return (true, "already executed tx"); 1156: }
recommended code :
1146: if (executionHistory[fromChainId][uint32(bytes4(data[1:5]))]) { 1147: _forceRevert(); 1148: //Return true to avoid triggering anyFallback in case of `_forceRevert()` failure 1149: return (true, "already executed tx"); 1150: } else { 1151: //Toggle Nonce as executed 1152: executionHistory[fromChainId][nonce] = true; 1153: 1154: //Retry failed fallback 1155: (success, result) = (false, ""); 1156: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootBridgeAgent.sol#L1146
File: src/ulysses-omnichain/CoreBranchRouter.sol 164: if (!IPort(localPortAddress).isBridgeAgentFactory(_newBridgeAgentFactoryAddress)) { 165: IPort(localPortAddress).addBridgeAgentFactory(_newBridgeAgentFactoryAddress); 166: } else { 167: IPort(localPortAddress).toggleBridgeAgentFactory(_newBridgeAgentFactoryAddress); 168: }
recommended code :
164: if (IPort(localPortAddress).isBridgeAgentFactory(_newBridgeAgentFactoryAddress)) { 165: IPort(localPortAddress).toggleBridgeAgentFactory(_newBridgeAgentFactoryAddress); 166: } else { 167: IPort(localPortAddress).addBridgeAgentFactory(_newBridgeAgentFactoryAddress); 168: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/CoreBranchRouter.sol#L164
File: src/ulysses-omnichain/CoreBranchRouter.sol 190: if (!IPort(localPortAddress).isStrategyToken(_underlyingToken)) { 191: IPort(localPortAddress).addStrategyToken(_underlyingToken, _minimumReservesRatio); 192: } else { 193: IPort(localPortAddress).toggleStrategyToken(_underlyingToken); 194: }
recommended code :
190: if (IPort(localPortAddress).isStrategyToken(_underlyingToken)) { 191: IPort(localPortAddress).toggleStrategyToken(_underlyingToken); 192: } else { 193: IPort(localPortAddress).toggleStrategyToken(_underlyingToken); 194: IPort(localPortAddress).addStrategyToken(_underlyingToken, _minimumReservesRatio); 195: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/CoreBranchRouter.sol#L190
File: src/ulysses-omnichain/CoreBranchRouter.sol 212: if (!IPort(localPortAddress).isPortStrategy(_portStrategy, _underlyingToken)) { 213: //Add new Port Strategy if new. 214: IPort(localPortAddress).addPortStrategy(_portStrategy, _underlyingToken, _dailyManagementLimit); 215: } else if (_isUpdateDailyLimit) { 216: //Or Update daily limit. 217: IPort(localPortAddress).updatePortStrategy(_portStrategy, _underlyingToken, _dailyManagementLimit); 218: } else { 219: //Or Toggle Port Strategy. 220: IPort(localPortAddress).togglePortStrategy(_portStrategy, _underlyingToken); 221: }
recommended code :
213: if (IPort(localPortAddress).isPortStrategy(_portStrategy, _underlyingToken)) { 214: //Toggle Port Strategy 215: IPort(localPortAddress).togglePortStrategy(_portStrategy, _underlyingToken); 216: } else if (_isUpdateDailyLimit) { 217: //Or Update daily limit. 218: IPort(localPortAddress).updatePortStrategy(_portStrategy, _underlyingToken, _dailyManagementLimit); 219: } else { 220: //Or Add new Port Strategy if new. 221: IPort(localPortAddress).addPortStrategy(_portStrategy, _underlyingToken, _dailyManagementLimit); 222: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/CoreBranchRouter.sol#L212
In the following instances,storage variable is being cached in memory and that cache is being used only once as event paramter.Thus,by rearranging the order in which events are emitted,gas costs incurred due to unnecessary memory operations can be saved.With the suggested arrangement,each time the functions are triggered (~16 gas) gas savings will be achieved.
9 instances in 4 files
File: src/governance/GovernorBravoDelegator.sol 47: address oldImplementation = implementation; 48: implementation = implementation_; 49: 50: emit NewImplementation(oldImplementation, implementation);
recommended code :
47: emit NewImplementation(implementation, implementation_); 48: implementation = implementation_;
https://github.com/code-423n4/2023-05-maia/blob/main/src/governance/GovernorBravoDelegator.sol#L50
File: src/governance/GovernorBravoDelegateMaia.sol 403: uint256 oldVotingDelay = votingDelay; 404: votingDelay = newVotingDelay; 405: 406: emit VotingDelaySet(oldVotingDelay, votingDelay);
recommended code :
403: emit VotingDelaySet(votingDelay, newVotingDelay); 404: votingDelay = newVotingDelay;
File: src/governance/GovernorBravoDelegateMaia.sol 417: uint256 oldVotingPeriod = votingPeriod; 418: votingPeriod = newVotingPeriod; 419: 420: emit VotingPeriodSet(oldVotingPeriod, votingPeriod);
recommended code :
417: emit VotingPeriodSet(votingPeriod, newVotingPeriod); 418: votingPeriod = newVotingPeriod;
File: src/governance/GovernorBravoDelegateMaia.sol 432: uint256 oldProposalThreshold = proposalThreshold; 433: proposalThreshold = newProposalThreshold; 434: 435: emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold);
recommended code :
432: emit ProposalThresholdSet(proposalThreshold, newProposalThreshold); 433: proposalThreshold = newProposalThreshold;
File: src/governance/GovernorBravoDelegateMaia.sol 457: address oldGuardian = whitelistGuardian; 458: whitelistGuardian = account; 459: 460: emit WhitelistGuardianSet(oldGuardian, whitelistGuardian);
recommended code :
457: emit WhitelistGuardianSet(whitelistGuardian, account); 458: whitelistGuardian = account;
File: src/governance/GovernorBravoDelegateMaia.sol 483: // Save current value, if any, for inclusion in log 484: address oldPendingAdmin = pendingAdmin; 485: 486: // Store pendingAdmin with value newPendingAdmin 487: pendingAdmin = newPendingAdmin; 488: 489: // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) 490: emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
recommended code :
483: emit NewPendingAdmin(pendingAdmin, newPendingAdmin); 484: 485: // Store pendingAdmin with value newPendingAdmin 486: pendingAdmin = newPendingAdmin;
Here, 1 SLOAD(costing 2100) additional gas along with 16 gas can be saved by directly emiitting address(0) instead of pendingAdmin
(which is a state variable)
File: src/governance/GovernorBravoDelegateMaia.sol 503: // Save current values for inclusion in log 504: address oldAdmin = admin; 505: address oldPendingAdmin = pendingAdmin; 506: 507: // Store admin with value pendingAdmin 508: admin = pendingAdmin; 509: 510: // Clear the pending value 511: pendingAdmin = address(0); 512: 513: emit NewAdmin(oldAdmin, admin); 514: emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); // @audit : emit `address(0)` directly instead of `pendingAdmin` which is being assigned the value `address(0)` saving 1 SLOAD
recommended code :
503: // Save current values for inclusion in log 504: address oldAdmin = admin; 505: 506: // Store admin with value pendingAdmin 507: admin = pendingAdmin; 508: 509: emit NewAdmin(oldAdmin, admin); 510: 511: emit NewPendingAdmin(pendingAdmin, address(0)); 512: 513: // Clear the pending value 514: pendingAdmin = address(0);
File: src/erc-20/ERC20MultiVotes.sol 97: uint256 oldMax = maxDelegates; 98: maxDelegates = newMax; 99: 100: emit MaxDelegatesUpdate(oldMax, newMax);
recommended code :
97: emit MaxDelegatesUpdate(maxDelegates, newMax); 98: maxDelegates = newMax;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20MultiVotes.sol#L100
File: src/erc-20/ERC20Gauges.sol 456: uint256 oldMax = maxGauges; 457: maxGauges = newMax; 458: 459: emit MaxGaugesUpdate(oldMax, newMax);
recommended code :
456: emit MaxGaugesUpdate(maxGauges, newMax); 457: maxGauges = newMax;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Gauges.sol#L459
Assigning constant variables based on the result of a mathematical calculation wastes gas as constant variables need to be revaluated each time they are accessed.
4 instances in 3 files
File: src/talos/TalosStrategyVanilla.sol 47: uint24 private constant protocolFee = 2 * 1e5; //20%
recommended code :
47: uint24 private constant protocolFee = 200000; //20%
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/TalosStrategyVanilla.sol#L47
File: src/hermes/minters/BaseV2Minter.sol 24: uint256 internal constant week = 86400 * 7;
recommended code :
24: uint256 internal constant week = 604800;
https://github.com/code-423n4/2023-05-maia/blob/main/src/hermes/minters/BaseV2Minter.sol#L24
File: src/ulysses-omnichain/lib/AnycallFlags.sol 10: uint256 public constant FLAG_PAY_FEE_ON_DEST = 0x1 << 1; 11: uint256 public constant FLAG_ALLOW_FALLBACK = 0x1 << 2; 15: uint256 public constant FLAG_EXEC_START_VALUE = 0x1 << 16; 16: uint256 public constant FLAG_EXEC_FALLBACK = 0x1 << 16;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/lib/AnycallFlags.sol#L10-L11 https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/lib/AnycallFlags.sol#L15-L16
By changing the pattern of assigning value to the storage structure, gas savings can be achieved.In addition, this use will provide significant savings in distribution costs.
10 instances in 6 files
No tests have been provided for the functions used in these instances from protocol's side (using which gas savings could be calculated).
Here is a POC experimented in remix to demonstrate the estimated gas savings.
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; contract StructGasTest { struct SomeStruct { uint256 num; bool state; address token; string name; bytes32 value; } SomeStruct public someStruct; function setStruct( uint256 num, bool state, address token, string calldata name, bytes32 value) public { // someStruct = SomeStruct({num : num, state : state, token : token, name: name, value: value}); // @audit : costs 38994 gas someStruct.num = num; // @audit : costs 38594 gas someStruct.state = state; someStruct.token = token; someStruct.name = name; someStruct.value = value; } } // paramters used // num => 123 // state => true // token => 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 // name = "hello" // value = 0x0000000000000000000000000000000011111111111111111111111111111111
File: src/rewards/rewards/FlywheelGaugeRewards.sol 189: gaugeQueuedRewards[gauge] = QueuedRewards({ 190: priorCycleRewards: queuedRewards.priorCycleRewards + completedRewards, 191: cycleRewards: uint112(nextRewards), 192: storedCycle: currentCycle 193: });
recommended code :
189: QueuedRewards storage _gaugeQueuedRewards1 = gaugeQueuedRewards[gauge]; 190: _gaugeQueuedRewards1.priorCycleRewards = queuedRewards.priorCycleRewards + completedRewards; 191: _gaugeQueuedRewards1.cycleRewards = uint112(nextRewards); 192: _gaugeQueuedRewards1.storedCycle = currentCycle;
File: src/rewards/rewards/FlywheelGaugeRewards.sol 227: gaugeQueuedRewards[ERC20(msg.sender)] = QueuedRewards({ 228: priorCycleRewards: 0, 229: cycleRewards: cycleRewardsNext, 230: storedCycle: queuedRewards.storedCycle 231: });
recommended code :
227: QueuedRewards storage _gaugeQueuedRewards2 = gaugeQueuedRewards[ERC20(msg.sender)]; 228: _gaugeQueuedRewards2.priorCycleRewards = 0; 229: _gaugeQueuedRewards2.cycleRewards = cycleRewardsNext; 230: _gaugeQueuedRewards2.storedCycle = queuedRewards.storedCycle;
File: src/ulysses-omnichain/RootPort.sol 473: getGasPoolInfo[chainId] = GasPoolInfo({ 474: zeroForOneOnInflow: zeroForOneOnInflow, 475: priceImpactPercentage: _priceImpactPercentage, 476: gasTokenGlobalAddress: newGlobalToken, 477: poolAddress: newGasPoolAddress 478: });
recommended code :
473: GasPoolInfo storage _gasPoolInfo = getGasPoolInfo[chainId]; 474: _gasPoolInfo.zeroForOneOnInflow = zeroForOneOnInflow; 475: _gasPoolInfo.priceImpactPercentage = _priceImpactPercentage; 476: _gasPoolInfo.gasTokenGlobalAddress = newGlobalToken; 477: _gasPoolInfo.poolAddress = newGasPoolAddress;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootPort.sol#L473-L478
File: src/uni-v3-staker/UniswapV3Staker.sol 229: deposits[tokenId] = Deposit({owner: from, tickLower: tickLower, tickUpper: tickUpper, stakedTimestamp: 0});
recommended code :
229: Deposit storage _deposit = deposits[tokenId]; 230: _deposit.owner = from; 231: _deposit.tickLower = tickLower; 232: _deposit.tickUpper = tickUpper; 233: _deposit.stakedTimestamp = stakedTimestamp;
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L229
File: src/uni-v3-staker/UniswapV3Staker.sol 511: if (liquidity >= type(uint96).max) { 512: _stakes[tokenId][incentiveId] = Stake({ 513: secondsPerLiquidityInsideInitialX128: secondsPerLiquidityInsideX128, 514: liquidityNoOverflow: type(uint96).max, 515: liquidityIfOverflow: liquidity 516: }); 517: } else { 518: Stake storage stake = _stakes[tokenId][incentiveId]; 519: stake.secondsPerLiquidityInsideInitialX128 = secondsPerLiquidityInsideX128; 520: stake.liquidityNoOverflow = uint96(liquidity); 521: }
recommended code :
511: Stake storage stake = _stakes[tokenId][incentiveId]; 512: 513: if (liquidity >= type(uint96).max) { 514: stake.secondsPerLiquidityInsideInitialX128 = secondsPerLiquidityInsideX128; 515: stake.liquidityNoOverflow = type(uint96).max; 516: stake.liquidityIfOverflow = liquidity; 517: } else { 518: stake.secondsPerLiquidityInsideInitialX128 = secondsPerLiquidityInsideX128; 519: stake.liquidityNoOverflow = uint96(liquidity); 520: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L507-L511
File: src/ulysses-omnichain/RootBridgeAgent.sol 531: getSettlement[_getAndIncrementSettlementNonce()] = Settlement({ 532: owner: _owner, 533: recipient: _recipient, 534: hTokens: _hTokens, 535: tokens: _tokens, 536: amounts: _amounts, 537: deposits: _deposits, 538: callData: _callData, 539: toChain: _toChain, 540: status: SettlementStatus.Success, 541: gasToBridgeOut: userFeeInfo.gasToBridgeOut 542: });
recommended code :
531: Settlement storage _settlement = getSettlement[_getAndIncrementSettlementNonce()]; 532: _settlement.owner = _owner; 533: _settlement.recipient = _recipient; 534: _settlement.hTokens = _hTokens; 535: _settlement.tokens = _tokens; 536: _settlement.amounts = _amounts; 537: _settlement.deposits = _deposits; 538: _settlement.callData = _callData; 539: _settlement.toChain = _toChain; 540: _settlement.status = SettlementStatus.Success; 541: _settlement.gasToBridgeOut = userFeeInfo.gasToBridgeOut;
File: src/ulysses-omnichain/BranchBridgeAgent.sol 836: getDeposit[_getAndIncrementDepositNonce()] = Deposit({ 837: owner: _user, 838: hTokens: new address[](0), 839: tokens: new address[](0), 840: amounts: new uint256[](0), 841: deposits: new uint256[](0), 842: status: DepositStatus.Success, 843: depositedGas: _gasToBridgeOut 844: });
recommended code :
837: Deposit storage _deposit1 = getDeposit[_getAndIncrementDepositNonce()]; 838: _deposit1.owner =_user; 839: _deposit1.hTokens = new address[](0); 840: _deposit1.tokens = new address[](0); 841: _deposit1.amounts = new uint256[](0); 842: _deposit1.deposits = new uint256[](0); 843: _deposit1.status = DepositStatus.Success; 844: _deposit1.depositedGas = _gasToBridgeOut;
File: src/ulysses-omnichain/BranchBridgeAgent.sol 882: getDeposit[_getAndIncrementDepositNonce()] = Deposit({ 883: owner: _user, 884: hTokens: hTokens, 885: tokens: tokens, 886: amounts: amounts, 887: deposits: deposits, 888: status: DepositStatus.Success, 889: depositedGas: _gasToBridgeOut 890: });
recommended code :
882: Deposit storage _deposit2 = getDeposit[_getAndIncrementDepositNonce()]; 883: _deposit2.owner = _user; 884: _deposit2.hTokens = hTokens; 885: _deposit2.tokens = tokens; 886: _deposit2.amounts = amounts; 887: _deposit2.deposits = deposits; 888: _deposit2.status = DepositStatus.Success; 889: _deposit2.depositedGas = _gasToBridgeOut;
File: src/ulysses-omnichain/BranchBridgeAgent.sol 917: getDeposit[_getAndIncrementDepositNonce()] = Deposit({ 918: owner: _user, 919: hTokens: _hTokens, 920: tokens: _tokens, 921: amounts: _amounts, 922: deposits: _deposits, 923: status: DepositStatus.Success, 924: depositedGas: _gasToBridgeOut 925: });
recommended code :
917: Deposit storage _deposit3 = getDeposit[_getAndIncrementDepositNonce()]; 918: _deposit3.owner = _user; 919: _deposit3.hTokens = _hTokens; 920: _deposit3.tokens = _tokens; 921: _deposit3.amounts = _amounts; 922: _deposit3.deposits = _deposits; 923: _deposit3.status = DepositStatus.Success; 924: _deposit3.depositedGas = _gasToBridgeOut;
File: src/erc-20/ERC20Boost.sol 131: getUserGaugeBoost[user][msg.sender] = 132: GaugeState({userGaugeBoost: userGaugeBoost, totalGaugeBoost: totalSupply.toUint128()});
recommended code :
131: GaugeState storage _gaugeState = getUserGaugeBoost[user][msg.sender]; 132: _gaugeState.userGaugeBoost = userGaugeBoost; 133: _gaugeState.totalGaugeBoost = totalSupply.toUint128();
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Boost.sol#L131-L132
Caching can save gas only when there are multiple accesses of a particular key of a mapping or a particular memeber of a struct inside a function.There is no point in caching such value when it is being used only once inside a function as it would increase gas costs due to involved memory operations.
7 instances in 4 files
addGauge
function as it is an internal function used by addGauge
function | min | avg | med | max | #calls | |
---|---|---|---|---|---|---|
Before | addGauge | 584 | 58103 | 51694 | 73594 | 77 |
After | addGauge | 584 | 55877 | 49351 | 71251 | 77 |
variables newAdd
and previouslyDeprecated
cached in lines 408
and 409
respectively are being accessed only once in line 411
File: src/erc-20/ERC20Gauges.sol 408: bool newAdd = _gauges.add(gauge); 409: bool previouslyDeprecated = _deprecatedGauges.remove(gauge); 410: // add and fail loud if zero address or already present and not deprecated 411: if (gauge == address(0) || !(newAdd || previouslyDeprecated)) revert InvalidGaugeError();
recommended code :
File: src/erc-20/ERC20Gauges.sol 408: // add and fail loud if zero address or already present and not deprecated 409: if (gauge == address(0) || !(_gauges.add(gauge) || _deprecatedGauges.remove(gauge))) revert InvalidGaugeError();
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Gauges.sol#L408-L409
incrementGauges
function as it is an internal function used by incrementGauges
function | min | avg | med | max | #calls | |
---|---|---|---|---|---|---|
Before | incrementGauges | 5922 | 143527 | 109941 | 270614 | 7 |
After | incrementGauges | 5922 | 143511 | 109919 | 270592 | 7 |
variable added
cached in line 210
is being accessed only once in line 211
File: src/erc-20/ERC20Gauges.sol 210: bool added = _userGauges[user].add(gauge); // idempotent add 211: if (added && _userGauges[user].length() > maxGauges && !canContractExceedMaxGauges[user]) { 212: revert MaxGaugeError(); 213: }
recommended code :
210: if (_userGauges[user].add(gauge) && _userGauges[user].length() > maxGauges && !canContractExceedMaxGauges[user]) { 211: revert MaxGaugeError(); 212: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Gauges.sol#L210
addGauge
function as it is an internal function used by addGauge
function | min | avg | med | max | #calls | |
---|---|---|---|---|---|---|
Before | addGauge | 1310 | 64241 | 72921 | 72921 | 50 |
After | addGauge | 2514 | 61094 | 70578 | 70578 | 50 |
variables newAdd
and previouslyDeprecated
cached in lines 265
and 266
respectively are being accessed only once in line 268
File: src/erc-20/ERC20Boost.sol 265: bool newAdd = _gauges.add(gauge); 266: bool previouslyDeprecated = _deprecatedGauges.remove(gauge); 267: // add and fail loud if zero address or already present and not deprecated 268: if (gauge == address(0) || !(newAdd || previouslyDeprecated)) revert InvalidGauge();
recommended code :
265: // add and fail loud if zero address or already present and not deprecated 266: if (gauge == address(0) || !(_gauges.add(gauge) || _deprecatedGauges.remove(gauge))) revert InvalidGauge();
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Boost.sol#L264-L265
variable zeroForOne
cached in line 337
is being accessed only once in line 339
File: src/talos/base/TalosBaseStrategy.sol 337: bool zeroForOne = data.zeroForOne; 338: 339: if (zeroForOne) address(token0).safeTransfer(msg.sender, uint256(amount0));
recommended code :
337: if (data.zeroForOne) address(token0).safeTransfer(msg.sender, uint256(amount0));
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L337
incrementDelegation
function as it is an internal function used by incrementDelegation
function | min | avg | med | max | #calls | |
---|---|---|---|---|---|---|
Before | incrementDelegation | 1059 | 127382 | 155661 | 161661 | 30 |
After | incrementDelegation | 1059 | 127373 | 155651 | 161651 | 30 |
variable newDelegate
cached in line 190
is being accessed only once in line 191
File: src/erc-20/ERC20MultiVotes.sol 190: bool newDelegate = _delegates[delegator].add(delegatee); // idempotent add 191: if (newDelegate && delegateCount(delegator) > maxDelegates && !canContractExceedMaxDelegates[delegator]) { 192: // if is a new delegate, exceeds max and is not approved to exceed, revert 193: revert DelegationError(); 194: }
recommended code :
190: if (_delegates[delegator].add(delegatee) && delegateCount(delegator) > maxDelegates && !canContractExceedMaxDelegates[delegator]) { 191: // if is a new delegate, exceeds max and is not approved to exceed, revert 192: revert DelegationError(); 193: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20MultiVotes.sol#L193
Caching the result of a function call can can save gas only when that result is being accessed mutiple times inside a function .There is no point in caching such result when it is being used only once inside a function as it would increase gas costs due to involved memory operations.
4 instances in 2 files
addGauge
function as it is an internal function used by addGauge
function | min | avg | med | max | #calls | |
---|---|---|---|---|---|---|
Before | addGauge | 584 | 58103 | 51694 | 73594 | 77 |
After | addGauge | 584 | 57855 | 51436 | 73336 | 77 |
variable currentCycle
cached in line 410
is being accessed only once in line 415
File: src/erc-20/ERC20Gauges.sol 410: uint32 currentCycle = _getGaugeCycleEnd(); 411: 412: // Check if some previous weight exists and re-add to the total. Gauge and user weights are preserved. 413: weight = _getGaugeWeight[gauge].currentWeight; 414: if (weight > 0) { 415: _writeGaugeWeight(_totalWeight, _add112, weight, currentCycle); 416: }
recommended code :
410: // Check if some previous weight exists and re-add to the total. Gauge and user weights are preserved. 411: weight = _getGaugeWeight[gauge].currentWeight; 412: if (weight > 0) { 413: _writeGaugeWeight(_totalWeight, _add112, weight, _getGaugeCycleEnd()); 414: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Gauges.sol#L413
incrementDelegation
function as it is an internal function used by incrementDelegation
function | min | avg | med | max | #calls | |
---|---|---|---|---|---|---|
Before | incrementDelegation | 1059 | 127382 | 155661 | 161661 | 30 |
After | incrementDelegation | 572 | 127287 | 155648 | 161648 | 30 |
variable free
cached in line 188
is being accessed only once in line 189
File: src/erc-20/ERC20MultiVotes.sol 187: // Require freeVotes exceed the delegation size 188: uint256 free = freeVotes(delegator); 189: if (delegatee == address(0) || free < amount || amount == 0) revert DelegationError();
recommended code :
187: // Require freeVotes exceed the delegation size 188: if (delegatee == address(0) || freeVotes(delegator) < amount || amount == 0) revert DelegationError();
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20MultiVotes.sol#L190
function | min | avg | med | max | #calls | |
---|---|---|---|---|---|---|
Before | calculateGaugeAllocation | 2057 | 2058 | 2057 | 2061 | 6 |
After | calculateGaugeAllocation | 2049 | 2050 | 2049 | 2053 | 6 |
variables total
and weigth
cache in line 178
and 179
respectively are being accessed only once in line 180
File: src/erc-20/ERC20Gauges.sol 178: uint112 total = _getStoredWeight(_totalWeight, currentCycle); 179: uint112 weight = _getStoredWeight(_getGaugeWeight[gauge], currentCycle); 180: return (quantity * weight) / total;
recommended code :
178: return (quantity * _getStoredWeight(_getGaugeWeight[gauge], currentCycle)) / _getStoredWeight(_totalWeight, currentCycle);
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Gauges.sol#L178-L180
The function parameters in the following instances are complex types (arrays of structs) and thus will result in more complex offset calculations to retrieve specific data from calldata. We can avoid peforming some of these offset calculations by instantiating calldata/memory pointers.
32 instances in 7 files
No tests have been provided for the functions used in thsese instances from protocol's side (using which gas savings could be calculated).
Here is a POC experimented in remix to demonstrate the estimated gas savings.
Note : Gas savings will actually be very high since calldata offset calculation is occuring within loops in all of the instances covered in this section
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; contract CacheComplexTypesGasTest { struct Num { uint256 a; uint256 b; } // @audit : call to `test` costs 3397 gas in CASE 1 while it costs 3217 in CASE 2 function test(Num[] memory num) public pure returns(uint256) { // CASE 1 // uint256 someNum; // uint256 length = num.length; // for (uint256 i; i < length;) { // someNum = num[i].a + num[i].b; // unchecked { // ++i; // } // } // return someNum; // CASE 2 uint256 someNum; uint256 length = num.length; for (uint i; i < length;) { Num memory _num = num[i]; someNum = _num.a + _num.b; unchecked { ++i; } } return someNum; } function callTest() public pure { Num[] memory num = new Num[](5); num[0] = Num({a : 123, b : 456}); num[1] = Num({a : 321, b : 654}); num[2] = Num({a : 213, b : 564}); num[3] = Num({a : 146, b : 365}); num[4] = Num({a : 254, b : 777}); test(num); } }
calls[i]
File: src/ulysses-omnichain/VirtualAccount.sol 48: for (uint256 i = 0; i < calls.length; i++) { 49: (bool success, bytes memory data) = calls[i].target.call(calls[i].callData); 50: if (!success) revert CallFailed(); 51: returnData[i] = data; 52: }
recommended code :
48: for (uint256 i = 0; i < calls.length; i++) { 49: Call calldata _call = calls[i]; 50: (bool success, bytes memory data) = _call.target.call(_call.callData); 51: if (!success) revert CallFailed(); 52: returnData[i] = data; 53: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/VirtualAccount.sol#L49
routes[i]
File: src/ulysses-amm/UlyssesRouter.sol 78: for (uint256 i = 0; i < length;) { 79: amount = getUlyssesLP(routes[i].from).swapIn(amount, routes[i].to); 80: 81: unchecked { 82: ++i; 83: } 84: }
recommended code :
78: for (uint256 i = 0; i < length;) { 79: Route calldata _route = routes[i]; 80: amount = getUlyssesLP(_route.from).swapIn(amount, _route.to); 81: 82: unchecked { 83: ++i; 84: } 85: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesRouter.sol#L79
assets[i]
and _weights[i]
File: src/ulysses-amm/UlyssesToken.sol 95: for (uint256 i = 0; i < assets.length; i++) { 96: newTotalWeights += _weights[i]; 97: 98: emit AssetRemoved(assets[i]); 99: emit AssetAdded(assets[i], _weights[i]); 100: }
recommended code :
95: for (uint256 i = 0; i < assets.length; i++) { 96: address _asset = assets[i]; 97: uint256 _weight = _weights[i]; 98: newTotalWeights += _weight; 99: 100: emit AssetRemoved(_asset); 101: emit AssetAdded(_asset, _weight); 102: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesToken.sol#L96-L99
weights[i][j]
File: src/ulysses-amm/factories/UlyssesFactory.sol 110: for (uint256 j = 0; j < length;) { 111: if (j != i && weights[i][j] > 0) pools[poolIds[i]].addNewBandwidth(poolIds[j], weights[i][j]); 112: 113: unchecked { 114: ++j; 115: } 116: }
recommended code : Similar mitigation steps as shown in above instances can be applied to this one too
_deposits[i]
, _amounts[i]
and _underlyingAddresses[i]
File: src/ulysses-omnichain/ArbitrumBranchPort.sol 136: for (uint256 i = 0; i < _localAddresses.length;) { 137: if (_deposits[i] > 0) { 138: _underlyingAddresses[i].safeTransferFrom( 139: _depositor, 140: address(this), 141: _denormalizeDecimals(_deposits[i], ERC20(_underlyingAddresses[i]).decimals()) 142: ); 143: } 144: if (_amounts[i] - _deposits[i] > 0) { 145: IRootPort(rootPortAddress).bridgeToRootFromLocalBranch( 146: _depositor, _localAddresses[i], _amounts[i] - _deposits[i] 147: ); 148: } 149: 150: unchecked { 151: ++i; 152: } 153: }
recommended code :
136: for (uint256 i = 0; i < _localAddresses.length;) { 137: uint256 _deposit = _deposits[i]; 138: uint256 _amount = -_amounts[i]; 139: if (_deposit > 0) { 140: address _underlyingAddress = _underlyingAddresses[i]; 141: _underlyingAddress.safeTransferFrom( 142: _depositor, 143: address(this), 144: _denormalizeDecimals(_deposit, ERC20(_underlyingAddress).decimals()) 145: ); 146: } 147: if (_amount - _deposit > 0) { 148: IRootPort(rootPortAddress).bridgeToRootFromLocalBranch( 149: _depositor, _localAddresses[i], _amount - _deposit 150: ); 151: } 152: 153: unchecked { 154: ++i; 155: } 156: }
_deposits[i]
,_amounts[i]
,_underlyingAddresses[i]
and _localAddresses[i]
File: src/ulysses-omnichain/BranchPort.sol 267: for (uint256 i = 0; i < _localAddresses.length;) { 268: if (_deposits[i] > 0) { 269: _underlyingAddresses[i].safeTransferFrom( 270: _depositor, 271: address(this), 272: _denormalizeDecimals(_deposits[i], ERC20(_underlyingAddresses[i]).decimals()) 273: ); 274: } 275: if (_amounts[i] - _deposits[i] > 0) { 276: _localAddresses[i].safeTransferFrom(_depositor, address(this), _amounts[i] - _deposits[i]); 277: ERC20hTokenBranch(_localAddresses[i]).burn(_amounts[i] - _deposits[i]); 278: } 279: unchecked { 280: i++; 281: } 282: }
recommended code :
267: for (uint256 i = 0; i < _localAddresses.length;) { 268: uint256 _deposit = _deposits[i]; 269: uint256 _amount = -_amounts[i]; 270: if (_deposit > 0) { 271: address _underlyingAddress = _underlyingAddresses[i]; 272: _underlyingAddress.safeTransferFrom( 273: _depositor, 274: address(this), 275: _denormalizeDecimals(_deposit, ERC20(_underlyingAddress).decimals()) 276: ); 277: } 278: if (_amount - _deposit > 0) { 279: address _localAddress = _localAddresses[i]; 280: _localAddress.safeTransferFrom(_depositor, address(this), _amount - _deposit); 281: ERC20hTokenBranch(_localAddress).burn(_amount - _deposit); 282: } 283: unchecked { 284: i++; 285: } 286: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchPort.sol#L268-L277
_weights[i]
File: src/erc-4626/ERC4626MultiToken.sol 50: for (uint256 i = 0; i < length;) { 51: require(ERC20(_assets[i]).decimals() == 18); 52: require(_weights[i] > 0); 53: 54: _totalWeights += _weights[i]; 55: assetId[_assets[i]] = i + 1; 56: 57: emit AssetAdded(_assets[i], _weights[i]); 58: 59: unchecked { 60: i++; 61: } 62: }
recommended code :
50: for (uint256 i = 0; i < length;) { 51: uint256 _asset = _assets[i]; // @audit : _assets[i] is a state variable being read in a loop(this issue is already included in automated finding), this line has been added in this recommendation just so that the code semantics looks similar overall 52: address _weight = _weights[i]; 53: require(ERC20(_asset).decimals() == 18); 54: require(_weight > 0); 55: 56: _totalWeights += _weight; 57: assetId[_asset] = i + 1; 58: 59: emit AssetAdded(_asset, _weight); 60: 61: unchecked { 62: i++; 63: } 64: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/ERC4626MultiToken.sol#L51-L57
Move require statement to the top of the function as it's logic is not influenced by the code above it in any way and in the current code design,users will have to pay more gas if function reverts.Thus by moving require statements to the top,users gas can be saved if they call the function and the function reverts as early as possible
15 instances in 9 files
File: src/ulysses-amm/UlyssesPool.sol 88: require(_owner != address(0)); 89: factory = UlyssesFactory(_factory); 90: _initializeOwner(_owner); 91: require(_id != 0); 92: id = _id;
recommended code :
89: require(_id != 0); 90: factory = UlyssesFactory(_factory); 91: _initializeOwner(_owner); 92: id = _id;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesPool.sol#L91
File: src/ulysses-amm/UlyssesToken.sol 29: _initializeOwner(_owner); 30: require(_id != 0); 31: id = _id;
recommended code :
29: require(_id != 0); 30: _initializeOwner(_owner); 31: id = _id;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesToken.sol#L30
File: src/ulysses-amm/UlyssesToken.sol 61: // No need to check if index is 0, it will underflow and revert if it is 0 62: uint256 assetIndex = assetId[asset] - 1; 63: 64: uint256 newAssetsLength = assets.length - 1; 65: 66: if (newAssetsLength == 0) revert CannotRemoveLastAsset();
recommended code :
61: uint256 newAssetsLength = assets.length - 1; 62: 63: if (newAssetsLength == 0) revert CannotRemoveLastAsset(); 64: 65: // No need to check if index is 0, it will underflow and revert if it is 0 66: uint256 assetIndex = assetId[asset] - 1;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesToken.sol#L66
File: src/uni-v3-staker/UniswapV3Staker.sol 138: if (reward <= 0) revert IncentiveRewardMustBePositive(); 139: 140: uint96 startTime = IncentiveTime.computeEnd(block.timestamp); 141: 142: IUniswapV3Pool pool = gaugePool[msg.sender]; 143: 144: if (address(pool) == address(0)) revert IncentiveCallerMustBeRegisteredGauge();
recommended code :
138: if (reward <= 0) revert IncentiveRewardMustBePositive(); 139: 140: IUniswapV3Pool pool = gaugePool[msg.sender]; 141: 142: if (address(pool) == address(0)) revert IncentiveCallerMustBeRegisteredGauge(); 143: 144: uint96 startTime = IncentiveTime.computeEnd(block.timestamp);
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L144
File: src/erc-4626/ERC4626MultiToken.sol 42: assets = _assets; 43: weights = _weights; 44: 45: uint256 length = _weights.length; 46: uint256 _totalWeights; 47: 48: if (length != _assets.length || length == 0) revert InvalidLength();
recommended code :
42: uint256 length = _weights.length; 43: if (length != _assets.length || length == 0) revert InvalidLength(); 44: 45: assets = _assets; 46: weights = _weights; 47: 48: uint256 _totalWeights;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/ERC4626MultiToken.sol#L48
File: src/erc-4626/ERC4626MultiToken.sol 197: uint256 _totalWeights = totalWeights; 198: uint256 length = assetsAmounts.length; 199: 200: if (length != assets.length) revert InvalidLength();
recommended code :
197: uint256 length = assetsAmounts.length; 198: 199: if (length != assets.length) revert InvalidLength(); 200: uint256 _totalWeights = totalWeights;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/ERC4626MultiToken.sol#L198
File: src/erc-4626/ERC4626MultiToken.sol 247: uint256 _totalWeights = totalWeights; 248: uint256 length = assetsAmounts.length; 249: 250: if (length != assets.length) revert InvalidLength();
recommended code :
247: uint256 length = assetsAmounts.length; 248: 249: if (length != assets.length) revert InvalidLength(); 250: uint256 _totalWeights = totalWeights;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/ERC4626MultiToken.sol#L248
File: src/erc-4626/UlyssesERC4626.sol 25: asset = _asset; 26: 27: if (ERC20(_asset).decimals() != 18) revert InvalidAssetDecimals();
recommended code :
File: src/erc-4626/UlyssesERC4626.sol 25: if (ERC20(_asset).decimals() != 18) revert InvalidAssetDecimals(); 26: 27: asset = _asset;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/UlyssesERC4626.sol#L27
File: src/erc-4626/UlyssesERC4626.sol 35: // Need to transfer before minting or ERC777s could reenter. 36: asset.safeTransferFrom(msg.sender, address(this), assets); 37: 38: shares = beforeDeposit(assets); 39: 40: require(shares != 0, "ZERO_SHARES");
recommended code :
35: shares = beforeDeposit(assets); 36: 37: require(shares != 0, "ZERO_SHARES"); 38: 39: // Need to transfer before minting or ERC777s could reenter. 40: asset.safeTransferFrom(msg.sender, address(this), assets);
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/UlyssesERC4626.sol#L40
File: src/erc-4626/UlyssesERC4626.sol 65: if (msg.sender != owner) { 66: uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. 67: 68: if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; 69: } 70: 71: _burn(owner, shares); 72: 73: assets = afterRedeem(shares); 74: 75: require(assets != 0, "ZERO_ASSETS");
recommended code :
65: assets = afterRedeem(shares); 66: 67: require(assets != 0, "ZERO_ASSETS"); 68: 69: if (msg.sender != owner) { 70: uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. 71: 72: if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; 73: } 74: 75: _burn(owner, shares);
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/UlyssesERC4626.sol#L75
File: src/erc-4626/ERC4626.sol 81: if (msg.sender != owner) { 82: uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. 83: 84: if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; 85: } 86: 87: // Check for rounding error since we round down in previewRedeem. 88: require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");
recommended code :
81: // Check for rounding error since we round down in previewRedeem. 82: require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); 83: 84: if (msg.sender != owner) { 85: uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. 86: 87: if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; 88: } 89:
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-4626/ERC4626.sol#L88
File: src/talos/base/TalosBaseStrategy.sol 246: if (msg.sender != _owner) { 247: uint256 allowed = allowance[_owner][msg.sender]; // Saves gas for limited approvals. 248: 249: if (allowed != type(uint256).max) allowance[_owner][msg.sender] = allowed - shares; 250: } 251: 252: if (shares == 0) revert RedeemingZeroShares(); 253: if (receiver == address(0)) revert ReceiverIsZeroAddress();
recommended code :
246: if (shares == 0) revert RedeemingZeroShares(); 247: if (receiver == address(0)) revert ReceiverIsZeroAddress(); 248: 249: if (msg.sender != _owner) { 250: uint256 allowed = allowance[_owner][msg.sender]; // Saves gas for limited approvals. 251: 252: if (allowed != type(uint256).max) allowance[_owner][msg.sender] = allowed - shares; 253: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L252-253
File: src/ulysses-amm/UlyssesRouter.sol 74: address(getUlyssesLP(routes[0].from).asset()).safeTransferFrom(msg.sender, address(this), amount); 75: 76: uint256 length = routes.length; 77: 78: for (uint256 i = 0; i < length;) { 79: Route calldata _route = routes[i]; 80: amount = getUlyssesLP(_route.from).swapIn(amount, _route.to); 81: 82: unchecked { 83: ++i; 84: } 85: } 86: 87: if (amount < minOutput) revert OutputTooLow(); 88: 89: unchecked { 90: --length; 91: }
recommended code :
74: if (amount < minOutput) revert OutputTooLow(); 75: 76: address(getUlyssesLP(routes[0].from).asset()).safeTransferFrom(msg.sender, address(this), amount); 77: 78: uint256 length = routes.length; 79: 80: for (uint256 i = 0; i < length;) { 81: Route calldata _route = routes[i]; 82: amount = getUlyssesLP(_route.from).swapIn(amount, _route.to); 83: 84: unchecked { 85: ++i; 86: } 87: } 88: 89: unchecked { 90: --length; 91: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesRouter.sol#L86
File: src/erc-20/ERC20Gauges.sol 292: if (!_gauges.contains(gauge)) revert InvalidGaugeError(); 293: 294: uint112 oldWeight = getUserGaugeWeight[user][gauge]; 295: 296: IBaseV2Gauge(gauge).accrueBribes(user); 297: 298: getUserGaugeWeight[user][gauge] = oldWeight - weight; 299: if (oldWeight == weight) { 300: // If removing all weight, remove gauge from user list. 301: require(_userGauges[user].remove(gauge)); 302: }
recommended code :
292: if (!_gauges.contains(gauge)) revert InvalidGaugeError(); 293: 294: uint112 oldWeight = getUserGaugeWeight[user][gauge]; 295: 296: if (oldWeight == weight) { 297: // If removing all weight, remove gauge from user list. 298: require(_userGauges[user].remove(gauge)); 299: } 300: 301: IBaseV2Gauge(gauge).accrueBribes(user); 302: 303: getUserGaugeWeight[user][gauge] = oldWeight - weight;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Gauges.sol#L300-L302
Caching the value of a particular key of a mapping or the value of a particular index of an array in a local storage variable when the value is accessed(read and written) multiple times saves ~40 gas per access due to not having to perform the same offset calculation every time.Help the Optimizer by saving a storage variable’s reference instead of repeatedly fetching it
To help the optimizer,declare a storage type variable and use it instead of repeatedly fetching the reference in a map or an array.
As an example, instead of repeatedly calling someMap[someKey]
and SomeArray[someIndex]
, save their references like SomeType storage someVariable = someMap[someIndex]
and SomeType storage someVariable = SomeArray[someIndex]
use them.
146 instances in 18 files
Note : Maximum gas savings can be achieved in instances where the value being accessed multiple times are of complex types(such as array of structs that contains arrays) and especially when they are being accessed inside lengthy for loops
_isAsset[asset]
and _isRewardsContract[rewardsContract]
should be cached in local storageFile: src/rewards/depots/MultiRewardsDepot.sol 48: if (_isAsset[asset] || _isRewardsContract[rewardsContract]) revert ErrorAddingAsset(); 49: _isAsset[asset] = true; 50: _isRewardsContract[rewardsContract] = true;
_isRewardsContract[rewardsContract]
and _assets[rewardsContract]
should be cached in local storageFile: src/rewards/depots/MultiRewardsDepot.sol 58: if (!_isRewardsContract[rewardsContract]) revert ErrorRemovingAsset(); 59: 60: emit AssetRemoved(rewardsContract, _assets[rewardsContract]); 61: 62: delete _isAsset[_assets[rewardsContract]]; 63: delete _isRewardsContract[rewardsContract]; 64: delete _assets[rewardsContract];
partnerIds[partnerManager]
and partners[partnerIds[partnerManager]]
should be cached in local storageFile: src/maia/factories/PartnerManagerFactory.sol 81: if (partners[partnerIds[partnerManager]] != partnerManager) revert InvalidPartnerManager(); 82: delete partners[partnerIds[partnerManager]]; 83: delete partnerIds[partnerManager];
vaultIds[vault]
and vaults[vaultIds[vault]]`` should be cached in local storageFile: src/maia/factories/PartnerManagerFactory.sol 90: if (vaults[vaultIds[vault]] != vault) revert InvalidVault(); 91: delete vaults[vaultIds[vault]]; 92: delete vaultIds[vault];
pools[id]
should be cached in local storageFile: src/ulysses-amm/UlyssesRouter.sol 32: ulysses = pools[id]; 33: if (address(ulysses) == address(0)) { 34: ulysses = ulyssesFactory.pools(id); 35: 36: if (address(ulysses) == address(0)) revert UnrecognizedUlyssesLP(); 37: 38: pools[id] = ulysses;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesRouter.sol#L32-L38
flywheelTokens[bribeToken]
should be cached in local storageFile: src/gauges/factories/BribesFactory.sol 73: if (address(flywheelTokens[bribeToken]) == address(0)) createBribeFlywheel(bribeToken); 74: 75: flywheelTokens[bribeToken].addStrategyForRewards(ERC20(gauge));
https://github.com/code-423n4/2023-05-maia/blob/main/src/gauges/factories/BribesFactory.sol#L73-L75
flywheelTokens[bribeToken]
should be cached in local storageFile: src/gauges/factories/BribesFactory.sol 80: if (address(flywheelTokens[bribeToken]) != address(0)) revert BribeFlywheelAlreadyExists(); 81: 82: FlywheelCore flywheel = new FlywheelCore( 83: bribeToken, 84: FlywheelBribeRewards(address(0)), 85: flywheelGaugeWeightBooster, 86: address(this) 87: ); 88: 89: flywheelTokens[bribeToken] = flywheel;
https://github.com/code-423n4/2023-05-maia/blob/main/src/gauges/factories/BribesFactory.sol#L80-L89
assetId[asset]
should be cached in local storageFile: src/ulysses-amm/UlyssesToken.sol 45: if (assetId[asset] != 0) revert AssetAlreadyAdded(); 46: require(ERC20(asset).decimals() == 18); 47: require(_weight > 0); 48: 49: assetId[asset] = assets.length + 1;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesToken.sol#L45-L49
assetId[asset]
should be cached in local storageFile: src/ulysses-amm/UlyssesToken.sol 62: uint256 assetIndex = assetId[asset] - 1; 63: 64: uint256 newAssetsLength = assets.length - 1; 65: 66: if (newAssetsLength == 0) revert CannotRemoveLastAsset(); 67: 68: totalWeights -= weights[assetIndex]; 69: 70: address lastAsset = assets[newAssetsLength]; 71: 72: assetId[lastAsset] = assetIndex; 73: assets[assetIndex] = lastAsset; 74: weights[assetIndex] = weights[newAssetsLength]; 75: 76: assets.pop(); 77: weights.pop(); 78: assetId[asset] = 0;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesToken.sol#L62-L78
activeGaugeFactories[gaugeFactory]
should be cached in local storageFile: src/gauges/factories/BaseV2GaugeManager.sol 111: if (activeGaugeFactories[gaugeFactory]) revert GaugeFactoryAlreadyExists(); 112: 113: gaugeFactoryIds[gaugeFactory] = gaugeFactories.length; 114: gaugeFactories.push(gaugeFactory); 115: activeGaugeFactories[gaugeFactory] = true;
gaugeFactoryIds[gaugeFactory]
, gaugeFactories[gaugeFactoryIds[gaugeFactory]]
and activeGaugeFactories[gaugeFactory]
should be cached in local storageFile: src/gauges/factories/BaseV2GaugeManager.sol 122: if (!activeGaugeFactories[gaugeFactory] || gaugeFactories[gaugeFactoryIds[gaugeFactory]] != gaugeFactory) { 123: revert NotActiveGaugeFactory(); 124: } 125: delete gaugeFactories[gaugeFactoryIds[gaugeFactory]]; 126: delete gaugeFactoryIds[gaugeFactory]; 127: delete activeGaugeFactories[gaugeFactory];
isBridgeAgentFactory[_bridgeAgentFactory]
should be cached in local storageFile: src/ulysses-omnichain/BranchPort.sol 100: require(coreBranchRouterAddress == address(0), "Contract already initialized"); 101: require(!isBridgeAgentFactory[_bridgeAgentFactory], "Contract already initialized"); 102: 103: require(_coreBranchRouter != address(0), "CoreBranchRouter is zero address"); 104: require(_bridgeAgentFactory != address(0), "BridgeAgentFactory is zero address"); 105: 106: coreBranchRouterAddress = _coreBranchRouter; 107: isBridgeAgentFactory[_bridgeAgentFactory] = true;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchPort.sol#L101-L107
lastManaged[msg.sender][_token]
should be cached in local storageFile: src/ulysses-omnichain/BranchPort.sol 194: if (block.timestamp - lastManaged[msg.sender][_token] >= 1 days) { 195: strategyDailyLimitRemaining[msg.sender][_token] = strategyDailyLimitAmount[msg.sender][_token]; 196: } 197: strategyDailyLimitRemaining[msg.sender][_token] -= _amount; 198: lastManaged[msg.sender][_token] = block.timestamp;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchPort.sol#L194-L198
isBridgeAgentFactory[_newBridgeAgentFactory]
should be cached in local storageFile: src/ulysses-omnichain/BranchPort.sol 322: isBridgeAgentFactory[_newBridgeAgentFactory] = !isBridgeAgentFactory[_newBridgeAgentFactory];
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchPort.sol#L318
isBridgeAgent[_bridgeAgent]
should be cached in local storageFile: src/ulysses-omnichain/BranchPort.sol 329: isBridgeAgent[_bridgeAgent] = !isBridgeAgent[_bridgeAgent];
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchPort.sol#L325
isStrategyToken[_token]
should be cached in local storageFile: src/ulysses-omnichain/BranchPort.sol 347: isStrategyToken[_token] = !isStrategyToken[_token];
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchPort.sol#L343
isPortStrategy[_portStrategy][_token]
should be cached in local storageFile: src/ulysses-omnichain/BranchPort.sol 368: isPortStrategy[_portStrategy][_token] = !isPortStrategy[_portStrategy][_token];
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/BranchPort.sol#L364
isRouterApproved[_userAccount][_router]
should be cached in local storage
File: src/ulysses-omnichain/RootPort.sol 358: isRouterApproved[_userAccount][_router] = !isRouterApproved[_userAccount][_router];
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootPort.sol#L358
isBridgeAgent[_bridgeAgent]
should be cached in local storageFile: src/ulysses-omnichain/RootPort.sol 367: if (isBridgeAgent[_bridgeAgent]) revert AlreadyAddedBridgeAgent(); 368: 369: bridgeAgents.push(_bridgeAgent); 370: bridgeAgentsLenght++; 371: getBridgeAgentManager[_bridgeAgent] = _manager; 372: isBridgeAgent[_bridgeAgent] = !isBridgeAgent[_bridgeAgent];
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootPort.sol#L367-L372
isBridgeAgent[_bridgeAgent]
should be cached in local storageFile: src/ulysses-omnichain/RootPort.sol 400: isBridgeAgent[_bridgeAgent] = !isBridgeAgent[_bridgeAgent];
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootPort.sol#L400
isBridgeAgentFactory[_bridgeAgentFactory]
should be cached in local storage
File: src/ulysses-omnichain/RootPort.sol 414: isBridgeAgentFactory[_bridgeAgentFactory] = !isBridgeAgentFactory[_bridgeAgentFactory];
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootPort.sol#L414
isGlobalAddress[_ecoTokenGlobalAddress]
should be cached in local storageFile: src/ulysses-omnichain/RootPort.sol 491: if (isGlobalAddress[_ecoTokenGlobalAddress]) revert AlreadyAddedEcosystemToken(); 492: if ( 493: getUnderlyingTokenFromLocal[_ecoTokenGlobalAddress][localChainId] != address(0) 494: || getLocalTokenFromUnder[_ecoTokenGlobalAddress][localChainId] != address(0) 495: ) revert AlreadyAddedEcosystemToken(); 496: 497: isGlobalAddress[_ecoTokenGlobalAddress] = true;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootPort.sol#L492-L498
rewards[msg.sender]
should be cached in local storageFile: src/uni-v3-staker/UniswapV3Staker.sol 268: reward = rewards[msg.sender]; 269: if (amountRequested != 0 && amountRequested < reward) { 270: reward = amountRequested; 271: rewards[msg.sender] -= reward; 272: } else { 273: rewards[msg.sender] = 0; 274: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L263-L268
rewards[msg.sender]
should be cached in local storageFile: src/uni-v3-staker/UniswapV3Staker.sol 283: reward = rewards[msg.sender]; 284: rewards[msg.sender] = 0;
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L278-L279
_userAttachements[owner][key.pool]
should be cached in local storageFile: src/uni-v3-staker/UniswapV3Staker.sol 406: // If tokenId is attached to gauge 407: if (hermesGaugeBoost.isUserGauge(owner, address(gauge)) && _userAttachements[owner][key.pool] == tokenId) { 408: // get boost amount and total supply 409: (boostAmount, boostTotalSupply) = hermesGaugeBoost.getUserGaugeBoost(owner, address(gauge)); 410: gauge.detachUser(owner); 411: _userAttachements[owner][key.pool] = 0; 412: }
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L402-L406
incentives[incentiveId]
should be cached in local storageFile: src/uni-v3-staker/UniswapV3Staker.sol 488: if (incentives[incentiveId].totalRewardUnclaimed == 0) revert NonExistentIncentiveError(); 489: 490: if (uint24(tickUpper - tickLower) < poolsMinimumWidth[pool]) revert RangeTooSmallError(); 491: if (liquidity == 0) revert NoLiquidityError(); 492: 493: stakedIncentiveKey[tokenId] = key; 494: 495: // If user not attached to gauge, attach 496: address tokenOwner = deposits[tokenId].owner; 497: if (tokenOwner == address(0)) revert TokenNotDeposited(); 498: 499: UniswapV3Gauge gauge = gauges[pool]; // saves another SLOAD if no tokenId is attached 500: 501: if (!hermesGaugeBoost.isUserGauge(tokenOwner, address(gauge))) { 502: _userAttachements[tokenOwner][pool] = tokenId; 503: gauge.attachUser(tokenOwner); 504: } 505: 506: deposits[tokenId].stakedTimestamp = uint40(block.timestamp); 507: incentives[incentiveId].numberOfStakes++;
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L483-L502
gauges[uniswapV3Pool]
should be cached in local storageFile: src/uni-v3-staker/UniswapV3Staker.sol 535: if (address(gauges[uniswapV3Pool]) != uniswapV3Gauge) { 536: emit GaugeUpdated(uniswapV3Pool, uniswapV3Gauge); 537: 538: gauges[uniswapV3Pool] = UniswapV3Gauge(uniswapV3Gauge);
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L531-L534
bribeDepots[uniswapV3Pool]
should be cached in local storageFile: src/uni-v3-staker/UniswapV3Staker.sol 549: if (newDepot != bribeDepots[uniswapV3Pool]) { 550: bribeDepots[uniswapV3Pool] = newDepot;
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L545-L546
poolsMinimumWidth[uniswapV3Pool]
should be cached in local storageFile: src/uni-v3-staker/UniswapV3Staker.sol 559: if (minimumWidth != poolsMinimumWidth[uniswapV3Pool]) { 560: poolsMinimumWidth[uniswapV3Pool] = minimumWidth;
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L555-L556
destinationIds[address(destination)]
should be cached in local storageHere, destinationIds[address(destination)]
has been accessed in lines 165
and 207
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-amm/UlyssesPool.sol#L165-L207
getSettlement[_settlementNonce].gasToBridgeOut
should be cached in local storageFile: src/ulysses-omnichain/RootBridgeAgent.sol 837: //Check if sufficient balance 838: if (minExecCost > getSettlement[_settlementNonce].gasToBridgeOut) { 839: _forceRevert(); 840: return; 841: } 842: 843: //Update user deposit reverts if not enough gas 844: getSettlement[_settlementNonce].gasToBridgeOut -= minExecCost.toUint128();
executionHistory[fromChainId][nonce]
should be cached in local storageHere, executionHistory[fromChainId][nonce]
has been accessed in lines 919
,936
,944
,961
,969
,985
,993
,1009
,1017
,1045
,1053
,1080
,1088
,1115
,1123
,1138
and 1148
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootBridgeAgent.sol#L919
getDeposit[_depositNonce]
should be cached in local storageHere, getDeposit[_depositNonce]
has been accessed in lines 327
,332
,338
,339
,340
,342
,353
,354
,355
,357
,365
,373
,408
and 411
getDeposit[_depositNonce].depositedGas
should be cached in local storageFile: src/ulysses-omnichain/BranchBridgeAgent.sol 1066: //Check if sufficient balance 1067: if (minExecCost > getDeposit[_depositNonce].depositedGas) { 1068: _forceRevert(); 1069: return; 1070: } 1071: 1072: //Update user deposit reverts if not enough gas => user must boost deposit with gas 1073: getDeposit[_depositNonce].depositedGas -= minExecCost.toUint128();
executionHistory[nonce]
should be cached in local storageHere, executionHistory[nonce]
has been accessed in lines 1148
,1162
,1170
,1186
,1194
and 1210
added[bribeFlywheel]
should be cached in local storageFile: src/gauges/BaseV2Gauge.sol 129: /// @dev Can't add existing flywheel (active or not) 130: if (added[bribeFlywheel]) revert FlywheelAlreadyAdded(); 131: 132: address flyWheelRewards = address(bribeFlywheel.flywheelRewards()); 133: FlywheelBribeRewards(flyWheelRewards).setRewardsDepot(multiRewardsDepot); 134: 135: multiRewardsDepot.addAsset(flyWheelRewards, bribeFlywheel.rewardToken()); 136: bribeFlywheels.push(bribeFlywheel); 137: isActive[bribeFlywheel] = true; 138: added[bribeFlywheel] = true;
https://github.com/code-423n4/2023-05-maia/blob/main/src/gauges/BaseV2Gauge.sol#L130-L138
isActive[bribeFlywheel]
should be cached in local storageFile: src/gauges/BaseV2Gauge.sol 145: /// @dev Can only remove active flywheels 146: if (!isActive[bribeFlywheel]) revert FlywheelNotActive(); 147: 148: /// @dev This is permanent; can't be re-added 149: delete isActive[bribeFlywheel];
https://github.com/code-423n4/2023-05-maia/blob/main/src/gauges/BaseV2Gauge.sol#L146-L149
strategyGauges[strategy]
should be cached in local storageFile: src/gauges/factories/BaseV2GaugeFactory.sol 110: if (address(strategyGauges[strategy]) != address(0)) revert GaugeAlreadyExists(); 111: 112: BaseV2Gauge gauge = newGauge(strategy, data); 113: strategyGauges[strategy] = gauge;
gaugeIds[gauge]
, gauges[gaugeIds[gauge]]
and activeGauges[gauge]
should be cached in local storageFile: src/gauges/factories/BaseV2GaugeFactory.sol 131: if (!activeGauges[gauge] || gauges[gaugeIds[gauge]] != gauge) revert InvalidGauge(); 132: delete gauges[gaugeIds[gauge]]; 133: delete gaugeIds[gauge]; 134: delete activeGauges[gauge];
rewardsAccrued[user]
should be cached in local storageFile: src/rewards/base/FlywheelCore.sol 95: uint256 accrued = rewardsAccrued[user]; 96: 97: if (accrued != 0) { 98: rewardsAccrued[user] = 0;
https://github.com/code-423n4/2023-05-maia/blob/main/src/rewards/base/FlywheelCore.sol#L95-L98
strategyIndex[strategy]
should be cached in local storageFile: src/rewards/base/FlywheelCore.sol 116: require(strategyIndex[strategy] == 0, "strategy"); 117: strategyIndex[strategy] = ONE;
https://github.com/code-423n4/2023-05-maia/blob/main/src/rewards/base/FlywheelCore.sol#L116-L117
userIndex[strategy][user]
and rewardsAccrued[user]
should be cached in local storageFile: src/rewards/base/FlywheelCore.sol 183: uint256 supplierIndex = userIndex[strategy][user]; 184: 185: // sync user index to global 186: userIndex[strategy][user] = index; 187: 188: // if user hasn't yet accrued rewards, grant them interest from the strategy beginning if they have a balance 189: // zero balances will have no effect other than syncing to global index 190: if (supplierIndex == 0) { 191: supplierIndex = ONE; 192: } 193: 194: uint256 deltaIndex = index - supplierIndex; 195: // use the booster or token balance to calculate reward balance multiplier 196: uint256 supplierTokens = address(flywheelBooster) != address(0) 197: ? flywheelBooster.boostedBalanceOf(strategy, user) 198: : strategy.balanceOf(user); 199: 200: // accumulate rewards by multiplying user tokens by rewardsPerToken index and adding on unclaimed 201: uint256 supplierDelta = (supplierTokens * deltaIndex) / ONE; 202: uint256 supplierAccrued = rewardsAccrued[user] + supplierDelta; 203: 204: rewardsAccrued[user] = supplierAccrued;
https://github.com/code-423n4/2023-05-maia/blob/main/src/rewards/base/FlywheelCore.sol#L183-204
getUserBoost[user]
should be cached in local storageFile: src/erc-20/ERC20Boost.sol 126: if (getUserBoost[user] < userGaugeBoost) { 127: getUserBoost[user] = userGaugeBoost;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Boost.sol#L126-L127
_delegatesVotesCount[delegator][delegatee]
should be cached in local storageFile: src/erc-20/ERC20MultiVotes.sol 218: uint256 newDelegates = _delegatesVotesCount[delegator][delegatee] - amount; 219: 220: if (newDelegates == 0) { 221: require(_delegates[delegator].remove(delegatee)); 222: } 223: 224: _delegatesVotesCount[delegator][delegatee] = newDelegates;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20MultiVotes.sol#L222-L228
getUserWeight[user]
should be cached in local storageFile: src/erc-20/ERC20Gauges.sol 232: newUserWeight = getUserWeight[user] + weight; 233: 234: // new user weight must be less than or equal to the total user weight 235: if (newUserWeight > getVotes(user)) revert OverWeightError(); 236: 237: // Update gauge state 238: getUserWeight[user] = newUserWeight; 239:
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Gauges.sol#L233-L239
getUserGaugeWeight[user][gauge]
should be cached in local storageFile: src/erc-20/ERC20Gauges.sol 295: uint112 oldWeight = getUserGaugeWeight[user][gauge]; 296: 297: IBaseV2Gauge(gauge).accrueBribes(user); 298: 299: getUserGaugeWeight[user][gauge] = oldWeight - weight;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Gauges.sol#L295-L299
getUserWeight[user]
should be cached in local storageFile: src/erc-20/ERC20Gauges.sol 317: newUserWeight = getUserWeight[user] - weight; 318: getUserWeight[user] = newUserWeight;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Gauges.sol#L317-L318
After caching the value of a particular key of a mapping or the value of a particular index of an array,the cache must be used in order to avoid offset calculations which is why the value was cached in the first place.Doing so, ~40 gas savings per access can be saved.
5 instances in 4 files
File: src/uni-v3-staker/UniswapV3Staker.sol 251: Deposit storage deposit = deposits[tokenId]; 252: 253: if (deposit.owner != msg.sender) revert NotCalledByOwner(); 254: if (deposit.stakedTimestamp != 0) revert TokenStakedError(); 255: 256: delete deposits[tokenId];
recommended code :
251: Deposit storage deposit = deposits[tokenId]; 252: 253: if (deposit.owner != msg.sender) revert NotCalledByOwner(); 254: if (deposit.stakedTimestamp != 0) revert TokenStakedError(); 255: 256: delete deposit;
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L251
File: src/ulysses-omnichain/RootBridgeAgent.sol 592: Settlement storage settlement = getSettlement[_settlementNonce]; 593: 594: //Clear Global hTokens To Recipient on Root Chain cancelling Settlement to Branch 595: for (uint256 i = 0; i < settlement.hTokens.length;) { 596: //Check if asset 597: if (settlement.hTokens[i] != address(0)) { 598: //Move hTokens from Branch to Root + Mint Sufficient hTokens to match new port deposit 599: IPort(localPortAddress).bridgeToRoot( 600: msg.sender, 601: IPort(localPortAddress).getGlobalTokenFromLocal(settlement.hTokens[i], settlement.toChain), 602: settlement.amounts[i], 603: settlement.deposits[i], 604: settlement.toChain 605: ); 606: } 607: 608: unchecked { 609: ++i; 610: } 611: } 612: 613: // Delete Settlement 614: delete getSettlement[_settlementNonce];
recommended code :
File: src/ulysses-omnichain/RootBridgeAgent.sol 592: Settlement storage settlement = getSettlement[_settlementNonce]; 593: 594: //Clear Global hTokens To Recipient on Root Chain cancelling Settlement to Branch 595: for (uint256 i = 0; i < settlement.hTokens.length;) { 596: //Check if asset 597: if (settlement.hTokens[i] != address(0)) { 598: //Move hTokens from Branch to Root + Mint Sufficient hTokens to match new port deposit 599: IPort(localPortAddress).bridgeToRoot( 600: msg.sender, 601: IPort(localPortAddress).getGlobalTokenFromLocal(settlement.hTokens[i], settlement.toChain), 602: settlement.amounts[i], 603: settlement.deposits[i], 604: settlement.toChain 605: ); 606: } 607: 608: unchecked { 609: ++i; 610: } 611: } 612: 613: // Delete Settlement 614: delete settlement;
https://github.com/code-423n4/2023-05-maia/blob/main/src/ulysses-omnichain/RootBridgeAgent.sol#L615
File: src/ulysses-omnichain/BranchBridgeAgent.sol 946: Deposit storage deposit = getDeposit[_depositNonce]; 947: 948: //Transfer token to depositor / user 949: for (uint256 i = 0; i < deposit.hTokens.length;) { 950: _clearToken(deposit.owner, deposit.hTokens[i], deposit.tokens[i], deposit.amounts[i], deposit.deposits[i]); 951: 952: unchecked { 953: ++i; 954: } 955: } 956: 957: //Delete Failed Deposit Token Info 958: delete getDeposit[_depositNonce];
recommended code :
File: src/ulysses-omnichain/BranchBridgeAgent.sol 946: Deposit storage deposit = getDeposit[_depositNonce]; 947: 948: //Transfer token to depositor / user 949: for (uint256 i = 0; i < deposit.hTokens.length;) { 950: _clearToken(deposit.owner, deposit.hTokens[i], deposit.tokens[i], deposit.amounts[i], deposit.deposits[i]); 951: 952: unchecked { 953: ++i; 954: } 955: } 956: 957: //Delete Failed Deposit Token Info 958: delete deposit;
File: src/erc-20/ERC20Boost.sol 177: GaugeState storage gaugeState = getUserGaugeBoost[msg.sender][gauge]; 178: if (boost >= gaugeState.userGaugeBoost) { 179: _userGauges[msg.sender].remove(gauge); 180: delete getUserGaugeBoost[msg.sender][gauge];
recommended code :
File: src/erc-20/ERC20Boost.sol 177: GaugeState storage gaugeState = getUserGaugeBoost[msg.sender][gauge]; 178: if (boost >= gaugeState.userGaugeBoost) { 179: _userGauges[msg.sender].remove(gauge); 180: delete gaugeState;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Boost.sol#L179
File: src/erc-20/ERC20Boost.sol 211: GaugeState storage gaugeState = getUserGaugeBoost[msg.sender][gauge]; 212: 213: if (_deprecatedGauges.contains(gauge) || boost >= gaugeState.userGaugeBoost) { 214: require(_userGauges[msg.sender].remove(gauge)); // Remove from set. Should never fail. 215: delete getUserGaugeBoost[msg.sender][gauge];
recommended code :
File: src/erc-20/ERC20Boost.sol 210: 211: GaugeState storage gaugeState = getUserGaugeBoost[msg.sender][gauge]; 212: 213: if (_deprecatedGauges.contains(gauge) || boost >= gaugeState.userGaugeBoost) { 214: require(_userGauges[msg.sender].remove(gauge)); // Remove from set. Should never fail. 215: delete gaugeState;
https://github.com/code-423n4/2023-05-maia/blob/main/src/erc-20/ERC20Boost.sol#L214
#0 - c4-judge
2023-07-11T07:02:56Z
trust1995 marked the issue as grade-a
#1 - c4-sponsor
2023-07-12T18:29:45Z
0xBugsy marked the issue as sponsor confirmed
#2 - c4-sponsor
2023-07-12T18:29:49Z
0xBugsy marked the issue as disagree with severity
#3 - 0xBugsy
2023-07-12T20:44:41Z
Didn't mean to mark this as marked the issue as disagree with severity
! Believe this is a very well structured submission
#4 - 0xLightt
2023-09-07T11:28:42Z