Abracadabra Mimswap - albahaca's results

General Information

Platform: Code4rena

Start Date: 07/03/2024

Pot Size: $63,000 USDC

Total HM: 20

Participants: 36

Period: 5 days

Judge: cccz

Total Solo HM: 11

Id: 349

League: BLAST

Abracadabra Money

Findings Distribution

Researcher Performance

Rank: 23/36

Findings: 2

Award: $184.90

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

15.328 USDC - $15.33

Labels

bug
grade-b
QA (Quality Assurance)
sufficient quality report
Q-04

External Links

Low Risk Issues

SUMMARY

issueinstance
[L-01]_setupRole is Deprecated:6
[L-02]Missing checks for address(0x0) when assigning values to address state variables:2
[L-03]Unsafe downcast:7
[L-04]Array does not have a pop function:5
[L-05]Approve type(uint256).max may not work with some tokens:2
[L-06]Setters should have initial value check:19
[L-07]Empty receive()/payable fallback() function does not authorize requests:2
[L-08]Contracts are designed to receive ETH but do not implement function for withdrawal:2
[L-09]latestAnswer() is deprecated:1
[L-10]Int casting block.timestamp can reduce the lifespan of a contract:1
[L-11]Contract inherits Pausable but does not expose pausing/unpausing functionality:1
[L-12]No limits when setting state variable amounts:21
[L-13]Pausing withdrawals is unfair to the users:1
[L-14]Allowed fees/rates should be capped by smart contracts:6
[L-15]Owner can renounce ownership while system is paused:2
[L-16]Constructor contains no validation:2
[L-17]For loops in public or external functions should be avoided due to high gas costs and possible DOS:2
[L-18]Function calls within for loops:4
[L-19]External calls in an unbounded for-loop may result in a DoS:3
[L-20]Multiplication on the result of a division:4
[L-21]Governance functions should be controlled by time locks:35
[L-22]Code does not follow the best practice of check-effects-interaction:4
[L-23]The additions/multiplications may silently overflow because they're in unchecked blocks with no preceding value checks, which may lead to unexpected results.:1
[L-24]Unbounded state array which is iterated upon:14
[L-25]Prefer continue over revert model in iteration:2
[L-26]Contracts use infinite approvals with no means to revoke:1
[L-27]External calls in modifiers should be avoided:1

[L-01] - _setupRole is Deprecated:

_setupRole is deprecated in favor of grantRole and renounceRole.

There are 6 instances of this issue:

File: src/blast/BlastOnboardingBoot.sol
MIM.safeApprove(address(router), type(uint256).max);

USDB.safeApprove(address(router), type(uint256).max);

pool.safeApprove(address(staking), totalPoolShares);

MIM.safeApprove(address(router), type(uint256).max);

USDB.safeApprove(address(router), type(uint256).max);

pool.safeApprove(address(staking), totalPoolShares);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L122:122

[L-02] - Missing checks for address(0x0) when assigning values to address state variables:

This issue arises when an address state variable is assigned a value without a preceding check to ensure it isn't address(0x0). This can lead to unexpected behavior as address(0x0) often represents an uninitialized address.

There are 2 instances of this issue:

File: src/blast/BlastOnboarding.sol
191             bootstrapper = bootstrapper_;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L191:191

File: src/mimswap/auxiliary/FeeRateModel.sol
58              implementation = implementation_;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/auxiliary/FeeRateModel.sol#L58:58

[L-03] - Unsafe downcast:

When a type is downcast to a smaller type, the higher order bits are truncated, effectively applying a modulo to the original value. Without any other checks, this wrapping will lead to unexpected behavior and bugs.

There are 7 instances of this issue

File: src/mimswap/MagicLP.sol
438             _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) - (uint256(_BASE_TARGET_) * shareAmount).divCeil(totalShares));

125             _BLOCK_TIMESTAMP_LAST_ = uint32(block.timestamp % 2 ** 32);

495             _I_ = uint128(newI);

494             _K_ = uint64(newK);

439             _QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_) - (uint256(_QUOTE_TARGET_) * shareAmount).divCeil(totalShares));

493             _LP_FEE_RATE_ = uint64(newLpFeeRate);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L493:493

File: src/staking/LockingMultiRewards.sol
389             reward.lastUpdateTime = uint248(block.timestamp);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L389:389

[L-04] - Array does not have a pop function:

Arrays without the pop operation in Solidity can lead to inefficient memory management and increase the likelihood of out-of-gas errors.

There are 5 instances of this issue

File: src/mimswap/periphery/Factory.sol
149             pools[baseToken][quoteToken].push(pool);

150             userPools[creator].push(pool);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L150:150

File: src/staking/LockingMultiRewards.sol
313             rewardTokens.push(rewardToken);

501                 _userLocks[user].push(LockedBalance({amount: amount, unlockTime: _nextUnlockTime}));

648                     _userRewardLock[user].items.push(RewardLockItem({token: rewardToken, amount: rewardAmount}));

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L648:648

[L-05] - Approve type(uint256).max may not work with some tokens:

Source: https://github.com/d-xo/weird-erc20#revert-on-large-approvals--transfers

There are 2 instances of this issue

File: src/blast/BlastOnboardingBoot.sol
103             MIM.safeApprove(address(router), type(uint256).max);

104             USDB.safeApprove(address(router), type(uint256).max);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L104:104

[L-06] - Setters should have initial value check:

Setters should have initial value check to prevent assigning wrong value to the variable. Assginment of wrong value can lead to unexpected behavior of the contract.

There are 19 instances of this issue

File: src/blast/BlastBox.sol
72          function setFeeTo(address feeTo_) external onlyOwner {
73              if (feeTo_ == address(0)) {
74                  revert ErrZeroAddress();
75              }
76      
77              feeTo = feeTo_;
78              emit LogFeeToChanged(feeTo_);
79          }


81          function setTokenEnabled(address token, bool enabled) external onlyOwner {
82              enabledTokens[token] = enabled;
83      
84              // enable native yields if token is recognized
85              // no matter if it's enabled or not
86              if (registry.nativeYieldTokens(token)) {
87                  BlastYields.enableTokenClaimable(token);
88              }
89      
90              emit LogTokenDepositEnabled(token, enabled);
91          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastBox.sol#L81:91

File: src/blast/BlastGovernor.sol
40          function setFeeTo(address _feeTo) external onlyOwner {
41              if(_feeTo == address(0)) {
42                  revert ErrZeroAddress();
43              }
44              
45              feeTo = _feeTo;
46              emit LogFeeToChanged(_feeTo);
47          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L40:47

File: src/blast/BlastMagicLP.sol
80          function setFeeTo(address feeTo_) external onlyImplementation onlyImplementationOwner {
81              if (feeTo_ == address(0)) {
82                  revert ErrZeroAddress();
83              }
84      
85              feeTo = feeTo_;
86              emit LogFeeToChanged(feeTo_);
87          }


89          function setOperator(address operator, bool status) external onlyImplementation onlyImplementationOwner {
90              operators[operator] = status;
91              emit LogOperatorChanged(operator, status);
92          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L89:92

File: src/blast/BlastOnboarding.sol
147         function setFeeTo(address feeTo_) external onlyOwner {
148             if (feeTo_ == address(0)) {
149                 revert ErrZeroAddress();
150             }
151     
152             feeTo = feeTo_;
153             emit LogFeeToChanged(feeTo_);
154         }


175         function setTokenSupported(address token, bool supported) external onlyOwner {
176             supportedTokens[token] = supported;
177     
178             if (registry.nativeYieldTokens(token)) {
179                 BlastYields.enableTokenClaimable(token);
180             }
181     
182             emit LogTokenSupported(token, supported);
183         }


185         function setCap(address token, uint256 cap) external onlyOwner onlySupportedTokens(token) {
186             caps[token] = cap;
187             emit LogTokenCapChanged(token, cap);
188         }


190         function setBootstrapper(address bootstrapper_) external onlyOwner {
191             bootstrapper = bootstrapper_;
192             emit LogBootstrapperChanged(bootstrapper_);
193         }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L190:193

File: src/blast/BlastOnboardingBoot.sol

137         function setStaking(LockingMultiRewards _staking) external onlyOwner {
138             if (ready) {
139                 revert ErrCannotChangeOnceReady();
140             }
141     
142             staking = _staking;
143             emit LogStakingChanged(address(_staking));
144         }


146         function setReady(bool _ready) external onlyOwner onlyState(State.Closed) {
147             ready = _ready;
148             emit LogReadyChanged(ready);
149         }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L146:149

File: src/blast/BlastTokenRegistry.sol

14          function setNativeYieldTokenEnabled(address token, bool enabled) external onlyOwner {
15              if (token == address(0)) {
16                  revert ErrZeroAddress();
17              }
18      
19              nativeYieldTokens[token] = enabled;
20              emit LogNativeYieldTokenEnabled(token, enabled);
21          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastTokenRegistry.sol#L14:21

File: src/mimswap/MagicLP.sol


470         function setParameters(
471             address assetTo,
472             uint256 newLpFeeRate,
473             uint256 newI,
474             uint256 newK,
475             uint256 baseOutAmount,
476             uint256 quoteOutAmount,
477             uint256 minBaseReserve,
478             uint256 minQuoteReserve
479         ) public nonReentrant onlyImplementationOwner {
480             if (_BASE_RESERVE_ < minBaseReserve || _QUOTE_RESERVE_ < minQuoteReserve) {
481                 revert ErrReserveAmountNotEnough();
482             }
483             if (newI == 0 || newI > MAX_I) {
484                 revert ErrInvalidI();
485             }
486             if (newK > MAX_K) {
487                 revert ErrInvalidK();
488             }
489             if (newLpFeeRate < MIN_LP_FEE_RATE || newLpFeeRate > MAX_LP_FEE_RATE) {
490                 revert ErrInvalidLPFeeRate();
491             }
492     
493             _LP_FEE_RATE_ = uint64(newLpFeeRate);
494             _K_ = uint64(newK);
495             _I_ = uint128(newI);
496     
497             _transferBaseOut(assetTo, baseOutAmount);
498             _transferQuoteOut(assetTo, quoteOutAmount);
499             (uint256 newBaseBalance, uint256 newQuoteBalance) = _resetTargetAndReserve();
500     
501             emit ParametersChanged(newLpFeeRate, newI, newK, newBaseBalance, newQuoteBalance);
502         }


560         function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal {
561             _BASE_RESERVE_ = baseReserve.toUint112();
562             _QUOTE_RESERVE_ = quoteReserve.toUint112();
563     
564             _twapUpdate();
565         }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L560:565

File: src/mimswap/auxiliary/FeeRateModel.sol


46          function setMaintainer(address maintainer_) external onlyOwner {
47              if (maintainer_ == address(0)) {
48                  revert ErrZeroAddress();
49              }
50      
51              maintainer = maintainer_;
52              emit LogMaintainerChanged(maintainer_);
53          }


57          function setImplementation(address implementation_) public onlyOwner {
58              implementation = implementation_;
59              emit LogImplementationChanged(implementation_);
60          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/auxiliary/FeeRateModel.sol#L57:60

File: src/mimswap/periphery/Factory.sol


96          function setLpImplementation(address implementation_) external onlyOwner {
97              if (implementation_ == address(0)) {
98                  revert ErrZeroAddress();
99              }
100     
101             implementation = implementation_;
102             emit LogSetImplementation(implementation_);
103         }


105         function setMaintainerFeeRateModel(IFeeRateModel maintainerFeeRateModel_) external onlyOwner {
106             if (address(maintainerFeeRateModel_) == address(0)) {
107                 revert ErrZeroAddress();
108             }
109     
110             maintainerFeeRateModel = maintainerFeeRateModel_;
111             emit LogSetMaintainerFeeRateModel(maintainerFeeRateModel_);
112         }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L105:112

File: src/staking/LockingMultiRewards.sol


317         function setMinLockAmount(uint256 _minLockAmount) external onlyOwner {
318             emit LogSetMinLockAmount(minLockAmount, _minLockAmount);
319             minLockAmount = _minLockAmount;
320         }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L317:320

[L-07] - Empty receive()/payable fallback() function does not authorize requests:

If the intention is for the Ether to be used, the function should call another function, otherwise it should revert (e.g. require(msg.sender == address(weth))). Having no access control on the function means that someone may send Ether to the contract, and have no way to get anything back out, which is a loss of funds. If the concern is having to spend a small amount of gas to check the sender against an immutable address, the code should at least have a function to rescue unused Ether.

There are 2 instances of this issue

File: src/blast/BlastGovernor.sol
13          receive() external payable {}

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L13:13

File: src/mimswap/periphery/Router.sol
36          receive() external payable {}

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L36:36

[L-08] - Contracts are designed to receive ETH but do not implement function for withdrawal:

The following contracts can receive ETH but can not withdraw, ETH is occasionally sent by users will be stuck in those contracts. This functionality also applies to baseTokens resulting in locked tokens and loss of funds. There are 2 instances of this issue

File: src/blast/BlastGovernor.sol
13          receive() external payable {}

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L13:13

File: src/mimswap/periphery/Router.sol
36          receive() external payable {}

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L36:36

[L-09] - latestAnswer() is deprecated:

Use latestRoundData() instead so that you can tell whether the answer is stale or not. The latestAnswer() function returns zero if it is unable to fetch data, which may be the case if ChainLink stops supporting this API. The API and its deprecation message no longer even appear on the ChainLink website, so it is dangerous to continue using it.

There are 1 instances of this issue

File: src/oracles/aggregators/MagicLpAggregator.sol

function latestAnswer() public view override returns (int256) {

uint256 baseAnswerNomalized = uint256(baseOracle.latestAnswer()) * (10 ** (WAD - baseOracle.decimals()));

uint256 quoteAnswerNormalized = uint256(quoteOracle.latestAnswer()) * (10 ** (WAD - quoteOracle.decimals()));

return (0, latestAnswer(), 0, 0, 0);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/oracles/aggregators/MagicLpAggregator.sol#L49

[L-10] - Int casting block.timestamp can reduce the lifespan of a contract:

Consider removing casting to ensure future functionality.

File: src/staking/LockingMultiRewards.sol
389             reward.lastUpdateTime = uint248(block.timestamp);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L389:389

[L-11] - Contract inherits Pausable but does not expose pausing/unpausing functionality:

When a contract inherits from the Pausable contract in Solidity, it gains the ability to pause and resume certain actions, which can be crucial in mitigating potential risks or issues. However, the _pause and _unpause functions are internal and can only be called from within the contract or derived contracts. If the functionality to pause and unpause isn't exposed through public or external functions, it can't be called by the contract owner or designated pauser account. To take advantage of the Pausable contract, create public or external functions that call _pause and _unpause.

File: src/blast/BlastOnboarding.sol
12      contract BlastOnboardingData is Owned, Pausable {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L12:12

[L-12] - No limits when setting state variable amounts:

It is important to ensure state variables numbers are set to a reasonable value.

File: src/mimswap/MagicLP.sol
125             _BLOCK_TIMESTAMP_LAST_ = uint32(block.timestamp % 2 ** 32);

140                 _RState_ = uint32(PMMPricing.RState.ONE);

142                 _QUOTE_TARGET_ = _QUOTE_RESERVE_;

145                 _RState_ = uint32(PMMPricing.RState.ONE);

194             state.i = _I_;

195             state.K = _K_;

196             state.B = _BASE_RESERVE_;

197             state.Q = _QUOTE_RESERVE_;

198             state.B0 = _BASE_TARGET_; // will be calculated in adjustedTarget

199             state.Q0 = _QUOTE_TARGET_;

212             R = uint256(state.R);

220             baseReserve = _BASE_RESERVE_;

221             quoteReserve = _QUOTE_RESERVE_;

258                 _RState_ = uint32(newRState);

281                 _RState_ = uint32(newRState);

322                     _RState_ = uint32(newRState);

344                     _RState_ = uint32(newRState);

493             _LP_FEE_RATE_ = uint64(newLpFeeRate);

494             _K_ = uint64(newK);

495             _I_ = uint128(newI);

540             _RState_ = uint32(PMMPricing.RState.ONE);

557             _BLOCK_TIMESTAMP_LAST_ = blockTimestamp;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L557:557

File: src/mimswap/libraries/Math.sol
31              y = x;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/Math.sol#L31:31

File: src/mimswap/periphery/Router.sol
155                     quoteAdjustedInAmount = quoteInAmount;

524                 baseAdjustedInAmount = shares;

531                         baseAdjustedInAmount = baseInAmount;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L531:531

[L-13] - Pausing withdrawals is unfair to the users:

Users should always have the possibility of accessing their own funds, but when these functions are paused, their funds will be locked until the contracts are unpaused.

File: src/blast/BlastOnboarding.sol
132         function withdraw(address token, uint256 amount) external whenNotPaused onlySupportedTokens(token) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L132:132

[L-14] - Allowed fees/rates should be capped by smart contracts:

Fees/rates should be required to be below 100%, preferably at a much lower limit, to ensure users don't have to monitor the blockchain for changes prior to using the protocol.

File: src/blast/BlastBox.sol
72          function setFeeTo(address feeTo_) external onlyOwner {
73              if (feeTo_ == address(0)) {
74                  revert ErrZeroAddress();
75              }
76      
77              feeTo = feeTo_;
78              emit LogFeeToChanged(feeTo_);
79          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastBox.sol#L72:79

File: src/blast/BlastGovernor.sol
40          function setFeeTo(address _feeTo) external onlyOwner {
41              if(_feeTo == address(0)) {
42                  revert ErrZeroAddress();
43              }
44              
45              feeTo = _feeTo;
46              emit LogFeeToChanged(_feeTo);
47          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L40:47

File: src/blast/BlastMagicLP.sol
80          function setFeeTo(address feeTo_) external onlyImplementation onlyImplementationOwner {
81              if (feeTo_ == address(0)) {
82                  revert ErrZeroAddress();
83              }
84      
85              feeTo = feeTo_;
86              emit LogFeeToChanged(feeTo_);
87          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L80:87

File: src/blast/BlastOnboarding.sol
147         function setFeeTo(address feeTo_) external onlyOwner {
148             if (feeTo_ == address(0)) {
149                 revert ErrZeroAddress();
150             }
151     
152             feeTo = feeTo_;
153             emit LogFeeToChanged(feeTo_);
154         }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L147:154

File: src/mimswap/periphery/Factory.sol
105         function setMaintainerFeeRateModel(IFeeRateModel maintainerFeeRateModel_) external onlyOwner {
106             if (address(maintainerFeeRateModel_) == address(0)) {
107                 revert ErrZeroAddress();
108             }
109     
110             maintainerFeeRateModel = maintainerFeeRateModel_;
111             emit LogSetMaintainerFeeRateModel(maintainerFeeRateModel_);
112         }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L105:112

[L-15] - Owner can renounce ownership while system is paused:

The contract owner is not prevented from renouncing the ownership while the contract is paused, which would cause any user assets stored in the protocol to be locked indefinitely.

File: src/blast/BlastOnboarding.sol
214         function pause() external onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L214:214

File: src/staking/LockingMultiRewards.sol
337         function pause() external onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L337:337

[L-16] - Constructor contains no validation:

In Solidity, when values are being assigned in constructors to unsigned or integer variables, it's crucial to ensure the provided values adhere to the protocol's specific operational boundaries as laid out in the project specifications and documentation. If the constructors lack appropriate validation checks, there's a risk of setting state variables with values that could cause unexpected and potentially detrimental behavior within the contract's operations, violating the intended logic of the protocol. This can compromise the contract's security and impact the maintainability and reliability of the system. In order to avoid such issues, it is recommended to incorporate rigorous validation checks in constructors. These checks should align with the project's defined rules and constraints, making use of Solidity's built-in require function to enforce these conditions. If the validation checks fail, the require function will cause the transaction to revert, ensuring the integrity and adherence to the protocol's expected behavior.

File: src/mimswap/MagicLP.sol
84          constructor(address owner_) Owned(owner_) {
85              implementation = this;
86      
87              // prevents the implementation contract initialization
88              _INITIALIZED_ = true;
89          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L84:89

File: src/oracles/aggregators/MagicLpAggregator.sol
21          constructor(IMagicLP pair_, IAggregator baseOracle_, IAggregator quoteOracle_) {
22              pair = pair_;
23              baseOracle = baseOracle_;
24              quoteOracle = quoteOracle_;
25              baseDecimals = IERC20Metadata(pair_._BASE_TOKEN_()).decimals();
26              quoteDecimals = IERC20Metadata(pair_._QUOTE_TOKEN_()).decimals();
27          }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/oracles/aggregators/MagicLpAggregator.sol#L21:27

[L-17] - For loops in public or external functions should be avoided due to high gas costs and possible DOS:

In Solidity, for loops can potentially cause Denial of Service (DoS) attacks if not handled carefully. DoS attacks can occur when an attacker intentionally exploits the gas cost of a function, causing it to run out of gas or making it too expensive for other users to call. Below are some scenarios where for loops can lead to DoS attacks: Nested for loops can become exceptionally gas expensive and should be used sparingly.

File: src/blast/BlastOnboarding.sol

164         function claimTokenYields(address[] memory tokens) external onlyOwner {
165             for (uint256 i = 0; i < tokens.length; i++) {
166                 if (!supportedTokens[tokens[i]]) {
167                     revert ErrUnsupportedToken();
168                 }
169                 if (registry.nativeYieldTokens(tokens[i])) {
170                     BlastYields.claimAllTokenYields(tokens[i], feeTo);
171                 }
172             }
173         }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L164:173

File: src/staking/LockingMultiRewards.sol
397         function processExpiredLocks(address[] memory users, uint256[] calldata lockIndexes) external onlyOperators {
398             if (users.length != lockIndexes.length) {
399                 revert ErrLengthMismatch();
400             }
401     
402             _updateRewardsForUsers(users);
403     
404             // Release all expired users' locks
405             for (uint256 i; i < users.length; ) {
406                 address user = users[i];
407                 Balances storage bal = _balances[user];
408                 LockedBalance[] storage locks = _userLocks[user];
409     
410                 if (locks.length == 0) {
411                     revert ErrNoLocks();
412                 }
413     
414                 uint256 index = lockIndexes[i];
415     
416                 // Prevents processing `lastLockIndex` out of order
417                 if (index == lastLockIndex[user] && locks.length > 1) {
418                     revert ErrInvalidLockIndex();
419                 }
420     
421                 // prohibit releasing non-expired locks
422                 if (locks[index].unlockTime > block.timestamp) {
423                     revert ErrLockNotExpired();
424                 }
425     
426                 uint256 amount = locks[index].amount;
427                 uint256 lastIndex = locks.length - 1;
428     
429                 /// Last lock index changed place with the one we just swapped.
430                 if (lastLockIndex[user] == lastIndex) {
431                     lastLockIndex[user] = index;
432                 }
433     
434                 if (index != lastIndex) {
435                     locks[index] = locks[lastIndex];
436                     emit LogLockIndexChanged(user, lastIndex, index);
437                 }
438     
439                 locks.pop();
440     
441                 unlockedSupply += amount;
442                 lockedSupply -= amount;
443     
444                 bal.unlocked += amount;
445                 bal.locked -= amount;
446     
447                 emit LogUnlocked(user, amount, index);
448     
449                 unchecked {
450                     ++i;
451                 }
452             }
453         }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L397:453

[L-18] - Function calls within for loops:

Making function calls within loops in Solidity can lead to inefficient gas usage, potential bottlenecks, and increased vulnerability to attacks. Each function call or external call consumes gas, and when executed within a loop, the gas cost multiplies, potentially causing the transaction to run out of gas or exceed block gas limits. This can result in transaction failure or unpredictable behavior.

File: src/staking/LockingMultiRewards.sol
580                 for (uint256 j; j < users.length; ) {
581                     address user = users[j];
582                     _udpateUserRewards(user, balanceOf(user), token, rewardPerToken_);
583     
584                     unchecked {
585                         ++j;
586                     }
587                 }


561             for (uint256 i; i < rewardTokens.length; ) {
562                 address token = rewardTokens[i];
563                 _udpateUserRewards(user, balance, token, _updateRewardsGlobal(token, totalSupply_));
564     
565                 unchecked {
566                     ++i;
567                 }
568             }


547             for (uint256 i; i < rewardTokens.length; ) {
548                 _updateRewardsGlobal(rewardTokens[i], totalSupply_);
549                 unchecked {
550                     ++i;
551                 }
552             }


575             for (uint256 i; i < rewardTokens.length; ) {
576                 address token = rewardTokens[i];
577                 uint256 rewardPerToken_ = _updateRewardsGlobal(token, totalSupply_);
578     
579                 // Record each user's rewards
580                 for (uint256 j; j < users.length; ) {
581                     address user = users[j];
582                     _udpateUserRewards(user, balanceOf(user), token, rewardPerToken_);
583     
584                     unchecked {
585                         ++j;
586                     }
587                 }
588     
589                 unchecked {
590                     ++i;
591                 }
592             }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L575:592

[L-19] - External calls in an unbounded for-loop may result in a DoS:

Consider limiting the number of iterations in for-loops that make external calls.

File: src/blast/BlastOnboarding.sol
165             for (uint256 i = 0; i < tokens.length; i++) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L165:165

File: src/staking/LockingMultiRewards.sol
405             for (uint256 i; i < users.length; ) {

614             for (uint256 i; i < rewardTokens.length; ) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L614:614

[L-20] - Multiplication on the result of a division:

Dividing an integer by another integer will often result in loss of precision. When the result is multiplied by another number, the loss of precision is magnified, often to material magnitudes. (X / Z) * Y should be re-written as (X * Y) / Z.

File: src/mimswap/libraries/Math.sol
99                  _sqrt = sqrt(((ki / V1) * delta) + DecimalMath.ONE2);

158                     temp = (((delta * V1) / V0) * i) / V0;

170             uint256 part2 = (((k * V0) / V1) * V0) + (i * delta); // kQ0^2/Q1-i*deltaB

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/Math.sol#L170:170

File: src/staking/LockingMultiRewards.sol
258             return (block.timestamp / rewardsDuration) * rewardsDuration;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L258:258

[L-21] - Governance functions should be controlled by time locks:

Governance functions (such as upgrading contracts, setting critical parameters) should be controlled using time locks to introduce a delay between a proposal and its execution. This gives users time to exit before a potentially dangerous or malicious operation is applied.

File: src/blast/BlastBox.sol

81          function setTokenEnabled(address token, bool enabled) external onlyOwner {

68          function callBlastPrecompile(bytes calldata data) external onlyOwner {

72          function setFeeTo(address feeTo_) external onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastBox.sol#L72:79

File: src/blast/BlastGovernor.sol
40          function setFeeTo(address _feeTo) external onlyOwner {

53          function execute(address to, uint256 value, bytes calldata data) external onlyOwner returns (bool success, bytes memory result) {

49          function callBlastPrecompile(bytes calldata data) external onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L49:51

File: src/blast/BlastOnboarding.sol
175         function setTokenSupported(address token, bool supported) external onlyOwner {

156         function callBlastPrecompile(bytes calldata data) external onlyOwner {

218         function unpause() external onlyOwner {

160         function claimGasYields() external onlyOwner returns (uint256) {

205         function rescue(address token, address to, uint256 amount) external onlyOwner {

147         function setFeeTo(address feeTo_) external onlyOwner {

214         function pause() external onlyOwner {

195         function open() external onlyOwner onlyState(State.Idle) {

200         function close() external onlyOwner onlyState(State.Opened) {

185         function setCap(address token, uint256 cap) external onlyOwner onlySupportedTokens(token) {

190         function setBootstrapper(address bootstrapper_) external onlyOwner {

164         function claimTokenYields(address[] memory tokens) external onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L164:173

File: src/blast/BlastOnboardingBoot.sol
129         function initialize(Router _router) external onlyOwner {

96          function bootstrap(uint256 minAmountOut) external onlyOwner onlyState(State.Closed) returns (address, address, uint256) {

137         function setStaking(LockingMultiRewards _staking) external onlyOwner {

146         function setReady(bool _ready) external onlyOwner onlyState(State.Closed) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L146:149

File: src/blast/BlastTokenRegistry.sol
14          function setNativeYieldTokenEnabled(address token, bool enabled) external onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastTokenRegistry.sol#L14:21

File: src/mimswap/auxiliary/FeeRateModel.sol
46          function setMaintainer(address maintainer_) external onlyOwner {

57          function setImplementation(address implementation_) public onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/auxiliary/FeeRateModel.sol#L57:60

File: src/mimswap/periphery/Factory.sol
105         function setMaintainerFeeRateModel(IFeeRateModel maintainerFeeRateModel_) external onlyOwner {

120         function removePool(
121             address creator,
122             address baseToken,
123             address quoteToken,
124             uint256 poolIndex,
125             uint256 userPoolIndex
126         ) external onlyOwner {

96          function setLpImplementation(address implementation_) external onlyOwner {

116         function addPool(address creator, address baseToken, address quoteToken, address pool) external onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L116:118

File: src/staking/LockingMultiRewards.sol
337         function pause() external onlyOwner {

300         function addReward(address rewardToken) public onlyOwner {

317         function setMinLockAmount(uint256 _minLockAmount) external onlyOwner {

324         function recover(address tokenAddress, uint256 tokenAmount) external onlyOwner {

341         function unpause() external onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L341:343

[L-22] - Code does not follow the best practice of check-effects-interaction:

Code should follow the best-practice of check-effects-interaction, where state variables are updated before any external calls are made. Doing so prevents a large class of reentrancy bugs.

File: src/blast/BlastOnboarding.sol
/// @audit token.safeTransferFrom() called prior to this assignment
118             balances[msg.sender][token].total += amount;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L118:118

File: src/mimswap/MagicLP.sol
/// @audit _BASE_TOKEN_.balanceOf() called prior to this assignment
557             _BLOCK_TIMESTAMP_LAST_ = blockTimestamp;

/// @audit _BASE_TOKEN_.balanceOf() called prior to this assignment
/// @audit State change when _sync() calls _twapUpdate() 
/// @audit at line 553                     _BASE_PRICE_CUMULATIVE_LAST_ += getMidPrice() * timeElapsed;
578             _twapUpdate();

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L578:578

File: src/staking/LockingMultiRewards.sol
/// @audit _getRewards() called prior to this assignment
/// @audit contains nested function call rewardToken.safeTransfer() 
/// @audit located in file 2024-03-abracadabra-money/src/staking/LockingMultiRewards.sol  
619                 rewards[user][rewardToken] = 0;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L619:619

[L-23] - The additions/multiplications may silently overflow because they're in unchecked blocks with no preceding value checks, which may lead to unexpected results.:

The additions/multiplications may silently overflow because they're in unchecked blocks with no preceding value checks, which may lead to unexpected results.

File: src/mimswap/MagicLP.sol
553                     _BASE_PRICE_CUMULATIVE_LAST_ += getMidPrice() * timeElapsed;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L553:553

[L-24] - Unbounded state array which is iterated upon:

Iterating over an unbounded state array in Solidity can result in excessive gas consumption, especially if the array size exceeds the block gas limit. This issue commonly arises in tasks like token distribution. To address this, it is recommended to limit array sizes for iteration, consider alternative data structures like linked lists, adopt paginated processing for smaller batches over multiple transactions, or use a 'state array' with a separate index-tracking array to manage large datasets and avoid gas-related problems.

File: src/staking/LockingMultiRewards.sol
407                 Balances storage bal = _balances[user];

408                 LockedBalance[] storage locks = _userLocks[user];

417                 if (index == lastLockIndex[user] && locks.length > 1) {

430                 if (lastLockIndex[user] == lastIndex) {

431                     lastLockIndex[user] = index;

548                 _updateRewardsGlobal(rewardTokens[i], totalSupply_);

562                 address token = rewardTokens[i];

576                 address token = rewardTokens[i];

615                 address rewardToken = rewardTokens[i];

616                 uint256 rewardAmount = rewards[user][rewardToken];

616                 uint256 rewardAmount = rewards[user][rewardToken];

619                 rewards[user][rewardToken] = 0;

619                 rewards[user][rewardToken] = 0;

648                     _userRewardLock[user].items.push(RewardLockItem({token: rewardToken, amount: rewardAmount}));

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L648:648

[L-25] - Prefer continue over revert model in iteration:

Preferably, it's better to skip operations on array indices when a condition is not met instead of reverting the entire transaction. Reverting could be exploited by malicious actors who might intentionally introduce array objects failing conditional checks, disrupting group operations. It's advisable to skip array indices rather than revert, unless there are valid security or logic reasons for doing otherwise

File: src/blast/BlastOnboarding.sol
165             for (uint256 i = 0; i < tokens.length; i++) {
166                 if (!supportedTokens[tokens[i]]) {
167                     revert ErrUnsupportedToken();
168                 }
169                 if (registry.nativeYieldTokens(tokens[i])) {
170                     BlastYields.claimAllTokenYields(tokens[i], feeTo);
171                 }
172             }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L165:172

File: src/staking/LockingMultiRewards.sol
405             for (uint256 i; i < users.length; ) {
406                 address user = users[i];
407                 Balances storage bal = _balances[user];
408                 LockedBalance[] storage locks = _userLocks[user];
409     
410                 if (locks.length == 0) {
411                     revert ErrNoLocks();
412                 }
413     
414                 uint256 index = lockIndexes[i];
415     
416                 // Prevents processing `lastLockIndex` out of order
417                 if (index == lastLockIndex[user] && locks.length > 1) {
418                     revert ErrInvalidLockIndex();
419                 }
420     
421                 // prohibit releasing non-expired locks
422                 if (locks[index].unlockTime > block.timestamp) {
423                     revert ErrLockNotExpired();
424                 }
425     
426                 uint256 amount = locks[index].amount;
427                 uint256 lastIndex = locks.length - 1;
428     
429                 /// Last lock index changed place with the one we just swapped.
430                 if (lastLockIndex[user] == lastIndex) {
431                     lastLockIndex[user] = index;
432                 }
433     
434                 if (index != lastIndex) {
435                     locks[index] = locks[lastIndex];
436                     emit LogLockIndexChanged(user, lastIndex, index);
437                 }
438     
439                 locks.pop();
440     
441                 unlockedSupply += amount;
442                 lockedSupply -= amount;
443     
444                 bal.unlocked += amount;
445                 bal.locked -= amount;
446     
447                 emit LogUnlocked(user, amount, index);
448     
449                 unchecked {
450                     ++i;
451                 }
452             }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L405:452

[L-26] - Contracts use infinite approvals with no means to revoke:

Infinite approvals on external contracts can be dangerous if the target becomes compromised. See here for a list of approval exploits. The following contracts are vulnerable to such attacks since they have no functionality to revoke the approval (call with amount ). Consider enabling the contract to revoke approval in emergency situations.

File: src/cauldrons/CauldronV4.sol
143             magicInternetMoney.approve(address(bentoBox), type(uint256).max);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/cauldrons/CauldronV4.sol#L143:143

[L-27] - External calls in modifiers should be avoided:

Using external calls within modifiers can introduce reentrancy risks and make a contract's logic less transparent. Modifiers are meant for pre-execution checks, and external calls can lead to unpredictable flow and potential reentrancy issues. Avoiding external calls in modifiers and placing them directly in the function body enhances code clarity, aids in auditing, and minimizes unexpected behaviors. This practice ensures a more explicit execution order and clearer understanding of the code's effects.

File: src/blast/BlastMagicLP.sol
119       if (!BlastMagicLP(address(implementation)).operators(msg.sender) && msg.sender != implementation.owner()) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L119:119

#0 - c4-pre-sort

2024-03-15T15:00:44Z

141345 marked the issue as sufficient quality report

#1 - 141345

2024-03-16T01:04:37Z

214 albahaca l r nc 3 0 11

L 1 n L 2 l L 3 n L 4 n L 5 i L 6 i L 7 n L 8 n L 9 d L 10 n L 11 n L 12 i L 13 i L 14 i L 15 i L 16 i L 17 i L 18 i L 19 i L 20 i L 21 l L 22 l L 23 n L 24 n L 25 n L 26 n dup of https://github.com/code-423n4/2024-03-abracadabra-money-findings/issues/82 L 27 i

#2 - c4-judge

2024-03-29T16:55:44Z

thereksfour marked the issue as grade-b

Findings Information

🌟 Selected for report: hihen

Also found by: 0x11singh99, Bozho, Sathish9098, albahaca, clara, dharma09, oualidpro, pfapostol, slvDev

Labels

bug
G (Gas Optimization)
grade-a
sufficient quality report
G-03

Awards

169.5732 USDC - $169.57

External Links

Gas Optimization For Abracadabra Mimswap

SUMMARY

issueinstance
[G‑01]Avoid contract existence checks by using low level calls51
[G‑02]State variables that are used multiple times in a function should be cached in stack variables8
[G‑03]Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate3
[G‑04]Use Assembly To Check For address(0)26
[G‑05]Use hardcode address instead address(this)41
[G‑06]Constructors can be marked payable16
[G‑07]Use assembly to write address storage values14
[G‑08]Avoid updating storage when the value hasn't changed18
[G‑09]Use calldata instead of memory for function arguments that do not get mutated2
[G‑10]Can Make The Variable Outside The Loop To Save Gas9
[G‑11]Use nested if statements instead of &&8
[G‑12]Sort Solidity operations using short-circuit mode1
[G‑13]Amounts should be checked for 0 before calling a transfer5
[G‑14]Do not calculate constants4
[G‑15]With assembly, .call (bool success) transfer can be done gas-optimized1
[G‑16]Internal functions only called once can be inlined to save gas3
[G‑17]Use constants instead of type(uintx).max6
[G‑18]Multiple accesses of a mapping/array should use a local variable cache5
[G‑19]Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement4
[G‑20]The result of function calls should be cached rather than re-calling the function2
[G‑21]Stack variable used as a cheaper cache for a state variable is only used once2
[G‑22]Use local variables for emitting4
[G‑23]State variables can be packed into fewer storage slots2
[G‑24]Storage re-read via storage pointer4
[G‑25]State variable read in a loop4
[G‑26]Initializers can be marked payable1
[G‑27]Use assembly to emit events57
[G‑28]Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead2
[G‑29]Emit Used In Loop4
[G‑30]Use assembly to calculate hashes to save gas1
[G‑31]Unused named return variables without optimizer waste gas18
[G‑32]Structs can be modified to fit in fewer storage slots2
[G‑33]Use assembly to validate msg.sender1
[G‑34]Unlimited gas consumption risk due to external call recipients1
[G‑35]Same cast is done multiple times5
[G‑36]Using constants instead of enum can save gas2
[G‑37]Move storage pointer to top of function to avoid offset calculation1
[G‑38]Consider using OZ EnumerateSet in place of nested mappings5
[G‑39]Instead of counting down in for statements, count up8
[G‑40]Use the inputs/results of assignments rather than re-reading state variables44

[G-01] Avoid contract existence checks by using low level calls

Prior to 0.8.10 the compiler inserted extra code, including EXTCODESIZE (100 gas), to check for contract existence for external function calls. In more recent solidity versions, the compiler will not insert these checks if the external call has a return value. Similar behavior can be achieved in earlier versions by using low-level calls, since low level calls never check for contract existence.

Note: these instances are missed from bots

There are 51 instances of this issue:

Total save gas : 100 * 51 = 5100

File: src/mimswap/auxiliary/FeeRateModel.sol
39              return IFeeRateImpl(implementation).getFeeRate(msg.sender, trader, lpFeeRate);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/auxiliary/FeeRateModel.sol#L39:39

File: src/mimswap/periphery/Factory.sol
86              IMagicLP(clone).init(address(baseToken_), address(quoteToken_), lpFeeRate_, address(maintainerFeeRateModel), i_, k_);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Factory.sol#L86:86

File: src/mimswap/periphery/Router.sol
286                 token = IMagicLP(lp)._QUOTE_TOKEN_();

70              (shares, , ) = IMagicLP(clone).buyShares(to);

295             } else if (IMagicLP(lp)._QUOTE_TOKEN_() == address(weth)) {

328                 IMagicLP(firstLp)._BASE_TOKEN_().safeTransferFrom(msg.sender, address(firstLp), amountIn);

208             } else if (IMagicLP(lp)._QUOTE_TOKEN_() == address(weth)) {

66              clone = IFactory(factory).create(baseToken, quoteToken, lpFeeRate, i, k);

411            IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);

204                 token = IMagicLP(lp)._QUOTE_TOKEN_();

186             IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, baseInAmount);

236             address token = IMagicLP(lp)._BASE_TOKEN_();

172             IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, baseAdjustedInAmount);

117             (uint256 baseReserve, uint256 quoteReserve) = IMagicLP(lp).getReserves();

287                 (ethAmountOut, tokenAmountOut) = IMagicLP(lp).sellShares(

296                 (tokenAmountOut, ethAmountOut) = IMagicLP(lp).sellShares(

351                 inToken = IMagicLP(firstLp)._QUOTE_TOKEN_();

489             IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);

136                 uint256 i = IMagicLP(lp)._I_();

443             IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);

572             amountOut = IMagicLP(lp).sellBase(to);

173             IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, quoteAdjustedInAmount);

439             if (IMagicLP(lp)._QUOTE_TOKEN_() != address(weth)) {

393                 IMagicLP(firstLp)._BASE_TOKEN_().safeTransferFrom(msg.sender, firstLp, amountIn);

467             address quoteToken = IMagicLP(lp)._QUOTE_TOKEN_();

522                 uint256 i = IMagicLP(lp)._I_();

563                 amountOut = IMagicLP(path[iterations]).sellQuote(to);

380                 outToken = IMagicLP(lastLp)._QUOTE_TOKEN_();

421             address baseToken = IMagicLP(lp)._BASE_TOKEN_();

187             IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, quoteInAmount);

485             if (IMagicLP(lp)._BASE_TOKEN_() != address(weth)) {

550            IMagicLP(path[i]).sellQuote(address(path[i + 1]));

252             uint256 baseBalance = IMagicLP(lp)._BASE_TOKEN_().balanceOf(address(lp));

238             token = IMagicLP(lp)._QUOTE_TOKEN_();

500             (shares, , ) = IMagicLP(lp).buyShares(to);

202             address token = IMagicLP(lp)._BASE_TOKEN_();

284             address token = IMagicLP(lp)._BASE_TOKEN_();

330            IMagicLP(firstLp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, address(firstLp), amountIn);

349                 inToken = IMagicLP(firstLp)._BASE_TOKEN_();

239             } else if (IMagicLP(lp)._QUOTE_TOKEN_() != address(weth)) {

514             (uint256 baseReserve, uint256 quoteReserve) = IMagicLP(lp).getReserves();

271             return IMagicLP(lp).sellShares(sharesIn, to, minimumBaseAmount, minimumQuoteAmount, "", deadline);

395                 IMagicLP(firstLp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, firstLp, amountIn);

253             uint256 quoteBalance = IMagicLP(lp)._QUOTE_TOKEN_().balanceOf(address(lp));

547                     IMagicLP(path[i]).sellBase(address(path[i + 1]));

579             amountOut = IMagicLP(lp).sellQuote(to);

382                 outToken = IMagicLP(lastLp)._BASE_TOKEN_();

93              (shares, , ) = IMagicLP(clone).buyShares(to);

561                 amountOut = IMagicLP(path[iterations]).sellBase(to);

456             IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);

88              clone = IFactory(factory).create(useTokenAsQuote ? address(weth) : token, useTokenAsQuote ? token : address(weth), lpFeeRate, i, k);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Router.sol#L88:88

[G‑02] State variables that are used multiple times in a function should be cached in stack variables

When performing multiple operations on a state variable in a function, it is recommended to cache it first. Either multiple reads or multiple writes to a state variable can save gas by caching it on the stack. Caching of a state variable replaces 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. Saves 100 gas per instance.

There are 8 instances of this issue:

Total save gas : 100 * 8 = 800

File:  src/blast/BlastMagicLP.sol
// @audit `_BASE_TOKEN_` is State variable should be cached in stack variables
56      if (registry.nativeYieldTokens(_BASE_TOKEN_)) {
            token0Amount = BlastYields.claimAllTokenYields(_BASE_TOKEN_, feeTo_);
        }

// @audit `_QUOTE_TOKEN_` is State variable should be cached in stack variables
59      if (registry.nativeYieldTokens(_QUOTE_TOKEN_)) {
            token1Amount = BlastYields.claimAllTokenYields(_QUOTE_TOKEN_, feeTo_);
        }            

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L56

File:  src/mimswap/MagicLP.sol
// @audit `_BASE_RESERVE_` and `_QUOTE_RESERVE_` are State variables should be cached in stack variables to save gas
138  function correctRState() external {
        if (_RState_ == uint32(PMMPricing.RState.BELOW_ONE) && _BASE_RESERVE_ < _BASE_TARGET_) {
            _RState_ = uint32(PMMPricing.RState.ONE);
            _BASE_TARGET_ = _BASE_RESERVE_;
            _QUOTE_TARGET_ = _QUOTE_RESERVE_;
        }
        if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_ < _QUOTE_TARGET_) {
            _RState_ = uint32(PMMPricing.RState.ONE);
            _BASE_TARGET_ = _BASE_RESERVE_;
            _QUOTE_TARGET_ = _QUOTE_RESERVE_;
        }
    }

// @audit `_BASE_TOKEN_` is State variable should be cached in stack variables
285     _setReserve(_BASE_TOKEN_.balanceOf(address(this)), quoteBalance);

        emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, to);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L138-L148

File: src/mimswap/MagicLP.sol
// @audit `_QUOTE_RESERVE_` and `_BASE_RESERVE_` are State variables should be cached in stack variables to save gas
290 function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external nonReentrant {
        _transferBaseOut(assetTo, baseAmount);
        _transferQuoteOut(assetTo, quoteAmount);

        if (data.length > 0) {
            ICallee(assetTo).FlashLoanCall(msg.sender, baseAmount, quoteAmount, data);
        }

        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));

        // no input -> pure loss
        if (baseBalance < _BASE_RESERVE_ && quoteBalance < _QUOTE_RESERVE_) {
            revert ErrFlashLoanFailed();
        }

        // sell quote case
        // quote input + base output
        if (baseBalance < _BASE_RESERVE_) {
            uint256 quoteInput = quoteBalance - uint256(_QUOTE_RESERVE_);
            (uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget) = querySellQuote(
                tx.origin,
                quoteInput
            );

            if (uint256(_BASE_RESERVE_) - baseBalance > receiveBaseAmount) {
                revert ErrFlashLoanFailed();
            }

            _transferBaseOut(_MT_FEE_RATE_MODEL_.maintainer(), mtFee);
            if (_RState_ != uint32(newRState)) {
                _QUOTE_TARGET_ = newQuoteTarget.toUint112();
                _RState_ = uint32(newRState);
                emit RChange(newRState);
            }
            emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo);
        }

        // sell base case
        // base input + quote output
        if (quoteBalance < _QUOTE_RESERVE_) {
            uint256 baseInput = baseBalance - uint256(_BASE_RESERVE_);
            (uint256 receiveQuoteAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newBaseTarget) = querySellBase(
                tx.origin,
                baseInput
            );

            if (uint256(_QUOTE_RESERVE_) - quoteBalance > receiveQuoteAmount) {
                revert ErrFlashLoanFailed();
            }

            _transferQuoteOut(_MT_FEE_RATE_MODEL_.maintainer(), mtFee);
            if (_RState_ != uint32(newRState)) {
                _BASE_TARGET_ = newBaseTarget.toUint112();
                _RState_ = uint32(newRState);
                emit RChange(newRState);
            }
            emit Swap(address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, assetTo);
        }

        _sync();

        emit FlashLoan(msg.sender, assetTo, baseAmount, quoteAmount);
    }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L290-L353

File:  src/mimswap/periphery/Factory.sol
// @audit `maintainerFeeRateModel` is State variable should be cached in stack variables
86     IMagicLP(clone).init(address(baseToken_), address(quoteToken_), lpFeeRate_, address(maintainerFeeRateModel), i_, k_);

        emit LogCreated(clone, baseToken_, quoteToken_, creator, lpFeeRate_, maintainerFeeRateModel, i_, k_);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L86-L88

[G-03] 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.

There are 3 instances of this issue:

File: src/blast/BlastOnboarding.sol
37          mapping(address token => Balances) public totals;
38          mapping(address token => uint256 cap) public caps;
39      
40          // Per-user
41          mapping(address user => mapping(address token => Balances)) public balances;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L37-L41

File: src/mimswap/periphery/Factory.sol
35          mapping(address base => mapping(address quote => address[] pools)) public pools;
36          mapping(address creator => address[] pools) public userPools;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L35-L36

File: src/staking/LockingMultiRewards.sol
89          mapping(address token => Reward info) private _rewardData;
90          mapping(address user => Balances balances) private _balances;
91          mapping(address user => LockedBalance[] locks) private _userLocks;
92          mapping(address user => RewardLock rewardLock) private _userRewardLock;
93      
94          mapping(address user => mapping(address token => uint256 amount)) public userRewardPerTokenPaid;
95          mapping(address user => mapping(address token => uint256 amount)) public rewards;
96          mapping(address user => uint256 index) public lastLockIndex;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L89-L96

[G-04] Use Assembly To Check For address(0)

Saves 6 gas per instance if using assembly to check for address(0)

There are 26 instances of this issue: Total save gas : 6 * 26 = 156

File: src/blast/BlastBox.sol
25              if (feeTo_ == address(0)) {

28              if (address(registry_) == address(0)) {

73              if (feeTo_ == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastBox.sol#L25

File: src/blast/BlastGovernor.sol
16              if (feeTo_ == address(0)) {

41              if(_feeTo == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L16

File: src/blast/BlastMagicLP.sol
24              if (feeTo_ == address(0)) {

27              if (address(registry_) == address(0)) {

81              if (feeTo_ == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L24

File: src/blast/BlastOnboarding.sol
85              if (address(registry_) == address(0)) {

89              if (feeTo_ == address(0)) {

148             if (feeTo_ == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L85

File: src/blast/BlastOnboardingBoot.sol
97              if (pool != address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L97

File: src/blast/BlastTokenRegistry.sol
15              if (token == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastTokenRegistry.sol#L15:15

File: src/blast/BlastWrappers.sol
21              if (governor_ == address(0)) {

35              if (governor_ == address(0)) {

48              if (governor_ == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastWrappers.sol#L48:48

File: src/mimswap/MagicLP.sol
102             if (mtFeeRateModel == address(0) || baseTokenAddress == address(0) || quoteTokenAddress == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L102:102

File: src/mimswap/auxiliary/FeeRateModel.sol
23              if (maintainer_ == address(0)) {

35              if (implementation == address(0)) {

47              if (maintainer_ == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/auxiliary/FeeRateModel.sol#L23

File: src/mimswap/periphery/Factory.sol
39              if (implementation_ == address(0)) {

42              if (address(maintainerFeeRateModel_) == address(0)) {

97              if (implementation_ == address(0)) {

106             if (address(maintainerFeeRateModel_) == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L106:106

File: src/mimswap/periphery/Router.sol
39              if (address(weth_) == address(0) || address(factory_) == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L39:39

File: src/staking/LockingMultiRewards.sol
301             if (rewardToken == address(0)) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L301:301

[G-05] Use hardcode address instead address(this)

it can be more gas-efficient to use a hardcoded address instead of the address(this) expression, especially if you need to use the same address multiple times in your contract.

There are 41 instances of this issue:

File:  src/blast/BlastOnboardingBoot.sol
106  (pool, totalPoolShares) = router.createPool(MIM, USDB, FEE_RATE, I, K, address(this), baseAmount, quoteAmount);

117        staking = new LockingMultiRewards(pool, 30_000, 7 days, 13 weeks, address(this));
        staking.setOperator(address(this), true);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L106

File:  src/blast/libraries/BlastYields.sol
21  if (IERC20Rebasing(token).getConfiguration(address(this)) == YieldMode.CLAIMABLE) {

26  emit LogBlastTokenClaimableEnabled(address(this), token);

31  emit LogBlastNativeClaimableEnabled(address(this));

39  return claimMaxGasYields(address(this), recipient);

52  return claimAllNativeYields(address(this), recipient);

71  amount = IERC20Rebasing(token).claim(recipient, IERC20Rebasing(token).getClaimableAmount(address(this)));

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/libraries/BlastYields.sol#L21

File:  src/mimswap/MagicLP.sol
229  return _BASE_TOKEN_.balanceOf(address(this)) - uint256(_BASE_RESERVE_);

233  return _QUOTE_TOKEN_.balanceOf(address(this)) - uint256(_QUOTE_RESERVE_);

245  uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));

262  _setReserve(baseBalance, _QUOTE_TOKEN_.balanceOf(address(this)));

268  uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));

285  _setReserve(_BASE_TOKEN_.balanceOf(address(this)), quoteBalance);

298      uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));

361     uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); 

427   if (to == address(this)) {

431      uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));  

505     uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); 


530     baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));   

568     uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); 

615    if (address(this) == address(implementation)) {

622  if (address(this) != address(implementation)) {                                            

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L229

File:  src/mimswap/periphery/Router.sol
92  address(weth).safeTransferFrom(address(this), clone, msg.value);

269  lp.safeTransferFrom(msg.sender, address(this), sharesIn);

283  lp.safeTransferFrom(msg.sender, address(this), sharesIn);

289   address(this),

298  address(this),

398  amountOut = _swap(address(this), path, directions, minimumOut);

444  amountOut = _sellBase(lp, address(this), minimumOut);

490   amountOut = _sellQuote(lp, address(this), minimumOut);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L92

File:  src/staking/LockingMultiRewards.sol
326  if (tokenAddress == stakingToken && tokenAmount > stakingToken.balanceOf(address(this)) - stakingTokenBalance) {

367  rewardToken.safeTransferFrom(msg.sender, address(this), amount);

464   stakingToken.safeTransferFrom(msg.sender, address(this), amount);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L326

[G-06] Constructors can be marked payable

Payable functions cost less gas to execute, since the compiler does not have to add extra checks to ensure that a payment wasn't provided. A constructor can safely be marked as payable, since only the deployer would be able to pass funds, and the project itself would not pass any funds.

There are 16 instances of this issue:

File: src/blast/BlastBox.sol
24          constructor(IERC20 weth_, BlastTokenRegistry registry_, address feeTo_) DegenBox(weth_) {
25              if (feeTo_ == address(0)) {
26                  revert ErrZeroAddress();
27              }
28              if (address(registry_) == address(0)) {
29                  revert ErrZeroAddress();
30              }
31      
32              registry = registry_;
33              feeTo = feeTo_;
34          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastBox.sol#L24:34

File: src/blast/BlastDapp.sol
7           constructor() {
8               BlastPoints.configure();
9           }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastDapp.sol#L7:9

File: src/blast/BlastGovernor.sol
15          constructor(address feeTo_, address _owner) OperatableV2(_owner) {
16              if (feeTo_ == address(0)) {
17                  revert ErrZeroAddress();
18              }
19      
20              feeTo = feeTo_;
21              BlastYields.configureDefaultClaimables(address(this));
22          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastGovernor.sol#L15:22

File: src/blast/BlastMagicLP.sol
23          constructor(BlastTokenRegistry registry_, address feeTo_, address owner_) MagicLP(owner_) {
24              if (feeTo_ == address(0)) {
25                  revert ErrZeroAddress();
26              }
27              if (address(registry_) == address(0)) {
28                  revert ErrZeroAddress();
29              }
30      
31              registry = registry_;
32              feeTo = feeTo_;
33          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastMagicLP.sol#L23:33

File: src/blast/BlastOnboarding.sol
58          constructor() Owned(msg.sender) {
59              BlastYields.configureDefaultClaimables(address(this));
60              BlastPoints.configure();
61          }


84          constructor(BlastTokenRegistry registry_, address feeTo_) {
85              if (address(registry_) == address(0)) {
86                  revert ErrZeroAddress();
87              }
88      
89              if (feeTo_ == address(0)) {
90                  revert ErrZeroAddress();
91              }
92      
93              registry = registry_;
94              feeTo = feeTo_;
95          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboarding.sol#L84:95

File: src/blast/BlastTokenRegistry.sol
12          constructor(address _owner) Owned(_owner) {}

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastTokenRegistry.sol#L12:12

File: src/blast/BlastWrappers.sol
20          constructor(IWETH weth_, IFactory factory, address governor_) Router(weth_, factory) {
21              if (governor_ == address(0)) {
22                  revert ErrZeroAddress();
23              }
24              BlastYields.configureDefaultClaimables(governor_);
25          }


29          constructor(
30              address implementation_,
31              IFeeRateModel maintainerFeeRateModel_,
32              address owner_,
33              address governor_
34          ) Factory(implementation_, maintainerFeeRateModel_, owner_) {
35              if (governor_ == address(0)) {
36                  revert ErrZeroAddress();
37              }
38              BlastYields.configureDefaultClaimables(governor_);
39          }


47          constructor(address box_, address mim_, address governor_) CauldronV4(IBentoBoxV1(box_), IERC20(mim_)) {
48              if (governor_ == address(0)) {
49                  revert ErrZeroAddress();
50              }
51              if (governor_ == address(this)) {
52                  revert ErrInvalidGovernorAddress();
53              }
54      
55              _governor = governor_;
56              _setupBlacklist();
57          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastWrappers.sol#L47:57

File: src/mimswap/MagicLP.sol
84          constructor(address owner_) Owned(owner_) {
85              implementation = this;
86      
87              // prevents the implementation contract initialization
88              _INITIALIZED_ = true;
89          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/MagicLP.sol#L84:89

File: src/mimswap/auxiliary/FeeRateModel.sol

22          constructor(address maintainer_, address owner_) Owned(owner_) {
23              if (maintainer_ == address(0)) {
24                  revert ErrZeroAddress();
25              }
26      
27              maintainer = maintainer_;
28          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/auxiliary/FeeRateModel.sol#L22:28

File: src/mimswap/periphery/Factory.sol
38          constructor(address implementation_, IFeeRateModel maintainerFeeRateModel_, address owner_) Owned(owner_) {
39              if (implementation_ == address(0)) {
40                  revert ErrZeroAddress();
41              }
42              if (address(maintainerFeeRateModel_) == address(0)) {
43                  revert ErrZeroAddress();
44              }
45              implementation = implementation_;
46              maintainerFeeRateModel = maintainerFeeRateModel_;
47          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Factory.sol#L38:47

File: src/mimswap/periphery/Router.sol
38          constructor(IWETH weth_, IFactory factory_) {
39              if (address(weth_) == address(0) || address(factory_) == address(0)) {
40                  revert ErrZeroAddress();
41              }
42      
43              weth = weth_;
44              factory = factory_;
45          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Router.sol#L38:45

File: src/oracles/aggregators/MagicLpAggregator.sol
21          constructor(IMagicLP pair_, IAggregator baseOracle_, IAggregator quoteOracle_) {
22              pair = pair_;
23              baseOracle = baseOracle_;
24              quoteOracle = quoteOracle_;
25              baseDecimals = IERC20Metadata(pair_._BASE_TOKEN_()).decimals();
26              quoteDecimals = IERC20Metadata(pair_._QUOTE_TOKEN_()).decimals();
27          }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/oracles/aggregators/MagicLpAggregator.sol#L21:27

File: src/staking/LockingMultiRewards.sol
112         constructor(
113             address _stakingToken,
114             uint256 _lockingBoostMultiplerInBips,
115             uint256 _rewardsDuration,
116             uint256 _lockDuration,
117             address _owner
118         ) OperatableV2(_owner) {
119             if (_lockingBoostMultiplerInBips <= BIPS) {
120                 revert ErrInvalidBoostMultiplier();
121             }
122     
123             if (_lockDuration < MIN_LOCK_DURATION) {
124                 revert ErrInvalidLockDuration();
125             }
126     
127             if (_rewardsDuration < MIN_REWARDS_DURATION) {
128                 revert ErrInvalidRewardDuration();
129             }
130     
131             if (_lockDuration % _rewardsDuration != 0) {
132                 revert ErrInvalidDurationRatio();
133             }
134     
135             stakingToken = _stakingToken;
136             lockingBoostMultiplerInBips = _lockingBoostMultiplerInBips;
137             rewardsDuration = _rewardsDuration;
138             lockDuration = _lockDuration;
139     
140             // kocks are combined into the same `rewardsDuration` epoch. So, if
141             // a user stake with locking every `rewardsDuration` this should reach the
142             // maximum number of possible simultaneous because the first lock gets expired,
143             // freeing up a slot.
144             maxLocks = _lockDuration / _rewardsDuration;
145         }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/staking/LockingMultiRewards.sol#L112:145

[G-07] Use assembly to write address storage values

By using assembly to write to address storage values, you can bypass some of these operations and lower the gas cost of writing to storage. Assembly code allows you to directly access the Ethereum Virtual Machine (EVM) and perform low-level operations that are not possible in Solidity.

example of using assembly to write to address storage values:

contract MyContract {
    address private myAddress;
    
    function setAddressUsingAssembly(address newAddress) public {
        assembly {
            sstore(0, newAddress)
        }
    }
}

There are 14 instances of this issue:

File:  src/blast/BlastBox.sol
33  feeTo = feeTo_;

77  feeTo = feeTo_;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastBox.sol#L33

File:  src/blast/BlastGovernor.sol
20  feeTo = feeTo_;

45  feeTo = _feeTo;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L45

File:  src/blast/BlastMagicLP.sol
32   feeTo = feeTo_;

85   feeTo = feeTo_;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L32

File:  src/blast/BlastOnboarding.sol
191   bootstrapper = bootstrapper_;

94    feeTo = feeTo_;

152   feeTo = feeTo_;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L191

File:  src/mimswap/MagicLP.sol
119  _BASE_TOKEN_ = baseTokenAddress;

120  _QUOTE_TOKEN_ = quoteTokenAddress;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L119

File:  src/mimswap/auxiliary/FeeRateModel.sol
27  maintainer = maintainer_;

51  maintainer = maintainer_;

58  implementation = implementation_;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/auxiliary/FeeRateModel.sol#L27

[G-08] Avoid updating storage when the value hasn't changed

If the old value is equal to the new value, not re-storing the value will avoid a Gsreset (2900 gas), potentially at the expense of a Gcoldsload (2100 gas) or a Gwarmaccess (100 gas).

There are 18 instances of this issue:

File: src/blast/BlastBox.sol
72          function setFeeTo(address feeTo_) external onlyOwner {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastBox.sol#L72:72

File: src/blast/BlastGovernor.sol
40          function setFeeTo(address _feeTo) external onlyOwner {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastGovernor.sol#L40:40

File: src/blast/BlastMagicLP.sol
80          function setFeeTo(address feeTo_) external onlyImplementation onlyImplementationOwner {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastMagicLP.sol#L80:80

File: src/blast/BlastOnboarding.sol
147         function setFeeTo(address feeTo_) external onlyOwner {

195         function open() external onlyOwner onlyState(State.Idle) {

190         function setBootstrapper(address bootstrapper_) external onlyOwner {

200         function close() external onlyOwner onlyState(State.Opened) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboarding.sol#L200:200

File: src/blast/BlastOnboardingBoot.sol
96          function bootstrap(uint256 minAmountOut) external onlyOwner onlyState(State.Closed) returns (address, address, uint256) {

146         function setReady(bool _ready) external onlyOwner onlyState(State.Closed) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboardingBoot.sol#L146:146

File: src/mimswap/auxiliary/FeeRateModel.sol
57          function setImplementation(address implementation_) public onlyOwner {

46          function setMaintainer(address maintainer_) external onlyOwner {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/auxiliary/FeeRateModel.sol#L46:46

File: src/mimswap/periphery/Factory.sol
96          function setLpImplementation(address implementation_) external onlyOwner {

105         function setMaintainerFeeRateModel(IFeeRateModel maintainerFeeRateModel_) external onlyOwner {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Factory.sol#L105:105

File: src/staking/LockingMultiRewards.sol
317         function setMinLockAmount(uint256 _minLockAmount) external onlyOwner {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/staking/LockingMultiRewards.sol#L317:317

File: test/mocks/ERC20Mock.sol
19          function setDecimals(uint8 decimals_) external {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mocks/ERC20Mock.sol#L19:19

File: utils/BaseScript.sol
33          function setNewDeploymentEnabled(bool _enabled) public {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/BaseScript.sol#L33:33

File: utils/BaseTest.sol
28          function setUpNoMocks() public virtual {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/BaseTest.sol#L28:28

File: utils/Toolkit.sol
273         function setTesting(bool _testing) public {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/Toolkit.sol#L273:273

[G-09] Use calldata instead of memory for function arguments that do not get mutated

Mark data types as calldata instead of memory where possible. This makes it so that Mark data types as calldata instead of memory where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in as calldata. The one exception to this is if the argument must later be passed into another function that takes an argument that specifies memory storage..

There are 2 instances of this issue:

File:  src/blast/BlastOnboarding.sol
164  function claimTokenYields(address[] memory tokens) external onlyOwner {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L164

File:  src/staking/LockingMultiRewards.sol
397  function processExpiredLocks(address[] memory users, uint256[] calldata lockIndexes) external onlyOperators {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L397

[G-10] Can Make The Variable Outside The Loop To Save Gas

When you declare a variable inside a loop, Solidity creates a new instance of the variable for each iteration of the loop. This can lead to unnecessary gas costs, especially if the loop is executed frequently or iterates over a large number of elements.

By declaring the variable outside the loop, you can avoid the creation of multiple instances of the variable and reduce the gas cost of your contract. Here's an example:

contract MyContract { function sum(uint256[] memory values) public pure returns (uint256) { uint256 total = 0; for (uint256 i = 0; i < values.length; i++) { total += values[i]; } return total; } }

There are 9 instances of this issue:

File:  src/staking/LockingMultiRewards.sol
406  address user = users[i];

414  uint256 index = lockIndexes[i];

426  uint256 amount = locks[index].amount;
427  uint256 lastIndex = locks.length - 1;

562  address token = rewardTokens[i];

576  address token = rewardTokens[i];

577  uint256 rewardPerToken_ = _updateRewardsGlobal(token, totalSupply_);

615  address rewardToken = rewardTokens[i];
616  uint256 rewardAmount = rewards[user][rewardToken];

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L406

[G-11] Use nested if statements instead of &&

If the if statement has a logical AND and is not followed by an else statement, it can be replaced with 2 if statements.

contract NestedIfTest { //Execution cost: 22334 gas function funcBad(uint256 input) public pure returns (string memory) { if (input<10 && input>0 && input!=6){ return "If condition passed"; } } //Execution cost: 22294 gas function funcGood(uint256 input) public pure returns (string memory) { if (input<10) { if (input>0){ if (input!=6){ return "If condition passed"; } } } } }

There are 8 instances of this issue:

File:  src/blast/BlastMagicLP.sol
119  if (!BlastMagicLP(address(implementation)).operators(msg.sender) && msg.sender != implementation.owner()) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L119

File:  src/blast/BlastOnboarding.sol
114  if (caps[token] > 0 && totals[token].total > caps[token]) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L114

File:  src/mimswap/MagicLP.sol
139  if (_RState_ == uint32(PMMPricing.RState.BELOW_ONE) && _BASE_RESERVE_ < _BASE_TARGET_) {

144  if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_ < _QUOTE_TARGET_) {

302  if (baseBalance < _BASE_RESERVE_ && quoteBalance < _QUOTE_RESERVE_) {

549  if (timeElapsed > 0 && _BASE_RESERVE_ != 0 && _QUOTE_RESERVE_ != 0) {            

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L139

File:  src/mimswap/periphery/Router.sol
527  if (quoteReserve > 0 && baseReserve > 0) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L527

File:  src/staking/LockingMultiRewards.sol
326  if (tokenAddress == stakingToken && tokenAmount > stakingToken.balanceOf(address(this)) - stakingTokenBalance) {

417  if (index == lastLockIndex[user] && locks.length > 1) {    

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L326

[G-12] Sort Solidity operations using short-circuit mode

Short-circuiting is a solidity contract development model that uses OR/AND logic to sequence different cost operations. It puts low gas cost operations in the front and high gas cost operations in the back, so that if the front is low If the cost operation is feasible, you can skip (short-circuit) the subsequent high-cost Ethereum virtual machine operation.

//f(x) is a low gas cost operation //g(y) is a high gas cost operation //Sort operations with different gas costs as follows f(x) || g(y) f(x) && g(y)

There are 1 instances of this issue:

  • BlastMagicLP is external call
File:  src/blast/BlastMagicLP.sol
119  if (!BlastMagicLP(address(implementation)).operators(msg.sender) && msg.sender != implementation.owner()) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L119

[G-13] Amounts should be checked for 0 before calling a transfer

Checking non-zero transfer values can avoid an expensive external call and save gas.

There are 5 instances of this issue:

File:  src/blast/BlastOnboarding.sol
138  token.safeTransfer(msg.sender, amount);

210  token.safeTransfer(to, amount);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L138

File:  src/staking/LockingMultiRewards.sol
217  address(weth).safeTransfer(lp, wethAdjustedAmount);

311   token.safeTransfer(to, tokenAmountOut);

359     weth.deposit{value: msg.value}();
        inToken.safeTransfer(address(firstLp), msg.value);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L217

[G-14] Do not calculate constants

Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas Reffrence

There are 4 instances of this issue:

File:  src/mimswap/MagicLP.sol
63    uint256 public constant MAX_I = 10 ** 36;
64    uint256 public constant MAX_K = 10 ** 18;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L63-L64

File:  src/mimswap/libraries/DecimalMath.sol
20    uint256 internal constant ONE = 10 ** 18;
21    uint256 internal constant ONE2 = 10 ** 36;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/DecimalMath.sol#L20-L21

[G-15] With assembly, .call (bool success) transfer can be done gas-optimized

return data (bool success,) has to be stored due to EVM architecture, but in a usage like below, ‘out’ and ‘outsize’ values are given (0,0), this storage disappears and gas optimization is provided. https://code4rena.com/reports/2023-01-biconomy#g-01-with-assembly-call-bool-success-transfer-can-be-done-gas-optimized

There are 1 instances of this issue:

File:  src/blast/BlastGovernor.sol
54  (success, result) = to.call{value: value}(data);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L54

[G-16] Internal functions only called once can be inlined to save gas

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

There are 3 instances of this issue:

File: src/mimswap/MagicLP.sol
528         function _resetTargetAndReserve() internal returns (uint256 baseBalance, uint256 quoteBalance) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L528:528

File: src/staking/LockingMultiRewards.sol
544         function _updateRewards() internal {

572         function _updateRewardsForUsers(address[] memory users) internal {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L572:572

[G-17] Use constants instead of type(uintx).max

Using constants instead of type(uintx).max can save gas in some cases because constants are precomputed at compile-time and can be reused throughout the code, whereas type(uintx).max requires computation at runtime

There are 6 instances of this issue:

File: src/blast/BlastOnboardingBoot.sol
103             MIM.safeApprove(address(router), type(uint256).max);

104             USDB.safeApprove(address(router), type(uint256).max);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L103

File:  src/mimswap/MagicLP.sol
508  if (baseBalance > type(uint112).max || quoteBalance > type(uint112).max) {

532  if (baseBalance > type(uint112).max || quoteBalance > type(uint112).max) {    

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L508

[G-18] Multiple accesses of a mapping/array should use a local variable cache

The instances below point to the second+ access of a value inside a mapping/array, within a function. Caching a mapping's value in a local storage or calldata variable when the value is accessed multiple times, saves ~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. Caching an array's struct avoids recalculating the array offsets into memory/calldata

There are 5 instances of this issue:

File: src/blast/BlastOnboarding.sol
// @audit `caps[token]` is mapping       
114     if (caps[token] > 0 && totals[token].total > caps[token]) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboarding.sol#L170:170

    function notifyRewardAmount(address rewardToken, uint256 amount, uint minRemainingTime) public onlyOperators {
        if (!_rewardData[rewardToken].exists) {
            revert ErrInvalidTokenAddress();
        }

        _updateRewards();
        rewardToken.safeTransferFrom(msg.sender, address(this), amount);

        Reward storage reward = _rewardData[rewardToken];

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L361-L369

Fix code

    function notifyRewardAmount(address rewardToken, uint256 amount, uint minRemainingTime) public onlyOperators {
+       Reward storage reward = _rewardData[rewardToken];
        if (!reward.exists) {
            revert ErrInvalidTokenAddress();
        }

        _updateRewards();
        rewardToken.safeTransferFrom(msg.sender, address(this), amount);

-       Reward storage reward = _rewardData[rewardToken];
File: src/staking/LockingMultiRewards.sol
// @audit `_rewardData[rewardToken].rewardPerTokenStored` is mapping also use in 285 
279  return _rewardData[rewardToken].rewardPerTokenStored;

// @audit `lastLockIndex[user]` is mapping also use in 430 
417  if (index == lastLockIndex[user] && locks.length > 1) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L279

[G-19] Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement

require(a <= b); x = b - a => require(a <= b); unchecked { x = b - a }

There are 4 instances of this issue:

File: src/mimswap/libraries/Math.sol
56              uint256 fairAmount = i * (V1 - V2);

178                 bAbs = part2 - bAbs;

191                 numerator = squareRoot - bAbs;

203                 return V1 - V2;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/Math.sol#L56

[G-20] The result of function calls should be cached rather than re-calling the function

Caching the result of a function call in a local variable when the function is called multiple times can save gas due to avoiding the need to execute the function code multiple times. There are 2 instances of this issue:

File: src/mimswap/MagicLP.sol
// @audit `_MT_FEE_RATE_MODEL_.maintainer()` function call also use in line 319
341                 _transferQuoteOut(_MT_FEE_RATE_MODEL_.maintainer(), mtFee);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L341:341

File: src/mimswap/libraries/Math.sol
// @audit `DecimalMath.mulFloor(i, delta)`
141                 return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/Math.sol#L141:141

[G-21] Stack variable used as a cheaper cache for a state variable is only used once

If the variable is only accessed once, it's cheaper to use the state variable directly that one time, and save the 3 gas the extra stack assignment would spend.

There are 2 instances of this issue:

File: src/mimswap/MagicLP.sol
// @audit `baseBalance` and `quoteBalance` only used once
435             baseAmount = (baseBalance * shareAmount) / totalShares;

436             quoteAmount = (quoteBalance * shareAmount) / totalShares;

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/MagicLP.sol#L436:436

[G-22] Use local variables for emitting

Use the function/modifier's local copy of the state variable, rather than incurring an extra Gwarmaccess (100 gas). In the unlikely event that the state variable hasn't already been used by the function/modifier, consider whether it is really necessary to include it in the event, given the fact that it incurs a Gcoldsload (2100 gas), or whether it can be passed in to or back out of the functions that do use it

There are 4 instances of this issue:

File: src/blast/BlastOnboardingBoot.sol
124             emit LogLiquidityBootstrapped(pool, address(staking), totalPoolShares);

148             emit LogReadyChanged(ready);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboardingBoot.sol#L148:148

File: src/mimswap/periphery/Factory.sol
88              emit LogCreated(clone, baseToken_, quoteToken_, creator, lpFeeRate_, maintainerFeeRateModel, i_, k_);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Factory.sol#L88:88

File: src/staking/LockingMultiRewards.sol
318             emit LogSetMinLockAmount(minLockAmount, _minLockAmount);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/staking/LockingMultiRewards.sol#L318:318

[G-23] State variables can be packed into fewer storage slots

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

There are 2 instances of this issue:

File: src/blast/BlastOnboardingBoot.sol
23      contract BlastOnboardingBootDataV1 is BlastOnboardingData {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboardingBoot.sol#L23:23

File: src/mimswap/MagicLP.sol
25      contract MagicLP is ERC20, ReentrancyGuard, Owned {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/MagicLP.sol#L25:25

[G-24] Storage re-read via storage pointer

The instances below point to the second+ access of a state variable, via a storage pointer, within a function. Caching the value replaces each Gwarmaccess (100 gas) with a much cheaper stack read.

There are 4 instances of this issue:

File: src/staking/LockingMultiRewards.sol
241             return bal.unlocked + ((bal.locked * lockingBoostMultiplerInBips) / BIPS);

380              amount += _remainingRewardTime * reward.rewardRate;

427              uint256 lastIndex = locks.length - 1;

605              uint256 rewardItemLength = _rewardLock.items.length;

https://github.com/r109n/2024-03-abracadabra-money/tree/main/staking/LockingMultiRewards.sol#L605:605

[G-25] State variable read in a loop

The state variable should be cached in and read from a local variable, or accumulated in a local variable then written to storage once outside of the loop, rather than reading/updating it on every iteration of the loop, which will replace each Gwarmaccess (100 gas) with a much cheaper stack read.

There are 4 instances of this issue:

File: src/staking/LockingMultiRewards.sol
547             for (uint256 i; i < rewardTokens.length; ) {

561             for (uint256 i; i < rewardTokens.length; ) {

575             for (uint256 i; i < rewardTokens.length; ) {

614             for (uint256 i; i < rewardTokens.length; ) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/staking/LockingMultiRewards.

[G-26] Initializers can be marked payable

Payable functions cost less gas to execute, since the compiler does not have to add extra checks to ensure that a payment wasn't provided. An initializer can safely be marked as payable, since only the deployer would be able to pass funds, and the project itself would not pass any funds.

There are 1 instances of this issue:

File: src/blast/BlastOnboardingBoot.sol
129         function initialize(Router _router) external onlyOwner {
130             router = Router(payable(_router));
131             factory = IFactory(router.factory());
132             emit LogInitialized(_router);
133         }

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboardingBoot.sol#L129:133sol#L614:614

[G-27] Use assembly to emit events

Using the scratch space for event arguments (two words or fewer) will save gas over needing Solidity's full abi memory expansion used for emitting normally.

There are 57 instances of this issue:

File: src/blast/BlastBox.sol
78              emit LogFeeToChanged(feeTo_);

90              emit LogTokenDepositEnabled(token, enabled);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastBox.sol#L90:90

File: src/blast/BlastGovernor.sol
46              emit LogFeeToChanged(_feeTo);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastGovernor.sol#L46:46

File: src/blast/BlastMagicLP.sol
86              emit LogFeeToChanged(feeTo_);

91              emit LogOperatorChanged(operator, status);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastMagicLP.sol#L91:91

File: src/blast/BlastOnboarding.sol
120             emit LogDeposit(msg.sender, token, amount, lock_);

129             emit LogLock(msg.sender, token, amount);

140             emit LogWithdraw(msg.sender, token, amount);

153             emit LogFeeToChanged(feeTo_);

182             emit LogTokenSupported(token, supported);

187             emit LogTokenCapChanged(token, cap);

192             emit LogBootstrapperChanged(bootstrapper_);

197             emit LogStateChange(State.Opened);

202             emit LogStateChange(State.Closed);

211             emit LogTokenRescue(token, to, amount);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboarding.sol#L211:211

File: src/blast/BlastOnboardingBoot.sol
71              emit LogClaimed(msg.sender, shares, lock);

124             emit LogLiquidityBootstrapped(pool, address(staking), totalPoolShares);

132             emit LogInitialized(_router);

143             emit LogStakingChanged(address(_staking));

148             emit LogReadyChanged(ready);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboardingBoot.sol#L148:148

File: src/blast/BlastTokenRegistry.sol
20              emit LogNativeYieldTokenEnabled(token, enabled);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastTokenRegistry.sol#L20:20

File: src/blast/libraries/BlastYields.sol
26              emit LogBlastTokenClaimableEnabled(address(this), token);

31              emit LogBlastNativeClaimableEnabled(address(this));

44              emit LogBlastGasClaimed(recipient, amount);

57              emit LogBlastETHClaimed(recipient, amount);

62              emit LogBlastETHClaimed(recipient, amount);

72              emit LogBlastTokenClaimed(recipient, address(token), amount);

77              emit LogBlastTokenClaimed(recipient, address(token), amount);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/libraries/BlastYields.sol#L77:77

File: src/mimswap/MagicLP.sol
259                 emit RChange(newRState);

264             emit Swap(address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, to);

282                 emit RChange(newRState);

287             emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, to);

323                     emit RChange(newRState);

325                 emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo);

345                     emit RChange(newRState);

347                 emit Swap(address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, assetTo);

352             emit FlashLoan(msg.sender, assetTo, baseAmount, quoteAmount);

409             emit BuyShares(to, shares, balanceOf(to));

454             emit SellShares(msg.sender, to, shareAmount, balanceOf(msg.sender));

467             emit TokenRescue(token, to, amount);

501             emit ParametersChanged(newLpFeeRate, newI, newK, newBaseBalance, newQuoteBalance);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/MagicLP.sol#L501:501

File: src/mimswap/periphery/Factory.sol
88              emit LogCreated(clone, baseToken_, quoteToken_, creator, lpFeeRate_, maintainerFeeRateModel, i_, k_);

102             emit LogSetImplementation(implementation_);

111             emit LogSetMaintainerFeeRateModel(maintainerFeeRateModel_);

141             emit LogPoolRemoved(pool);

152             emit LogPoolAdded(baseToken, quoteToken, creator, pool);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Factory.sol#L152:152

File: src/staking/LockingMultiRewards.sol
183             emit LogWithdrawn(msg.sender, amount);

318             emit LogSetMinLockAmount(minLockAmount, _minLockAmount);

331             emit LogRecovered(tokenAddress, tokenAmount);

392             emit LogRewardAdded(amount);

436             emit LogLockIndexChanged(user, lastIndex, index);

447                 emit LogUnlocked(user, amount, index);

475                 emit LogStaked(account, amount);

513             emit LogLocked(user, amount, _nextUnlockTime, lockCount);

611                 emit LogRewardLockCreated(user, _rewardLock.unlockTime);

638                             emit LogRewardPaid(user, rewardToken, amount);

651                 emit LogRewardLocked(user, rewardToken, rewardAmount);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/staking/LockingMultiRewards.sol#L651:651

[G-28] Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.htmlEach operation involving a uint8 costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8) as compared to ones involving uint256, due to the compiler having to clear the higher bits of the memory word before operating on the uint8, as well as the associated stack operations of doing so. Use a larger size then downcast where needed.

There are 2 instances of this issue:

File: src/mimswap/MagicLP.sol
546             uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32);

547             uint32 timeElapsed = blockTimestamp - _BLOCK_TIMESTAMP_LAST_;

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/MagicLP.sol#L547:547

[G-29] Emit Used In Loop

Emitting an event inside a loop performs a LOG op N times, where N is the loop length. Consider refactoring the code to emit the event only once at the end of loop. Gas savings should be multiplied by the average loop length.

There are 4 instances of this issue:

File: src/staking/LockingMultiRewards.sol
436    emit LogLockIndexChanged(user, lastIndex, index);

447    emit LogUnlocked(user, amount, index);

638    emit LogRewardPaid(user, rewardToken, amount);

651    emit LogRewardLocked(user, rewardToken, rewardAmount);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/staking/LockingMultiRewards.sol#L651:651

[G-30] Use assembly to calculate hashes to save gas

Using assembly to calculate hashes can save 80 gas per instance

There are 1 instances of this issue:

File: src/mimswap/periphery/Factory.sol
163             return keccak256(abi.encodePacked(sender_, implementation, baseToken_, quoteToken_, lpFeeRate_, i_, k_));

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Factory.sol#L163:163

[G-31] Unused named return variables without optimizer waste gas

Consider changing the variable to be an unnamed one, since the variable is never assigned, nor is it returned by name. If the optimizer is not turned on, leaving the code as it is will also waste gas for the stack variable.

There are 18 instances of this issue:

File: src/blast/BlastOnboardingBoot.sol
78      function claimable(address user) external view returns (uint256 shares) {

86      function previewTotalPoolShares() external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {

155     function _claimable(address user) internal view returns (uint256 shares) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboardingBoot.sol#L155:155

File: src/blast/libraries/BlastYields.sol
51          function claimAllNativeYields(address recipient) internal returns (uint256 amount) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/libraries/BlastYields.sol#L51:51

File: src/mimswap/MagicLP.sol
215         function getMidPrice() public view returns (uint256 midPrice) {

224         function getUserFeeRate(address user) external view returns (uint256 lpFeeRate, uint256 mtFeeRate) {

228         function getBaseInput() public view returns (uint256 input) {

232         function getQuoteInput() public view returns (uint256 input) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/MagicLP.sol#L232:232

File: src/mimswap/auxiliary/FeeRateModel.sol
34          function getFeeRate(address trader, uint256 lpFeeRate) external view returns (uint256 adjustedLpFeeRate, uint256 mtFeeRate) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/auxiliary/FeeRateModel.sol#L34:34

File: src/mimswap/periphery/Router.sol
185         ) external ensureDeadline(deadline) returns (uint256 shares) {

235         ) external payable ensureDeadline(deadline) returns (uint256 shares) {

268         ) external returns (uint256 baseAmountOut, uint256 quoteAmountOut) {

321         ) external ensureDeadline(deadline) returns (uint256 amountOut) {

342         ) external payable ensureDeadline(deadline) returns (uint256 amountOut) {

410         ) external ensureDeadline(deadline) returns (uint256 amountOut) {

420         ) external payable ensureDeadline(deadline) returns (uint256 amountOut) {

455         ) external ensureDeadline(deadline) returns (uint256 amountOut) {

466         ) external payable ensureDeadline(deadline) returns (uint256 amountOut) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Router.sol#L466:466

[G-32] Structs can be modified to fit in fewer storage slots

Some member types can be safely modified, and as result, these struct will fit in fewer storage slots. Each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct. Subsequent reads as well as writes have smaller gas savings.

There are 2 instances of this issue:

File:  src/blast/BlastOnboarding.sol
24  struct Balances {
        uint256 unlocked;
        uint256 locked;
        uint256 total;
    }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L24-L28

File:  src/mimswap/libraries/PMMPricing.sol
27  struct PMMState {
        uint256 i;
        uint256 K;
        uint256 B;
        uint256 Q;
        uint256 B0;
        uint256 Q0;
        RState R;
    }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/PMMPricing.sol#L27-L35

[G‑33] Use assembly to validate msg.sender

We can use assembly to efficiently validate msg.sender with the least amount of opcodes necessary.

There are 1 instances of this issue:

File:  src/mimswap/MagicLP.sol
608   if (msg.sender != implementation.owner()) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L608

[G‑34] Unlimited gas consumption risk due to external call recipients

When calling an external function without specifying a gas limit , the called contract may consume all the remaining gas, causing the tx to be reverted. To mitigate this, it is recommended to explicitly set a gas limit when making low level external calls.

There are 1 instances of this issue:

File:  src/blast/BlastGovernor.sol
54  (success, result) = to.call{value: value}(data);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L54

[G‑35] Same cast is done multiple times

It's cheaper to do it once, and store the result to a variable. The examples below are the second+ instance of a given cast of the variable.

There are 5 instances of this issue:

  • address(router)
File:  src/blast/BlastOnboardingBoot.sol
103        MIM.safeApprove(address(router), type(uint256).max);
           USDB.safeApprove(address(router), type(uint256).max);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L103-L104

  • address(staking)
File:  src/blast/BlastOnboardingBoot.sol
        pool.safeApprove(address(staking), totalPoolShares);

        emit LogLiquidityBootstrapped(pool, address(staking), totalPoolShares);

        return (pool, address(staking), totalPoolShares);

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L122-L126

[G‑36] Using constants instead of enum can save gas

Enum is expensive and it is more efficient to use constants instead. An illustrative example of this approach can be found in the ReentrancyGuard.sol.

There are 2 instances of this issue:

File:  src/blast/BlastOnboarding.sol
18  enum State {
        Idle,
        Opened,
        Closed
    }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L18-L22

File:  src/mimswap/libraries/PMMPricing.sol
21  enum RState {
        ONE,
        ABOVE_ONE,
        BELOW_ONE
    }

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/PMMPricing.sol#L21-L25

[G-37] Move storage pointer to top of function to avoid offset calculation

We can avoid unnecessary offset calculations by moving the storage pointer to the top of the function.

There are 1 instances of this issue:

File:  src/staking/LockingMultiRewards.sol
369  Reward storage reward = _rewardData[rewardToken];

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L369

[G-38] Consider using OZ EnumerateSet in place of nested mappings

Nested mappings and multi-dimensional arrays in Solidity operate through a process of double hashing, wherein the original storage slot and the first key are concatenated and hashed, and then this hash is again concatenated with the second key and hashed. This process can be quite gas expensive due to the double-hashing operation and subsequent storage operation (sstore). A possible optimization involves manually concatenating the keys followed by a single hash operation and an sstore. However, this technique introduces the risk of storage collision, especially when there are other nested hash maps in the contract that use the same key types. Because Solidity is unaware of the number and structure of nested hash maps in a contract, it follows a conservative approach in computing the storage slot to avoid possible collisions. OpenZeppelin's EnumerableSet provides a potential solution to this problem. It creates a data structure that combines the benefits of set operations with the ability to enumerate stored elements, which is not natively available in Solidity. EnumerableSet handles the element uniqueness internally and can therefore provide a more gas-efficient and collision-resistant alternative to nested mappings or multi-dimensional arrays in certain scenarios.

There are 5 instances of this issue:

File:  src/blast/BlastOnboarding.sol
41  mapping(address user => mapping(address token => Balances)) public balances;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L41

File:  src/mimswap/periphery/Factory.sol
35   mapping(address base => mapping(address quote => address[] pools)) public pools;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L35

File:  src/staking/LockingMultiRewards.sol
94    mapping(address user => mapping(address token => uint256 amount)) public userRewardPerTokenPaid;
95    mapping(address user => mapping(address token => uint256 amount)) public rewards;

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L94-L95

[G-39] Instead of counting down in for statements, count up

Counting down is more gas efficient than counting up because neither we are making zero variable to non-zero variable and also we will get gas refund in the last transaction when making non-zero to zero variable.

There are 8 instances of this issue:

File:  src/blast/BlastOnboarding.sol
165  for (uint256 i = 0; i < tokens.length; i++) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L165

File:  src/mimswap/periphery/Router.sol
544  for (uint256 i = 0; i < iterations; ) {

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L544

File:  src/staking/LockingMultiRewards.sol
504 for (uint256 i; i < users.length; ) {

547 for (uint256 i; i < rewardTokens.length; ) {

561 for (uint256 i; i < rewardTokens.length; ) {

575 for (uint256 i; i < rewardTokens.length; ) {

580 for (uint256 j; j < users.length; ) {

614 for (uint256 i; i < rewardTokens.length; ) {                    

https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L504

[G-40] Use the inputs/results of assignments rather than re-reading state variables

When a state variable is assigned, it saves gas to use the value being assigned, later in the function, rather than re-reading the state variable itself. If needed, it can also be stored to a local variable, and be used in that way. Both options avoid a Gwarmaccess (100 gas). Note that if the operation is, say +=, the assignment also results in a value which can be used. The instances below point to the first reference after the assignment, since later references are already covered by issues describing the caching of state variable values.

There are 44 instances of this issue:

File: src/blast/BlastOnboardingBoot.sol
103             MIM.safeApprove(address(router), type(uint256).max);

104             USDB.safeApprove(address(router), type(uint256).max);

117             staking = new LockingMultiRewards(pool, 30_000, 7 days, 13 weeks, address(this));

122             pool.safeApprove(address(staking), totalPoolShares);

122             pool.safeApprove(address(staking), totalPoolShares);

122             pool.safeApprove(address(staking), totalPoolShares);

124             emit LogLiquidityBootstrapped(pool, address(staking), totalPoolShares);

126             return (pool, address(staking), totalPoolShares);

148             emit LogReadyChanged(ready);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastOnboardingBoot.sol#L148:148

File: src/blast/BlastWrappers.sol
67              BlastYields.configureDefaultClaimables(_governor);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/blast/BlastWrappers.sol#L67:67

File: src/mimswap/MagicLP.sol
141                 _BASE_TARGET_ = _BASE_RESERVE_;

142                 _QUOTE_TARGET_ = _QUOTE_RESERVE_;

146                 _BASE_TARGET_ = _BASE_RESERVE_;

147                 _QUOTE_TARGET_ = _QUOTE_RESERVE_;

144             if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_ < _QUOTE_TARGET_) {

144             if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_ < _QUOTE_TARGET_) {

309                 uint256 quoteInput = quoteBalance - uint256(_QUOTE_RESERVE_);

315                 if (uint256(_BASE_RESERVE_) - baseBalance > receiveBaseAmount) {

325                 emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo);

325                 emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo);

331                 uint256 baseInput = baseBalance - uint256(_BASE_RESERVE_);

337                 if (uint256(_QUOTE_RESERVE_) - quoteBalance > receiveQuoteAmount) {

347                 emit Swap(address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, assetTo);

342                 if (_RState_ != uint32(newRState)) {

381                 shares = quoteBalance < DecimalMath.mulFloor(baseBalance, _I_) ? DecimalMath.divFloor(quoteBalance, _I_) : baseBalance;

381                 shares = quoteBalance < DecimalMath.mulFloor(baseBalance, _I_) ? DecimalMath.divFloor(quoteBalance, _I_) : baseBalance;

383                 _QUOTE_TARGET_ = DecimalMath.mulFloor(shares, _I_).toUint112();

402                 _BASE_TARGET_ = (uint256(_BASE_TARGET_) + DecimalMath.mulFloor(uint256(_BASE_TARGET_), mintRatio)).toUint112();

402                 _BASE_TARGET_ = (uint256(_BASE_TARGET_) + DecimalMath.mulFloor(uint256(_BASE_TARGET_), mintRatio)).toUint112();

402                 _BASE_TARGET_ = (uint256(_BASE_TARGET_) + DecimalMath.mulFloor(uint256(_BASE_TARGET_), mintRatio)).toUint112();

403                 _QUOTE_TARGET_ = (uint256(_QUOTE_TARGET_) + DecimalMath.mulFloor(uint256(_QUOTE_TARGET_), mintRatio)).toUint112();

403                 _QUOTE_TARGET_ = (uint256(_QUOTE_TARGET_) + DecimalMath.mulFloor(uint256(_QUOTE_TARGET_), mintRatio)).toUint112();

385                 if (_QUOTE_TARGET_ == 0) {

438             _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) - (uint256(_BASE_TARGET_) * shareAmount).divCeil(totalShares));

438             _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) - (uint256(_BASE_TARGET_) * shareAmount).divCeil(totalShares));

513                 _BASE_TARGET_ = uint112((uint256(_BASE_TARGET_) * baseBalance) / uint256(_BASE_RESERVE_));

517                 _QUOTE_TARGET_ = uint112((uint256(_QUOTE_TARGET_) * quoteBalance) / uint256(_QUOTE_RESERVE_));

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/MagicLP.sol#L517:517

File: src/mimswap/auxiliary/FeeRateModel.sol
39              return IFeeRateImpl(implementation).getFeeRate(msg.sender, trader, lpFeeRate);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/auxiliary/FeeRateModel.sol#L39:39

File: src/mimswap/periphery/Factory.sol
86              IMagicLP(clone).init(address(baseToken_), address(quoteToken_), lpFeeRate_, address(maintainerFeeRateModel), i_, k_);

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Factory.sol#L86:86

File: src/mimswap/periphery/Router.sol
88              clone = IFactory(factory).create(useTokenAsQuote ? address(weth) : token, useTokenAsQuote ? token : address(weth), lpFeeRate, i, k);

203             if (token == address(weth)) {

237             if (token == address(weth)) {

285             if (token == address(weth)) {

https://github.com/r109n/2024-03-abracadabra-money/tree/main/mimswap/periphery/Router.sol#L285:285

#0 - c4-pre-sort

2024-03-15T14:58:44Z

141345 marked the issue as sufficient quality report

#1 - 141345

2024-03-16T00:22:21Z

204 albahaca l r nc 3 0 25

G 1 i G 2 l G 3 n G 4 n G 5 n G 6 n G 7 i G 8 n G 9 n G 10 n G 11 n G 12 n G 13 n G 14 n G 15 i G 16 n G 17 n G 18 n G 19 n G 20 l G 21 n G 22 n G 23 i G 24 i G 25 i G 26 i G 27 i G 28 n G 29 n G 30 i G 31 n G 32 i G 33 i G 34 i G 35 n G 36 n G 37 n G 38 n G 39 n G 40 l

#2 - c4-judge

2024-03-29T17:00:32Z

thereksfour marked the issue as grade-b

#3 - albahaca0000

2024-04-04T10:09:50Z

Hi @thereksfour thanks for judging

I dont' know why my report has grade b , I suggest a lot of gas optimization techniques, That are similar to the grade a reports and with more unique ones

These issues marked in my report as i but in other report marked as L

[G-23] State variables can be packed into fewer storage slots

[G-25] State variable read in a loop

[G-32] Structs can be modified to fit in fewer storage slots

These issue makred in my report as but NC but in other report makred as L

[G-22] Use local variables for emitting

These issue makred in my report as i but in other report makred as NC

[G‑33] Use asembly to validate msg.sender

[G‑34] Unlimited gas consumption risk due to external call recipients

so finally in my report there are 7 L that no other grade a report have.

#4 - c4-judge

2024-04-06T07:34:39Z

thereksfour marked the issue as grade-a

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