Abracadabra Mimswap - pfapostol'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: 29/36

Findings: 2

Award: $40.20

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

15.328 USDC - $15.33

Labels

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

External Links

The content section shows only 10 examples, subsequent cases are listed as links.

Summary

Low Severity issues

IssueInstances
[L-01]Permanent DoS due to non-shrinking array usage in an unbounded loop5
[L-02]Missing checks for address(0) when assigning values to address state variables6
[L-03]Empty receive functions can cause gas issues2
[L-04]Contract inherits Pausable but does not expose pausing/unpausing functionality1
[L-05]Emitting storage values instead of the memory one.8
[L-06]Functions calling contracts/addresses with transfer hooks are missing reentrancy guards37
[L-07]onlyOwner functions not accessible if owner renounces ownership34
[L-08]Subtraction may underflow if multiplication is too large1
[L-09]Consider bounding input array length3
[L-10]Use of abi.encodePacked with dynamic types inside keccak2561
[L-11]Using >/>= without specifying an upper bound in version pragma is unsafe20

None Critical issues

IssueInstances
[N-01]Empty function blocks4
[N-02]Functions which are either private or internal should have a preceding _ in their name25
[N-03]Function names should differ to make the code more readable4
[N-04]Emits without msg.sender parameter6
[N-05]A function which defines named returns in it's declaration doesn't need to use return21
[N-06]Use multiple require() and if statements instead of &&11
[N-07]Change public to external for functions that are not called internally17
[N-08]Constants in comparisons should appear on the left side123
[N-09]Do not use underscore at the end of variable name98
[N-10]Event is missing indexed field39
[N-11]Variable names that consist of all capital letters should be reserved for constant/immutable variables35
[N-12]Consider using delete rather than assigning zero/false to clear values3
[N-13]Array is push()ed but not pop()ed5
[N-14]Unused contract variables27
[N-15]else-block not required5
[N-16]Remaining eth may not be refunded to users7
[N-17]Condition will not revert when block.timestamp is == to the compared variable3
[N-18]External calls in an un-bounded for-loop may result in a DOS8
[N-19]constructor missing zero address check11
[N-20]Variable initialization with default value2
[N-21]Complex math should be split into multiple steps21
[N-22]Duplicated require/if statements should be refactored69
[N-23]Variable names don't follow the Solidity naming convention38
[N-24]Contract functions should use an interface124
[N-25]Custom error without details78
[N-26]State variables should include comments83
[N-27]Events that mark critical parameter changes should contain both the old and the new value20
[N-28]Constant redefined elsewhere2
[N-29]Function ordering does not follow the Solidity style guide12
[N-30]Array indicies should be referenced via enums rather than via numeric literals3
[N-31]Use of override is unnecessary13
[N-32]Unused function parameter13
[N-33]Unused event definition3
[N-34]Function names should use lowerCamelCase22
[N-35]Overflows in unchecked blocks9
[N-36]Missing event and or timelock for critical parameter change21
[N-37]Setters should prevent re-setting of the same value21
[N-38]Events may be emitted out of order due to reentrancy16
[N-39]Execution at deadlines should be allowed4
[N-40]Function can return unassigned variable1
[N-41]Missing zero address check in initializer1
[N-42]Events should be emitted before external calls38
[N-43]Use @inheritdoc for overridden functions13
[N-44]Contract name does not follow the Solidity Style Guide7
[N-45]Variable names for immutables should use UPPER_CASE_WITH_UNDERSCORES16
[N-46]Use a single file for system wide constants22
[N-47]Unsafe uint to int conversion1
[N-48]Use SafeCast to safely downcast variables30
[N-49]Consider adding a block/deny-list5
[N-50]Expressions for constant values should use immutable rather than constant7
[N-51]Lines are too long54

Low Risk Issues

[L-01]<a name="l-01"></a> Permanent DoS due to non-shrinking array usage in an unbounded loop

There are some arrays that can grow indefinitely in size, as they never shrink. When these arrays are used in unbounded loops, they may lead to a permanent denial-of-service (DoS) of these functions.

There are 5 instance(s) of this issue:


- src/staking/LockingMultiRewards.sol
// @audit Array pushed here, but never poped
313	        rewardTokens.push(rewardToken);
...
// @audit Array pushed here, but never poped
501	            _userLocks[user].push(LockedBalance({amount: amount, unlockTime: _nextUnlockTime}));
...
// @audit Array pushed here, but never poped
648	                _userRewardLock[user].items.push(RewardLockItem({token: rewardToken, amount: rewardAmount}));
...
// @audit function _updateRewards is vulnerable as length grows in size but never shrinks
547	        for (uint256 i; i < rewardTokens.length; ) {
...
// @audit function _updateRewardsForUser is vulnerable as length grows in size but never shrinks
561	        for (uint256 i; i < rewardTokens.length; ) {
...
// @audit function _updateRewardsForUsers is vulnerable as length grows in size but never shrinks
575	        for (uint256 i; i < rewardTokens.length; ) {
...
// @audit function _updateRewardsForUsers is vulnerable as length grows in size but never shrinks
580	            for (uint256 j; j < users.length; ) {
...
// @audit function _getRewards is vulnerable as length grows in size but never shrinks
614	        for (uint256 i; i < rewardTokens.length; ) {

GitHub : 547, 561, 575, 580, 614

[L-02]<a name="l-02"></a> Missing checks for address(0) when assigning values to address state variables

There are 6 instance(s) of this issue:


- src/blast/BlastOnboarding.sol
190	    function setBootstrapper(address bootstrapper_) external onlyOwner {
...
// @audit Address `bootstrapper_` is not cheched for zero in current function
191	        bootstrapper = bootstrapper_;

GitHub : 191


- src/blast/BlastOnboardingBoot.sol
96	    function bootstrap(uint256 minAmountOut) external onlyOwner onlyState(State.Closed) returns (address, address, uint256) {
...
// @audit Address `` is not cheched for zero in current function
117	        staking = new LockingMultiRewards(pool, 30_000, 7 days, 13 weeks, address(this));
...
129	    function initialize(Router _router) external onlyOwner {
...
// @audit Address `` is not cheched for zero in current function
130	        router = Router(payable(_router));
...
// @audit Address `` is not cheched for zero in current function
131	        factory = IFactory(router.factory());
...
137	    function setStaking(LockingMultiRewards _staking) external onlyOwner {
...
// @audit Address `_staking` is not cheched for zero in current function
142	        staking = _staking;

GitHub : 117, 130, 131, 142


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

GitHub : 58

[L-03]<a name="l-03"></a> Empty receive functions can cause gas issues

In Solidity, functions that receive Ether without corresponding functionality to utilize or withdraw these funds can inadvertently lead to a permanent loss of value. This is because if someone sends Ether to the contract, they may be unable to retrieve it. To avoid this, functions receiving Ether should be accompanied by additional methods that process or allow the withdrawal of these funds. If the intent is to use the received Ether, it should trigger a separate function; if not, it should revert the transaction (for instance, via require(msg.sender == address(weth))). Access control checks can also prevent unintended Ether transfers, despite the slight gas cost they entail. If concerns over gas costs persist, at minimum, include a rescue function to recover unused Ether. Missteps in handling Ether in smart contracts can lead to irreversible financial losses, hence these precautions are crucial.

There are 2 instance(s) of this issue:


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

GitHub : 13


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

GitHub : 36

[L-04]<a name="l-04"></a> 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.

Resolution: To take advantage of the Pausable contract, create public or external functions that call _pause and _unpause.

There are 1 instance(s) of this issue:


- src/blast/BlastOnboarding.sol
12	contract BlastOnboardingData is Owned, Pausable {
13	    error ErrZeroAddress();
14	    error ErrWrongState();
15	    error ErrUnsupportedToken();
16	    error ErrNotAllowed();
17	
18	    enum State {
19	        Idle,
20	        Opened,
21	        Closed
22	    }
23	
24	    struct Balances {
25	        uint256 unlocked;
26	        uint256 locked;
27	        uint256 total;
28	    }
29	
30	    State public state;
31	    address public bootstrapper;
32	    address public feeTo;
33	    BlastTokenRegistry public registry;
34	
35	    // Global
36	    mapping(address token => bool) public supportedTokens;
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;
42	
43	    modifier onlyState(State _state) {
44	        if (state != _state) {
45	            revert ErrWrongState();
46	        }
47	        _;
48	    }
49	
50	    modifier onlySupportedTokens(address token) {
51	        if (!supportedTokens[token]) {
52	            revert ErrUnsupportedToken();
53	        }
54	
55	        _;
56	    }
57	
58	    constructor() Owned(msg.sender) {
59	        BlastYields.configureDefaultClaimables(address(this));
60	        BlastPoints.configure();
61	    }
62	}

GitHub : 12..62

[L-05]<a name="l-05"></a> Emitting storage values instead of the memory one.

Emitted values should not be read from storage again. Instead, the existing values from memory should be used.

There are 8 instance(s) of this issue:


- src/blast/BlastOnboardingBoot.sol
124	        emit LogLiquidityBootstrapped(pool, address(staking), totalPoolShares);
...
148	        emit LogReadyChanged(ready);

GitHub : 124, 148


- src/mimswap/MagicLP.sol
264	        emit Swap(address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, to);
...
287	        emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, to);
...
325	            emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo);
...
347	            emit Swap(address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, assetTo);

GitHub : 264, 287, 325, 347


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

GitHub : 88


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

GitHub : 318

[L-06]<a name="l-06"></a> Functions calling contracts/addresses with transfer hooks are missing reentrancy guards

Even if the function follows the best practice of check-effects-interaction, not using a reentrancy guard when there may be transfer hooks will open the users of this protocol up to read-only reentrancies with no way to protect against it, except by block-listing the whole protocol.`

There are 37 instance(s) of this issue:


- src/blast/BlastOnboarding.sol
101	    function deposit(address token, uint256 amount, bool lock_) external whenNotPaused onlyState(State.Opened) onlySupportedTokens(token) {
...
// @audit Function `deposit` doesn't  have the `nonReentrant` modifier
102	        token.safeTransferFrom(msg.sender, address(this), amount);
...
132	    function withdraw(address token, uint256 amount) external whenNotPaused onlySupportedTokens(token) {
...
// @audit Function `withdraw` doesn't  have the `nonReentrant` modifier
138	        token.safeTransfer(msg.sender, amount);
...
205	    function rescue(address token, address to, uint256 amount) external onlyOwner {
...
// @audit Function `rescue` doesn't  have the `nonReentrant` modifier
210	        token.safeTransfer(to, amount);

GitHub : 102, 138, 210


- src/mimswap/MagicLP.sol
461	    function rescue(address token, address to, uint256 amount) external onlyImplementationOwner {
...
// @audit Function `rescue` doesn't  have the `nonReentrant` modifier
466	        token.safeTransfer(to, amount);
...
581	    function _transferBaseOut(address to, uint256 amount) internal {
...
// @audit Function `_transferBaseOut` doesn't  have the `nonReentrant` modifier
583	            _BASE_TOKEN_.safeTransfer(to, amount);

GitHub : 466, 583, 589


- src/mimswap/periphery/Router.sol

GitHub : 68, 69, 91, 92, 172, 173, 186, 187, 217, 224, 244, 246, 269, 282, 311, 330, 328, 360, 395, 393, 411, 428, 443, 456, 474, 489


- src/staking/LockingMultiRewards.sol

GitHub : 180, 330, 367, 464, 637

[L-07]<a name="l-07"></a> onlyOwner functions not accessible if owner renounces ownership

The owner is able to perform certain privileged activities, but it's possible to set the owner to address(0). This can represent a certain risk if the ownership is renounced for any other reason than by design. Renouncing ownership will leave the contract without an owner, therefore limiting any functionality that needs authority.

There are 34 instance(s) of this issue:


- src/blast/BlastBox.sol
68	    function callBlastPrecompile(bytes calldata data) external onlyOwner {
...
72	    function setFeeTo(address feeTo_) external onlyOwner {
...
81	    function setTokenEnabled(address token, bool enabled) external onlyOwner {

GitHub : 68, 72, 81


- src/blast/BlastGovernor.sol
40	    function setFeeTo(address _feeTo) external onlyOwner {
...
49	    function callBlastPrecompile(bytes calldata data) external onlyOwner {
...
53	    function execute(address to, uint256 value, bytes calldata data) external onlyOwner returns (bool success, bytes memory result) {

GitHub : 40, 49, 53


- src/blast/BlastOnboarding.sol
147	    function setFeeTo(address feeTo_) external onlyOwner {
...
156	    function callBlastPrecompile(bytes calldata data) external onlyOwner {
...
160	    function claimGasYields() external onlyOwner returns (uint256) {
...
164	    function claimTokenYields(address[] memory tokens) external onlyOwner {

GitHub : 147, 156, 160, 164, 175, 185, 190, 195, 200, 205, 214, 218


- src/blast/BlastOnboardingBoot.sol

GitHub : 96, 129, 137, 146


- src/blast/BlastTokenRegistry.sol

GitHub : 14


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 46, 57


- src/mimswap/periphery/Factory.sol

GitHub : 96, 105, 116, 120..126


- src/staking/LockingMultiRewards.sol

GitHub : 300, 317, 324, 337, 341

[L-08]<a name="l-08"></a> Subtraction may underflow if multiplication is too large

The following expressions may underflow, as the subtrahend could be greater than the minuend in case the former is multiplied by a large number.

There are 1 instance(s) of this issue:


- src/mimswap/libraries/Math.sol
21	        uint256 remainder = a - quotient * b;

GitHub : 21

[L-09]<a name="l-09"></a> Consider bounding input array length

The functions below take in an unbounded array, and make function calls for entries in the array. While the function will revert if it eventually runs out of gas, it may be a nicer user experience to require() that the length of the array is below some reasonable maximum, so that the user doesn't have to use up a full transaction's gas only to see that the transaction reverts.

There are 3 instance(s) of this issue:


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

GitHub : 164


- src/staking/LockingMultiRewards.sol
397	    function processExpiredLocks(address[] memory users, uint256[] calldata lockIndexes) external onlyOperators {
...
572	    function _updateRewardsForUsers(address[] memory users) internal {

GitHub : 397, 572

[L-10]<a name="l-10"></a> Use of abi.encodePacked with dynamic types inside keccak256

abi.encodePacked should not be used with dynamic types when passing the result to a hash function such as keccak256. Use abi.encode instead, which will pad items to 32 bytes, to prevent any hash collisions.

There are 1 instance(s) of this issue:


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

GitHub : 163

[L-11]<a name="l-11"></a> Using >/>= without specifying an upper bound in version pragma is unsafe

There will be breaking changes in future versions of solidity, and at that point your code will no longer be compatible. While you may have the specific version to use in a configuration file, others that include your source files may not.

There are 20 instance(s) of this issue:


- src/blast/BlastDapp.sol
2	pragma solidity >=0.8.0;

GitHub : 2


- src/blast/BlastBox.sol
3	pragma solidity >=0.8.0;

GitHub : 3


- src/blast/BlastGovernor.sol
2	pragma solidity >=0.8.0;

GitHub : 2


- src/blast/BlastMagicLP.sol
3	pragma solidity >=0.8.0;

GitHub : 3


- src/blast/BlastOnboarding.sol
2	pragma solidity >=0.8.0;

GitHub : 2


- src/blast/BlastOnboardingBoot.sol
2	pragma solidity >=0.8.0;

GitHub : 2


- src/blast/BlastTokenRegistry.sol
2	pragma solidity >=0.8.0;

GitHub : 2


- src/blast/BlastWrappers.sol
2	pragma solidity >=0.8.0;

GitHub : 2


- src/blast/libraries/BlastPoints.sol
2	pragma solidity >=0.8.0;

GitHub : 2


- src/blast/libraries/BlastYields.sol
2	pragma solidity >=0.8.0;

GitHub : 2


- src/mimswap/MagicLP.sol

GitHub : 8


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 8


- src/mimswap/auxiliary/FeeRateModelImpl.sol

GitHub : 2


- src/mimswap/libraries/DecimalMath.sol

GitHub : 7


- src/mimswap/libraries/Math.sol

GitHub : 8


- src/mimswap/libraries/PMMPricing.sol

GitHub : 8


- src/mimswap/periphery/Factory.sol

GitHub : 2


- src/mimswap/periphery/Router.sol

GitHub : 2


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 2


- src/staking/LockingMultiRewards.sol

GitHub : 2

NonCritical Risk Issues

[N-01]<a name="n-01"></a> Empty function blocks

Empty code blocks (i.e., {}) in a Solidity contract can be harmful as they can lead to ambiguity, misinterpretation, and unintended behavior. When developers encounter empty code blocks, it may be unclear whether the absence of code is intentional or the result of an oversight. This uncertainty can cause confusion during development, testing, and debugging, increasing the likelihood of introducing errors or vulnerabilities. Moreover, empty code blocks may give a false impression of implemented functionality or security measures, creating a misleading sense of assurance. To ensure clarity and maintainability, it is essential to avoid empty code blocks and explicitly document the intended behavior or any intentional omissions.

There are 4 instance(s) of this issue:


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

GitHub : 13


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

GitHub : 12


- src/mimswap/MagicLP.sol
601	    function _afterInitialized() internal virtual {}

GitHub : 601


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

GitHub : 36

[N-02]<a name="n-02"></a> Functions which are either private or internal should have a preceding _ in their name

Add a preceding underscore to the function name, take care to refactor where there functions are called

There are 25 instance(s) of this issue:


- src/blast/BlastBox.sol
103	    function isOwner(address _account) internal view override returns (bool) {

GitHub : 103


- src/blast/libraries/BlastPoints.sol
10	    function configure() internal {

GitHub : 10


- src/blast/libraries/BlastYields.sol
20	    function enableTokenClaimable(address token) internal {
...
29	    function configureDefaultClaimables(address governor_) internal {
...
38	    function claimMaxGasYields(address recipient) internal returns (uint256) {
...
42	    function claimMaxGasYields(address contractAddress, address recipient) internal returns (uint256 amount) {
...
51	    function claimAllNativeYields(address recipient) internal returns (uint256 amount) {
...
55	    function claimAllNativeYields(address contractAddress, address recipient) internal returns (uint256 amount) {
...
60	    function claimNativeYields(address contractAddress, uint256 amount, address recipient) internal returns (uint256) {
...
70	    function claimAllTokenYields(address token, address recipient) internal returns (uint256 amount) {

GitHub : 20, 29, 38, 42, 51, 55, 60, 70, 75, 86


- src/mimswap/libraries/DecimalMath.sol

GitHub : 23, 27, 31, 35, 39, 43, 47


- src/mimswap/libraries/Math.sol

GitHub : 19, 29


- src/mimswap/libraries/PMMPricing.sol

GitHub : 39, 76, 190, 203

[N-03]<a name="n-03"></a> Function names should differ to make the code more readable

In Solidity, while function overriding allows for functions with the same name to coexist, it is advisable to avoid this practice to enhance code readability and maintainability. Having multiple functions with the same name, even with different parameters or in inherited contracts, can cause confusion and increase the likelihood of errors during development, testing, and debugging. Using distinct and descriptive function names not only clarifies the purpose and behavior of each function, but also helps prevent unintended function calls or incorrect overriding. By adopting a clear and consistent naming convention, developers can create more comprehensible and maintainable smart contracts.

There are 4 instance(s) of this issue:


- src/blast/libraries/BlastYields.sol
51	    function claimAllNativeYields(address recipient) internal returns (uint256 amount) {
...
55	    function claimAllNativeYields(address contractAddress, address recipient) internal returns (uint256 amount) {
...
38	    function claimMaxGasYields(address recipient) internal returns (uint256) {
...
42	    function claimMaxGasYields(address contractAddress, address recipient) internal returns (uint256 amount) {

GitHub : 51, 55, 38, 42

[N-04]<a name="n-04"></a> Emits without msg.sender parameter

If msg.sender play a part in the functionality of a function, any emits of this function should include msg.sender to ensure transparency with users

There are 6 instance(s) of this issue:


- src/mimswap/MagicLP.sol
// @audit Current function use `msg.sender`, but do not emit it
259	            emit RChange(newRState);
...
// @audit Current function use `msg.sender`, but do not emit it
282	            emit RChange(newRState);
...
// @audit Current function use `msg.sender`, but do not emit it
323	                emit RChange(newRState);
...
// @audit Current function use `msg.sender`, but do not emit it
345	                emit RChange(newRState);

GitHub : 259, 282, 323, 345


- src/staking/LockingMultiRewards.sol
// @audit Current function use `msg.sender`, but do not emit it
392	        emit LogRewardAdded(amount);
...
// @audit Current function use `msg.sender`, but do not emit it
475	            emit LogStaked(account, amount);

GitHub : 392, 475

[N-05]<a name="n-05"></a> A function which defines named returns in it's declaration doesn't need to use return

Once the return variable has been assigned (or has its default value), there is no need to explicitly return it at the end of the function, since it's returned automatically. Remove the return statement once ensuring it is safe to do so

There are 21 instance(s) of this issue:


- 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) {

GitHub : 78, 86, 155


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

GitHub : 51


- 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) {

GitHub : 215, 224, 228, 232


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

GitHub : 34


- src/mimswap/auxiliary/FeeRateModelImpl.sol
9	    function getFeeRate(
10	        address /*pool*/,
11	        address /*trader*/,
12	        uint256 lpFeeRate
13	    ) external pure returns (uint256 adjustedLpFeeRate, uint256 mtFeeRate) {

GitHub : 9..13


- src/mimswap/periphery/Router.sol

GitHub : 96..100, 112..116, 178..185, 229..235, 261..268, 314..321, 336..342, 404..410, 415..420, 449..455, 461..466

[N-06]<a name="n-06"></a> Use multiple require() and if statements instead of &&

Using multiple require() and if improves code readability and makes it easier to debug.

There are 11 instance(s) of this issue:


- src/blast/BlastMagicLP.sol
119	        if (!BlastMagicLP(address(implementation)).operators(msg.sender) && msg.sender != implementation.owner()) {
120	            revert ErrNotAllowedImplementationOperator();
121	        }

GitHub : 119..121


- src/blast/BlastOnboarding.sol
114	        if (caps[token] > 0 && totals[token].total > caps[token]) {
115	            revert ErrCapReached();
116	        }

GitHub : 114..116


- src/mimswap/MagicLP.sol
139	        if (_RState_ == uint32(PMMPricing.RState.BELOW_ONE) && _BASE_RESERVE_ < _BASE_TARGET_) {
140	            _RState_ = uint32(PMMPricing.RState.ONE);
141	            _BASE_TARGET_ = _BASE_RESERVE_;
142	            _QUOTE_TARGET_ = _QUOTE_RESERVE_;
143	        }
...
144	        if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_ < _QUOTE_TARGET_) {
145	            _RState_ = uint32(PMMPricing.RState.ONE);
146	            _BASE_TARGET_ = _BASE_RESERVE_;
147	            _QUOTE_TARGET_ = _QUOTE_RESERVE_;
148	        }
...
302	        if (baseBalance < _BASE_RESERVE_ && quoteBalance < _QUOTE_RESERVE_) {
303	            revert ErrFlashLoanFailed();
304	        }
...
395	        } else if (baseReserve > 0 && quoteReserve > 0) {
396	            // case 2. normal case
397	            uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve);
398	            uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve);
399	            uint256 mintRatio = quoteInputRatio < baseInputRatio ? quoteInputRatio : baseInputRatio;
400	            shares = DecimalMath.mulFloor(totalSupply(), mintRatio);
401	
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();
404	        }
...
549	        if (timeElapsed > 0 && _BASE_RESERVE_ != 0 && _QUOTE_RESERVE_ != 0) {
550	            /// @dev It is desired and expected for this value to
551	            /// overflow once it has hit the max of `type.uint256`.
552	            unchecked {
553	                _BASE_PRICE_CUMULATIVE_LAST_ += getMidPrice() * timeElapsed;
554	            }
555	        }

GitHub : 139..143, 144..148, 302..304, 395..404, 549..555


- src/mimswap/periphery/Router.sol
147	        } else if (baseReserve > 0 && quoteReserve > 0) {
148	            uint256 baseInputRatio = DecimalMath.divFloor(baseInAmount, baseReserve);
149	            uint256 quoteInputRatio = DecimalMath.divFloor(quoteInAmount, quoteReserve);
150	            if (baseInputRatio <= quoteInputRatio) {
151	                baseAdjustedInAmount = baseInAmount;
152	                quoteAdjustedInAmount = DecimalMath.mulCeil(quoteReserve, baseInputRatio);
153	                shares = DecimalMath.mulFloor(totalSupply, baseInputRatio);
154	            } else {
155	                quoteAdjustedInAmount = quoteInAmount;
156	                baseAdjustedInAmount = DecimalMath.mulCeil(baseReserve, quoteInputRatio);
157	                shares = DecimalMath.mulFloor(totalSupply, quoteInputRatio);
158	            }
159	        }
...
527	            if (quoteReserve > 0 && baseReserve > 0) {
528	                uint256 baseIncreaseRatio = DecimalMath.divFloor(baseInAmount, baseReserve);
529	                uint256 quoteIncreaseRatio = DecimalMath.divFloor(quoteInAmount, quoteReserve);
530	                if (baseIncreaseRatio <= quoteIncreaseRatio) {
531	                    baseAdjustedInAmount = baseInAmount;
532	                    quoteAdjustedInAmount = DecimalMath.mulFloor(quoteReserve, baseIncreaseRatio);
533	                } else {
534	                    quoteAdjustedInAmount = quoteInAmount;
535	                    baseAdjustedInAmount = DecimalMath.mulFloor(baseReserve, quoteIncreaseRatio);
536	                }
537	            }

GitHub : 147..159, 527..537


- src/staking/LockingMultiRewards.sol
326	        if (tokenAddress == stakingToken && tokenAmount > stakingToken.balanceOf(address(this)) - stakingTokenBalance) {
327	            revert ErrSkimmingTooMuch();
328	        }

GitHub : 326..328, 417..419

[N-07]<a name="n-07"></a> Change public to external for functions that are not called internally

Contracts are allowed to override their parents' functions and change the visibility from external to public.

There are 17 instance(s) of this issue:


- src/blast/BlastWrappers.sol
59	    function init(bytes calldata data) public payable override {

GitHub : 59


- src/mimswap/MagicLP.sol
155	    function name() public view override returns (string memory) {
...
159	    function symbol() public pure override returns (string memory) {
...
163	    function decimals() public view override returns (uint8) {
...
228	    function getBaseInput() public view returns (uint256 input) {
...
232	    function getQuoteInput() public view returns (uint256 input) {
...
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 {

GitHub : 155, 159, 163, 228, 232, 470..479


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

GitHub : 57


- src/mimswap/periphery/Factory.sol
65	    function predictDeterministicAddress(
66	        address creator,
67	        address baseToken_,
68	        address quoteToken_,
69	        uint256 lpFeeRate_,
70	        uint256 i_,
71	        uint256 k_
72	    ) public view returns (address) {

GitHub : 65..72


- src/staking/LockingMultiRewards.sol
150	    function stake(uint256 amount, bool lock_) public whenNotPaused {

GitHub : 150, 155, 186, 191, 265, 288, 300, 361

[N-08]<a name="n-08"></a> Constants in comparisons should appear on the left side

Doing so will prevent typo bugs

There are 123 instance(s) of this issue:


- src/blast/BlastBox.sol
25	        if (feeTo_ == address(0)) {
...
28	        if (address(registry_) == address(0)) {
...
73	        if (feeTo_ == address(0)) {

GitHub : 25, 28, 73


- src/blast/BlastGovernor.sol
16	        if (feeTo_ == address(0)) {
...
41	        if(_feeTo == address(0)) {

GitHub : 16, 41


- src/blast/BlastMagicLP.sol
24	        if (feeTo_ == address(0)) {
...
27	        if (address(registry_) == address(0)) {
...
81	        if (feeTo_ == address(0)) {

GitHub : 24, 27, 81


- src/blast/BlastOnboarding.sol
85	        if (address(registry_) == address(0)) {
...
89	        if (feeTo_ == address(0)) {

GitHub : 85, 89, 114, 148


- src/blast/BlastOnboardingBoot.sol

GitHub : 64, 97, 158


- src/blast/BlastTokenRegistry.sol

GitHub : 15


- src/blast/BlastWrappers.sol

GitHub : 21, 35, 48


- src/mimswap/MagicLP.sol

GitHub : 63, 64, 102, 102, 102, 108, 125, 294, 369, 375, 395, 395, 377, 385, 389, 450, 483, 546, 549, 549, 549, 582, 588, 594


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 23, 35, 47


- src/mimswap/libraries/DecimalMath.sol

GitHub : 20, 21, 48, 50, 53, 55, 55, 49


- src/mimswap/libraries/Math.sol

GitHub : 22, 23, 30, 30, 34, 52, 58, 80, 89, 94, 101, 132, 136, 140, 153, 184, 188, 192


- src/mimswap/periphery/Factory.sol

GitHub : 39, 42, 97, 106, 131, 138


- src/mimswap/periphery/Router.sol

GitHub : 39, 39, 105, 125, 131, 147, 147, 132, 142, 327, 327, 348, 348, 375, 379, 379, 392, 392, 521, 527, 527, 542, 545, 545, 550, 547, 560, 560, 590, 593, 599, 599


- src/staking/LockingMultiRewards.sol

GitHub : 131, 156, 171, 278, 283, 294, 301, 410, 417, 427, 459, 490, 636

[N-09]<a name="n-09"></a> Do not use underscore at the end of variable name

The use of underscore at the end of the variable name is uncommon and also suggests that the variable name was not completely changed.

Consider refactoring variableName_ to variableName.

There are 98 instance(s) of this issue:


- src/blast/BlastBox.sol
24	    constructor(IERC20 weth_, BlastTokenRegistry registry_, address feeTo_) DegenBox(weth_) {
...
24	    constructor(IERC20 weth_, BlastTokenRegistry registry_, address feeTo_) DegenBox(weth_) {
...
24	    constructor(IERC20 weth_, BlastTokenRegistry registry_, address feeTo_) DegenBox(weth_) {
...
56	    function claimTokenYields(address token_) external onlyOperators returns (uint256 amount) {
...
72	    function setFeeTo(address feeTo_) external onlyOwner {

GitHub : 24, 24, 24, 56, 72


- src/blast/BlastGovernor.sol
15	    constructor(address feeTo_, address _owner) OperatableV2(_owner) {

GitHub : 15


- src/blast/BlastMagicLP.sol
23	    constructor(BlastTokenRegistry registry_, address feeTo_, address owner_) MagicLP(owner_) {
...
23	    constructor(BlastTokenRegistry registry_, address feeTo_, address owner_) MagicLP(owner_) {
...
23	    constructor(BlastTokenRegistry registry_, address feeTo_, address owner_) MagicLP(owner_) {
...
48	        address feeTo_ = BlastMagicLP(address(implementation)).feeTo();

GitHub : 23, 23, 23, 48, 54, 80


- src/blast/BlastOnboarding.sol

GitHub : 84, 84, 101, 147, 190


- src/blast/BlastWrappers.sol

GitHub : 20, 20, 30, 31, 32, 33, 47, 47, 47


- src/blast/libraries/BlastYields.sol

GitHub : 29


- src/mimswap/MagicLP.sol

GitHub : 68, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 84


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 22, 22, 46, 57


- src/mimswap/periphery/Factory.sol

GitHub : 13, 14, 15, 16, 17, 19, 20, 38, 38, 38, 67, 68, 69, 70, 71, 81, 81, 81, 81, 81, 96, 105, 156, 157, 158, 159, 160, 161


- src/mimswap/periphery/Router.sol

GitHub : 38, 38


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 21, 21, 21


- src/staking/LockingMultiRewards.sol

GitHub : 150, 277, 277, 292, 292, 349, 458, 529, 528, 528, 528, 536, 536, 536, 536, 545, 559, 573, 577

[N-10]<a name="n-10"></a> Event is missing indexed field

There are 39 instance(s) of this issue:


- src/blast/BlastBox.sol
14	    event LogTokenDepositEnabled(address indexed token, bool enabled);

GitHub : 14


- src/blast/BlastMagicLP.sol
12	    event LogOperatorChanged(address indexed, bool);
...
13	    event LogYieldClaimed(uint256 gasAmount, uint256 nativeAmount, uint256 token0Amount, uint256 token1Amount);

GitHub : 12, 13


- src/blast/BlastOnboarding.sol
68	    event LogTokenSupported(address indexed token, bool supported);
...
69	    event LogDeposit(address indexed user, address indexed token, uint256 amount, bool lock);
...
70	    event LogLock(address indexed user, address indexed token, uint256 amount);
...
72	    event LogWithdraw(address indexed user, address indexed token, uint256 amount);
...
73	    event LogTokenCapChanged(address indexed token, uint256 cap);
...
74	    event LogStateChange(State state);
...
75	    event LogTokenRescue(address indexed token, address indexed to, uint256 amount);

GitHub : 68, 69, 70, 72, 73, 74, 75


- src/blast/BlastOnboardingBoot.sol

GitHub : 37, 38, 40


- src/blast/BlastTokenRegistry.sol

GitHub : 7


- src/blast/libraries/BlastYields.sol

GitHub : 8, 9, 10


- src/mimswap/MagicLP.sol

GitHub : 30, 31, 32, 33, 34, 35, 36


- src/mimswap/periphery/Factory.sol

GitHub : 23, 24, 27


- src/staking/LockingMultiRewards.sol

GitHub : 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28

[N-11]<a name="n-11"></a> Variable names that consist of all capital letters should be reserved for constant/immutable variables

If the variable needs to be different based on which class it comes from, a view/pure function should be used instead (e.g. like this).

There are 35 instance(s) of this issue:


- src/mimswap/MagicLP.sol
68	    bool internal _INITIALIZED_;
...
70	    address public _BASE_TOKEN_;
...
71	    address public _QUOTE_TOKEN_;
...
72	    uint112 public _BASE_RESERVE_;
...
73	    uint112 public _QUOTE_RESERVE_;
...
74	    uint32 public _BLOCK_TIMESTAMP_LAST_;
...
75	    uint256 public _BASE_PRICE_CUMULATIVE_LAST_;
...
76	    uint112 public _BASE_TARGET_;
...
77	    uint112 public _QUOTE_TARGET_;
...
79	    IFeeRateModel public _MT_FEE_RATE_MODEL_;

GitHub : 68, 70, 71, 72, 73, 74, 75, 76, 77, 79, 80, 81, 82, 204, 204, 204, 204, 204, 204


- src/mimswap/libraries/Math.sol

GitHub : 62, 51, 51, 51, 79, 199, 131, 131


- src/mimswap/libraries/PMMPricing.sol

GitHub : 29, 30, 31, 32, 33, 34, 209, 205

[N-12]<a name="n-12"></a> Consider using delete rather than assigning zero/false to clear values

The delete keyword more closely matches the semantics of what is being done, and draws more attention to the changing of state, which may lead to a more thorough audit of its associated logic

There are 3 instance(s) of this issue:


- src/mimswap/libraries/Math.sol
154	                temp = 0;
...
176	            bSig = false;

GitHub : 154, 176


- src/staking/LockingMultiRewards.sol
619	            rewards[user][rewardToken] = 0;

GitHub : 619

[N-13]<a name="n-13"></a> Array is push()ed but not pop()ed

Array entries are added but are never removed. Consider whether this should be the case, or whether there should be a maximum, or whether old entries should be removed. Cases where there are specific potential problems will be flagged separately under a different issue.

There are 5 instance(s) of this issue:


- src/mimswap/periphery/Factory.sol
149	        pools[baseToken][quoteToken].push(pool);
...
150	        userPools[creator].push(pool);

GitHub : 149, 150


- 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}));

GitHub : 313, 501, 648

[N-14]<a name="n-14"></a> Unused contract variables

Note that there may be cases where a variable appears to be used, but this is only because there are multiple definitions of the varible in different files. In such cases, the variable definition should be moved into a separate file. The instances below are the unused variables.

There are 27 instance(s) of this issue:


- 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) {
...
86	    function previewTotalPoolShares() external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
86	    function previewTotalPoolShares() external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
155	    function _claimable(address user) internal view returns (uint256 shares) {

GitHub : 78, 86, 86, 86, 155


- src/mimswap/MagicLP.sol
215	    function getMidPrice() public view returns (uint256 midPrice) {
...
224	    function getUserFeeRate(address user) external view returns (uint256 lpFeeRate, uint256 mtFeeRate) {
...
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) {

GitHub : 215, 224, 224, 228, 232


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 34, 34


- src/mimswap/auxiliary/FeeRateModelImpl.sol

GitHub : 13


- src/mimswap/periphery/Router.sol

GitHub : 22, 185, 235, 268, 268, 321, 342, 410, 420, 455, 466


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 34, 34


- src/staking/LockingMultiRewards.sol

GitHub : 69

[N-15]<a name="n-15"></a> else-block not required

One level of nesting can be removed by not having an else block when the if-block returns, and if (foo) { return 1; } else { return 2; } becomes if (foo) { return 1; } return 2;

There are 5 instance(s) of this issue:


- src/mimswap/libraries/DecimalMath.sol
48	        if (e == 0) {
49	            return 10 ** 18;
50	        } else if (e == 1) {
51	            return target;
52	        } else {
53	            uint p = powFloor(target, e / 2);
54	            p = (p * p) / ONE;
55	            if (e % 2 == 1) {
56	                p = (p * target) / ONE;
57	            }
58	            return p;
59	        }
...
50	        } else if (e == 1) {
51	            return target;
52	        } else {
53	            uint p = powFloor(target, e / 2);
54	            p = (p * p) / ONE;
55	            if (e % 2 == 1) {
56	                p = (p * target) / ONE;
57	            }
58	            return p;
59	        }

GitHub : 48..59, 50..59


- src/mimswap/libraries/Math.sol
22	        if (remainder > 0) {
23	            return quotient + 1;
24	        } else {
25	            return quotient;
26	        }
...
200	        if (V2 > V1) {
201	            return 0;
202	        } else {
203	            return V1 - V2;
204	        }

GitHub : 22..26, 200..204


- src/mimswap/libraries/PMMPricing.sol
204	        if (state.R == RState.BELOW_ONE) {
205	            uint256 R = DecimalMath.divFloor((state.Q0 * state.Q0) / state.Q, state.Q);
206	            R = (DecimalMath.ONE - state.K) + DecimalMath.mulFloor(state.K, R);
207	            return DecimalMath.divFloor(state.i, R);
208	        } else {
209	            uint256 R = DecimalMath.divFloor((state.B0 * state.B0) / state.B, state.B);
210	            R = (DecimalMath.ONE - state.K) + DecimalMath.mulFloor(state.K, R);
211	            return DecimalMath.mulFloor(state.i, R);
212	        }

GitHub : 204..212

[N-16]<a name="n-16"></a> Remaining eth may not be refunded to users

When a contract function accepts Ethereum and executes a .call() or similar function that also forwards Ethereum value, it's important to check for and refund any remaining balance. This is because some of the supplied value may not be used during the call execution due to gas constraints, a revert in the called contract, or simply because not all the value was needed.

If you do not account for this remaining balance, it can become "locked" in the contract. It's crucial to either return the remaining balance to the sender or handle it in a way that ensures it is not permanently stuck. Neglecting to do so can lead to loss of funds and degradation of the contract's reliability. Furthermore, it's good practice to ensure fairness and trust with your users by returning unused funds.

There are 7 instance(s) of this issue:


- src/blast/BlastWrappers.sol
59	    function init(bytes calldata data) public payable override {

GitHub : 59


- src/mimswap/periphery/Router.sol
73	    function createPoolETH(
74	        address token,
75	        bool useTokenAsQuote,
76	        uint256 lpFeeRate,
77	        uint256 i,
78	        uint256 k,
79	        address to,
80	        uint256 tokenInAmount
81	    ) external payable returns (address clone, uint256 shares) {
...
192	    function addLiquidityETH(
193	        address lp,
194	        address to,
195	        address payable refundTo,
196	        uint256 tokenInAmount,
197	        uint256 minimumShares,
198	        uint256 deadline
199	    ) external payable ensureDeadline(deadline) returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
229	    function addLiquidityETHUnsafe(
230	        address lp,
231	        address to,
232	        uint256 tokenInAmount,
233	        uint256 minimumShares,
234	        uint256 deadline
235	    ) external payable ensureDeadline(deadline) returns (uint256 shares) {
...
336	    function swapETHForTokens(
337	        address to,
338	        address[] calldata path,
339	        uint256 directions,
340	        uint256 minimumOut,
341	        uint256 deadline
342	    ) external payable ensureDeadline(deadline) returns (uint256 amountOut) {
...
415	    function sellBaseETHForTokens(
416	        address lp,
417	        address to,
418	        uint256 minimumOut,
419	        uint256 deadline
420	    ) external payable ensureDeadline(deadline) returns (uint256 amountOut) {
...
461	    function sellQuoteETHForTokens(
462	        address lp,
463	        address to,
464	        uint256 minimumOut,
465	        uint256 deadline
466	    ) external payable ensureDeadline(deadline) returns (uint256 amountOut) {

GitHub : 73..81, 192..199, 229..235, 336..342, 415..420, 461..466

[N-17]<a name="n-17"></a> Condition will not revert when block.timestamp is == to the compared variable

The condition remains unaltered even if block.timestamp is equal to the compared > or < variable. For example, if there is a condition block.timestamp > proposerSignature.expirationTimestamp, it is necessary to also account for cases where block.timestamp is == to proposerSignature.expirationTimestamp.

There are 3 instance(s) of this issue:


- src/mimswap/MagicLP.sol
421	        if (deadline < block.timestamp) {
422	            revert ErrExpired();
423	        }

GitHub : 421..423


- src/mimswap/periphery/Router.sol
48	        if (block.timestamp > deadline) {
49	            revert ErrExpired();
50	        }

GitHub : 48..50


- src/staking/LockingMultiRewards.sol
422	            if (locks[index].unlockTime > block.timestamp) {
423	                revert ErrLockNotExpired();
424	            }

GitHub : 422..424

[N-18]<a name="n-18"></a> External calls in an un-bounded for-loop may result in a DOS

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

There are 8 instance(s) of this issue:


- 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	        }

GitHub : 165..172


- src/mimswap/periphery/Router.sol
544	        for (uint256 i = 0; i < iterations; ) {
545	            if (directions & 1 == 0) {
546	                // Sell base
547	                IMagicLP(path[i]).sellBase(address(path[i + 1]));
548	            } else {
549	                // Sell quote
550	                IMagicLP(path[i]).sellQuote(address(path[i + 1]));
551	            }
552	
553	            directions >>= 1;
554	
555	            unchecked {
556	                ++i;
557	            }
558	        }

GitHub : 544..558


- 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	        }
...
547	        for (uint256 i; i < rewardTokens.length; ) {
548	            _updateRewardsGlobal(rewardTokens[i], totalSupply_);
549	            unchecked {
550	                ++i;
551	            }
552	        }
...
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	        }
...
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	        }
...
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	            }
...
614	        for (uint256 i; i < rewardTokens.length; ) {
615	            address rewardToken = rewardTokens[i];
616	            uint256 rewardAmount = rewards[user][rewardToken];
617	
618	            // in all scenario, reset the reward amount immediately
619	            rewards[user][rewardToken] = 0;
620	
621	            // don't assume the rewardTokens array is always the same length as the items array
622	            // as new reward tokens can be added by the owner
623	            if (i < rewardItemLength) {
624	                RewardLockItem storage item = _rewardLock.items[i];
625	
626	                // expired lock, claim existing unlocked rewards if any
627	                if (expired) {
628	                    uint256 amount = item.amount;
629	
630	                    // since this current lock is expired and that item index
631	                    // matches the reward index, override the current amount
632	                    // with the new locked amount.
633	                    item.amount = rewardAmount;
634	
635	                    // use cached amount
636	                    if (amount > 0) {
637	                        rewardToken.safeTransfer(user, amount);
638	                        emit LogRewardPaid(user, rewardToken, amount);
639	                    }
640	                } else {
641	                    // not expired, just add to the existing lock
642	                    item.amount += rewardAmount;
643	                }
644	            }
645	            // new reward token, create a new lock item
646	            // could mean it's adding to an existing lock or creating a new one
647	            else {
648	                _userRewardLock[user].items.push(RewardLockItem({token: rewardToken, amount: rewardAmount}));
649	            }
650	
651	            emit LogRewardLocked(user, rewardToken, rewardAmount);
652	
653	            unchecked {
654	                ++i;
655	            }
656	        }

GitHub : 405..452, 547..552, 561..568, 575..592, 580..587, 614..656

[N-19]<a name="n-19"></a> constructor missing zero address check

It is important to ensure that the constructor does not allow zero address to be set. This is a common mistake that can lead to loss of funds or redeployment of the contract.

There are 11 instance(s) of this issue:


- src/blast/BlastBox.sol
24	    constructor(IERC20 weth_, BlastTokenRegistry registry_, address feeTo_) DegenBox(weth_) {

GitHub : 24


- src/blast/BlastGovernor.sol
15	    constructor(address feeTo_, address _owner) OperatableV2(_owner) {

GitHub : 15


- src/blast/BlastMagicLP.sol
23	    constructor(BlastTokenRegistry registry_, address feeTo_, address owner_) MagicLP(owner_) {

GitHub : 23


- src/blast/BlastWrappers.sol
20	    constructor(IWETH weth_, IFactory factory, address governor_) Router(weth_, factory) {
...
29	    constructor(
30	        address implementation_,
31	        IFeeRateModel maintainerFeeRateModel_,
32	        address owner_,
33	        address governor_
34	    ) Factory(implementation_, maintainerFeeRateModel_, owner_) {
...
47	    constructor(address box_, address mim_, address governor_) CauldronV4(IBentoBoxV1(box_), IERC20(mim_)) {

GitHub : 20, 29..34, 47


- src/mimswap/MagicLP.sol
84	    constructor(address owner_) Owned(owner_) {

GitHub : 84


- src/mimswap/auxiliary/FeeRateModel.sol
22	    constructor(address maintainer_, address owner_) Owned(owner_) {

GitHub : 22


- src/mimswap/periphery/Factory.sol
38	    constructor(address implementation_, IFeeRateModel maintainerFeeRateModel_, address owner_) Owned(owner_) {

GitHub : 38


- src/oracles/aggregators/MagicLpAggregator.sol
21	    constructor(IMagicLP pair_, IAggregator baseOracle_, IAggregator quoteOracle_) {

GitHub : 21


- src/staking/LockingMultiRewards.sol

GitHub : 112..118

[N-20]<a name="n-20"></a> Variable initialization with default value

It's not necessary to initialize a variable with its default value, and it's actually worse in gas terms as it adds an overhead.

There are 2 instance(s) of this issue:


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

GitHub : 165


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

GitHub : 544

[N-21]<a name="n-21"></a> Complex math should be split into multiple steps

Consider splitting long arithmetic calculations into multiple steps to improve the code readability.

There are 21 instance(s) of this issue:


- src/mimswap/libraries/Math.sol
34	            z = (x / z + z) / 2;
...
64	        return (((DecimalMath.ONE - k) + penalty) * fairAmount) / DecimalMath.ONE2;
...
64	        return (((DecimalMath.ONE - k) + penalty) * fairAmount) / DecimalMath.ONE2;
...
64	        return (((DecimalMath.ONE - k) + penalty) * fairAmount) / DecimalMath.ONE2;
...
99	            _sqrt = sqrt(((ki / V1) * delta) + DecimalMath.ONE2);
...
97	            _sqrt = sqrt(((ki * delta) / V1) + DecimalMath.ONE2);
...
158	                temp = (((delta * V1) / V0) * i) / V0;
...
158	                temp = (((delta * V1) / V0) * i) / V0;
...
158	                temp = (((delta * V1) / V0) * i) / V0;
...
170	        uint256 part2 = (((k * V0) / V1) * V0) + (i * delta); // kQ0^2/Q1-i*deltaB

GitHub : 34, 64, 64, 64, 99, 97, 158, 158, 158, 170, 170, 170


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 38, 39, 43, 44, 45


- src/staking/LockingMultiRewards.sol

GitHub : 236, 241, 283, 294

[N-22]<a name="n-22"></a> Duplicated require/if statements should be refactored

These statements should be refactored to a separate function, as there are multiple parts of the codebase that use the same logic, to improve the code readability and reduce code duplication.

There are 69 instance(s) of this issue:


- src/blast/BlastBox.sol
25	        if (feeTo_ == address(0)) {
...
73	        if (feeTo_ == address(0)) {

GitHub : 25, 73


- src/blast/BlastMagicLP.sol
56	        if (registry.nativeYieldTokens(_BASE_TOKEN_)) {
...
59	        if (registry.nativeYieldTokens(_QUOTE_TOKEN_)) {
...
81	        if (feeTo_ == address(0)) {
...
105	        if (registry.nativeYieldTokens(_BASE_TOKEN_)) {
...
109	        if (registry.nativeYieldTokens(_QUOTE_TOKEN_)) {
...
24	        if (feeTo_ == address(0)) {

GitHub : 56, 59, 81, 105, 109, 24


- src/blast/BlastOnboarding.sol
89	        if (feeTo_ == address(0)) {
...
148	        if (feeTo_ == address(0)) {

GitHub : 89, 148, 169, 178


- src/blast/BlastWrappers.sol

GitHub : 35, 48, 51, 21


- src/mimswap/MagicLP.sol

GitHub : 256, 279, 294, 320, 342, 450, 508, 512, 516, 532, 571, 574, 582, 588


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 47, 23


- src/mimswap/libraries/Math.sol

GitHub : 52, 58, 80, 132, 140


- src/mimswap/libraries/PMMPricing.sol

GitHub : 45, 77, 80, 191, 193, 204, 40


- src/mimswap/periphery/Factory.sol

GitHub : 39, 42, 97, 106


- src/mimswap/periphery/Router.sol

GitHub : 285, 295, 327, 348, 392, 439, 545, 566, 573, 581, 105, 142, 203, 208, 237, 239


- src/staking/LockingMultiRewards.sol

GitHub : 459, 609, 627, 156, 171

[N-23]<a name="n-23"></a> Variable names don't follow the Solidity naming convention

Use mixedCase for local and state variables that are not constants, and add a trailing underscore for internal variables. Documentation

There are 38 instance(s) of this issue:


- src/blast/BlastMagicLP.sol
48	        address feeTo_ = BlastMagicLP(address(implementation)).feeTo();
...
54	        address feeTo_ = BlastMagicLP(address(implementation)).feeTo();

GitHub : 48, 54


- src/blast/BlastWrappers.sol
45	    address private immutable _governor;

GitHub : 45


- src/mimswap/MagicLP.sol
68	    bool internal _INITIALIZED_;
...
70	    address public _BASE_TOKEN_;
...
71	    address public _QUOTE_TOKEN_;
...
72	    uint112 public _BASE_RESERVE_;
...
73	    uint112 public _QUOTE_RESERVE_;
...
74	    uint32 public _BLOCK_TIMESTAMP_LAST_;
...
75	    uint256 public _BASE_PRICE_CUMULATIVE_LAST_;

GitHub : 68, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82


- src/mimswap/libraries/Math.sol

GitHub : 62, 92, 199


- src/mimswap/libraries/PMMPricing.sol

GitHub : 209, 205


- src/mimswap/periphery/Factory.sol

GitHub : 127, 129


- src/staking/LockingMultiRewards.sol

GitHub : 89, 90, 91, 92, 371, 372, 481, 482, 529, 545, 559, 573, 577, 598

[N-24]<a name="n-24"></a> Contract functions should use an interface

All external/public functions should extend an interface. This is useful to make sure that the whole API is extracted.

There are 124 instance(s) of this issue:


- src/blast/BlastBox.sol
52	    function claimGasYields() external onlyOperators returns (uint256) {
...
56	    function claimTokenYields(address token_) external onlyOperators returns (uint256 amount) {
...
68	    function callBlastPrecompile(bytes calldata data) external onlyOwner {
...
72	    function setFeeTo(address feeTo_) external onlyOwner {
...
81	    function setTokenEnabled(address token, bool enabled) external onlyOwner {

GitHub : 52, 56, 68, 72, 81


- src/blast/BlastGovernor.sol
28	    function claimNativeYields(address contractAddress) external onlyOperators returns (uint256) {
...
32	    function claimMaxGasYields(address contractAddress) external onlyOperators returns (uint256) {
...
40	    function setFeeTo(address _feeTo) external onlyOwner {
...
49	    function callBlastPrecompile(bytes calldata data) external onlyOwner {
...
53	    function execute(address to, uint256 value, bytes calldata data) external onlyOwner returns (bool success, bytes memory result) {

GitHub : 28, 32, 40, 49, 53


- src/blast/BlastMagicLP.sol

GitHub : 47, 53, 64, 72, 80, 89


- src/blast/BlastOnboarding.sol

GitHub : 101, 123, 132, 147, 156, 160, 164, 175, 185, 190, 195, 200, 205, 214, 218


- src/blast/BlastOnboardingBoot.sol

GitHub : 55, 78, 86, 96, 129, 137, 146


- src/blast/BlastTokenRegistry.sol

GitHub : 14


- src/mimswap/MagicLP.sol

GitHub : 91..98, 134, 138, 167..170, 180..183, 193, 204, 215, 219, 224, 228, 232, 236, 244, 267, 290, 360, 413..420, 461, 470..479, 504


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 34, 46, 57


- src/mimswap/auxiliary/FeeRateModelImpl.sol

GitHub : 9..13


- src/mimswap/periphery/Factory.sol

GitHub : 53, 57, 65..72, 81, 96, 105, 116, 120..126


- src/mimswap/periphery/Router.sol

GitHub : 54..63, 73..81, 96..100, 112..116, 162..169, 178..185, 192..199, 229..235, 251, 261..268, 274..281, 314..321, 336..342, 365..372, 404..410, 415..420, 432..438, 449..455, 461..466, 478..484


- src/staking/LockingMultiRewards.sol

GitHub : 150, 155, 170, 186, 191, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 253, 257, 261, 265, 269, 273, 277, 288, 300, 317, 324, 337, 341, 349, 361, 397

[N-25]<a name="n-25"></a> Custom error without details

Consider adding some parameters to the error to indicate which user or values caused the failure.

There are 78 instance(s) of this issue:


- src/blast/BlastBox.sol
17	    error ErrZeroAddress();
...
18	    error ErrUnsupportedToken();

GitHub : 17, 18


- src/blast/BlastGovernor.sol
9	    error ErrZeroAddress();

GitHub : 9


- src/blast/BlastMagicLP.sol
15	    error ErrNotAllowedImplementationOperator();

GitHub : 15


- src/blast/BlastOnboarding.sol
13	    error ErrZeroAddress();
...
14	    error ErrWrongState();
...
15	    error ErrUnsupportedToken();
...
16	    error ErrNotAllowed();
...
77	    error ErrUnsupported();
...
78	    error ErrCapReached();

GitHub : 13, 14, 15, 16, 77, 78


- src/blast/BlastOnboardingBoot.sol

GitHub : 43, 44, 45, 46, 47, 48, 49


- src/blast/BlastTokenRegistry.sol

GitHub : 8


- src/blast/BlastWrappers.sol

GitHub : 17, 43


- src/mimswap/MagicLP.sol

GitHub : 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 17


- src/mimswap/libraries/Math.sol

GitHub : 17


- src/mimswap/periphery/Factory.sol

GitHub : 29, 30


- src/mimswap/periphery/Router.sol

GitHub : 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29


- src/staking/LockingMultiRewards.sol

GitHub : 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48

[N-26]<a name="n-26"></a> State variables should include comments

Consider adding some comments on critical state variables to explain what they are supposed to do: this will help for future code reviews.

There are 83 instance(s) of this issue:


- src/blast/BlastBox.sol
20	    BlastTokenRegistry public immutable registry;
...
21	    mapping(address => bool) public enabledTokens;
...
22	    address public feeTo;

GitHub : 20, 21, 22


- src/blast/BlastGovernor.sol
11	    address public feeTo;

GitHub : 11


- src/blast/BlastMagicLP.sol
17	    BlastTokenRegistry public immutable registry;
...
21	    mapping(address => bool) public operators;

GitHub : 17, 21


- src/blast/BlastOnboarding.sol
30	    State public state;
...
31	    address public bootstrapper;
...
32	    address public feeTo;
...
33	    BlastTokenRegistry public registry;

GitHub : 30, 31, 32, 33, 36, 37, 38, 41


- src/blast/BlastOnboardingBoot.sol

GitHub : 24, 25, 26, 27, 28, 29, 30


- src/blast/BlastTokenRegistry.sol

GitHub : 10


- src/blast/BlastWrappers.sol

GitHub : 45


- src/blast/libraries/BlastPoints.sol

GitHub : 7, 8


- src/blast/libraries/BlastYields.sol

GitHub : 14


- src/mimswap/MagicLP.sol

GitHub : 61, 63, 64, 65, 66, 68, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 19, 20


- src/mimswap/libraries/DecimalMath.sol

GitHub : 20, 21


- src/mimswap/periphery/Factory.sol

GitHub : 32, 33, 35, 36


- src/mimswap/periphery/Router.sol

GitHub : 31, 33, 34


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 10, 11, 12, 13, 14, 16


- src/staking/LockingMultiRewards.sol

GitHub : 78, 79, 80, 81, 83, 84, 85, 86, 87, 89, 90, 91, 92, 94, 95, 96, 98, 100, 101, 102, 103

[N-27]<a name="n-27"></a> Events that mark critical parameter changes should contain both the old and the new value

This should especially be done if the new value is not required to be different from the old value

There are 20 instance(s) of this issue:


- src/blast/BlastBox.sol
15	    event LogFeeToChanged(address indexed feeTo);

GitHub : 15


- src/blast/BlastGovernor.sol
8	    event LogFeeToChanged(address indexed feeTo);

GitHub : 8


- src/blast/BlastMagicLP.sol
11	    event LogFeeToChanged(address indexed feeTo);
...
12	    event LogOperatorChanged(address indexed, bool);

GitHub : 11, 12


- src/blast/BlastOnboarding.sol
67	    event LogBootstrapperChanged(address indexed bootstrapper);
...
71	    event LogFeeToChanged(address indexed feeTo);
...
73	    event LogTokenCapChanged(address indexed token, uint256 cap);
...
74	    event LogStateChange(State state);

GitHub : 67, 71, 73, 74


- src/blast/BlastOnboardingBoot.sol
37	    event LogReadyChanged(bool ready);
...
41	    event LogStakingChanged(address indexed staking);

GitHub : 37, 41


- src/mimswap/MagicLP.sol

GitHub : 34, 36


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 14, 15


- src/mimswap/periphery/Factory.sol

GitHub : 25, 26, 27


- src/staking/LockingMultiRewards.sol

GitHub : 21, 26, 28

[N-28]<a name="n-28"></a> Constant redefined elsewhere

Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant in a library. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.

There are 2 instance(s) of this issue:


- src/blast/BlastBox.sol
// @audit Variable redefined in: file `src/blast/BlastBox.sol`: `registry`, file `src/blast/BlastMagicLP.sol`: `registry`
20	    BlastTokenRegistry public immutable registry;

GitHub : 20


- src/blast/BlastMagicLP.sol
// @audit Variable redefined in: file `src/blast/BlastBox.sol`: `registry`, file `src/blast/BlastMagicLP.sol`: `registry`
17	    BlastTokenRegistry public immutable registry;

GitHub : 17

[N-29]<a name="n-29"></a> Function ordering does not follow the Solidity style guide

According to the Solidity style guide, functions should be laid out in the following order :constructor(), receive(), fallback(), external, public, internal, private, but the cases below do not follow this pattern

There are 12 instance(s) of this issue:


- src/blast/BlastBox.sol
// @audit function `_onBeforeDeposit` is `internal` followed by function `claimGasYields` which is `external`
36	    function _onBeforeDeposit(
37	        IERC20 token,
38	        address /*from*/,
39	        address /*to*/,
40	        uint256 /*amount*/,
41	        uint256 /*share*/
42	    ) internal view override {

GitHub : 36..42


- src/mimswap/MagicLP.sol
// @audit function `getPMMState` is `public` followed by function `getPMMStateForCall` which is `external`
193	    function getPMMState() public view returns (PMMPricing.PMMState memory state) {
...
// @audit function `getMidPrice` is `public` followed by function `getReserves` which is `external`
215	    function getMidPrice() public view returns (uint256 midPrice) {
...
// @audit function `getQuoteInput` is `public` followed by function `version` which is `external`
232	    function getQuoteInput() public view returns (uint256 input) {
...
// @audit function `setParameters` is `public` followed by function `ratioSync` which is `external`
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 {

GitHub : 193, 215, 232, 470..479


- src/mimswap/periphery/Factory.sol
// @audit function `predictDeterministicAddress` is `public` followed by function `create` which is `external`
65	    function predictDeterministicAddress(
66	        address creator,
67	        address baseToken_,
68	        address quoteToken_,
69	        uint256 lpFeeRate_,
70	        uint256 i_,
71	        uint256 k_
72	    ) public view returns (address) {

GitHub : 65..72


- src/oracles/aggregators/MagicLpAggregator.sol
// @audit function `_getReserves` is `internal` followed by function `latestAnswer` which is `public`
33	    function _getReserves() internal view virtual returns (uint256, uint256) {
...
// @audit function `latestAnswer` is `public` followed by function `latestRoundData` which is `external`
37	    function latestAnswer() public view override returns (int256) {

GitHub : 33, 37


- src/staking/LockingMultiRewards.sol
// @audit function `getRewards` is `public` followed by function `rewardData` which is `external`
191	    function getRewards() public virtual {
...
// @audit function `_earned` is `internal` followed by function `addReward` which is `public`
292	    function _earned(address user, uint256 balance_, address rewardToken, uint256 rewardPerToken_) internal view returns (uint256) {

GitHub : 191, 292, 300, 361

[N-30]<a name="n-30"></a> Array indicies should be referenced via enums rather than via numeric literals

There are 3 instance(s) of this issue:


- src/mimswap/periphery/Router.sol
324	        address firstLp = path[0];
...
345	        address firstLp = path[0];
...
389	        address firstLp = path[0];

GitHub : 324, 345, 389

[N-31]<a name="n-31"></a> Use of override is unnecessary

Starting with Solidity version 0.8.8, using the override keyword when the function solely overrides an interface function, and the function doesn't exist in multiple base contracts, is unnecessary.

There are 13 instance(s) of this issue:


- src/blast/BlastBox.sol
36	    function _onBeforeDeposit(
37	        IERC20 token,
38	        address /*from*/,
39	        address /*to*/,
40	        uint256 /*amount*/,
41	        uint256 /*share*/
42	    ) internal view override {
...
98	    function _configure() internal override {
...
103	    function isOwner(address _account) internal view override returns (bool) {

GitHub : 36..42, 98, 103


- src/blast/BlastMagicLP.sol
39	    function version() external pure override returns (string memory) {
...
98	    function _afterInitialized() internal override {

GitHub : 39, 98


- src/blast/BlastOnboarding.sol
226	    function _implementation() internal view override returns (address) {

GitHub : 226


- src/blast/BlastWrappers.sol
59	    function init(bytes calldata data) public payable override {

GitHub : 59


- src/mimswap/MagicLP.sol
155	    function name() public view override returns (string memory) {
...
159	    function symbol() public pure override returns (string memory) {
...
163	    function decimals() public view override returns (uint8) {

GitHub : 155, 159, 163, 593


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 29, 37

[N-32]<a name="n-32"></a> Unused function parameter

Comment out the variable name to suppress compiler warnings

There are 13 instance(s) of this issue:


- src/mimswap/periphery/Router.sol
162	    function addLiquidity(
163	        address lp,
164	        address to,
165	        uint256 baseInAmount,
166	        uint256 quoteInAmount,
167	        uint256 minimumShares,
168	        uint256 deadline
169	    ) external ensureDeadline(deadline) returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
170	        (baseAdjustedInAmount, quoteAdjustedInAmount) = _adjustAddLiquidity(lp, baseInAmount, quoteInAmount);
171	
172	        IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, baseAdjustedInAmount);
173	        IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, quoteAdjustedInAmount);
174	
175	        shares = _addLiquidity(lp, to, minimumShares);
176	    }
...
178	    function addLiquidityUnsafe(
179	        address lp,
180	        address to,
181	        uint256 baseInAmount,
182	        uint256 quoteInAmount,
183	        uint256 minimumShares,
184	        uint256 deadline
185	    ) external ensureDeadline(deadline) returns (uint256 shares) {
186	        IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, baseInAmount);
187	        IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, quoteInAmount);
188	
189	        return _addLiquidity(lp, to, minimumShares);
190	    }
...
192	    function addLiquidityETH(
193	        address lp,
194	        address to,
195	        address payable refundTo,
196	        uint256 tokenInAmount,
197	        uint256 minimumShares,
198	        uint256 deadline
199	    ) external payable ensureDeadline(deadline) returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
200	        uint256 wethAdjustedAmount;
201	        uint256 tokenAdjustedAmount;
202	        address token = IMagicLP(lp)._BASE_TOKEN_();
203	        if (token == address(weth)) {
204	            token = IMagicLP(lp)._QUOTE_TOKEN_();
205	            (baseAdjustedInAmount, quoteAdjustedInAmount) = _adjustAddLiquidity(lp, msg.value, tokenInAmount);
206	            wethAdjustedAmount = baseAdjustedInAmount;
207	            tokenAdjustedAmount = quoteAdjustedInAmount;
208	        } else if (IMagicLP(lp)._QUOTE_TOKEN_() == address(weth)) {
209	            (baseAdjustedInAmount, quoteAdjustedInAmount) = _adjustAddLiquidity(lp, tokenInAmount, msg.value);
210	            wethAdjustedAmount = quoteAdjustedInAmount;
211	            tokenAdjustedAmount = baseAdjustedInAmount;
212	        } else {
213	            revert ErrNotETHLP();
214	        }
215	
216	        weth.deposit{value: wethAdjustedAmount}();
217	        address(weth).safeTransfer(lp, wethAdjustedAmount);
218	
219	        // Refund unused ETH
220	        if (msg.value > wethAdjustedAmount) {
221	            refundTo.safeTransferETH(msg.value - wethAdjustedAmount);
222	        }
223	
224	        token.safeTransferFrom(msg.sender, lp, tokenAdjustedAmount);
225	
226	        shares = _addLiquidity(lp, to, minimumShares);
227	    }
...
229	    function addLiquidityETHUnsafe(
230	        address lp,
231	        address to,
232	        uint256 tokenInAmount,
233	        uint256 minimumShares,
234	        uint256 deadline
235	    ) external payable ensureDeadline(deadline) returns (uint256 shares) {
236	        address token = IMagicLP(lp)._BASE_TOKEN_();
237	        if (token == address(weth)) {
238	            token = IMagicLP(lp)._QUOTE_TOKEN_();
239	        } else if (IMagicLP(lp)._QUOTE_TOKEN_() != address(weth)) {
240	            revert ErrNotETHLP();
241	        }
242	
243	        weth.deposit{value: msg.value}();
244	        address(weth).safeTransfer(lp, msg.value);
245	
246	        token.safeTransferFrom(msg.sender, lp, tokenInAmount);
247	
248	        return _addLiquidity(lp, to, minimumShares);
249	    }
...
314	    function swapTokensForTokens(
315	        address to,
316	        uint256 amountIn,
317	        address[] calldata path,
318	        uint256 directions,
319	        uint256 minimumOut,
320	        uint256 deadline
321	    ) external ensureDeadline(deadline) returns (uint256 amountOut) {
322	        _validatePath(path);
323	
324	        address firstLp = path[0];
325	
326	        // Transfer to the first LP
327	        if (directions & 1 == 0) {
328	            IMagicLP(firstLp)._BASE_TOKEN_().safeTransferFrom(msg.sender, address(firstLp), amountIn);
329	        } else {
330	            IMagicLP(firstLp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, address(firstLp), amountIn);
331	        }
332	
333	        return _swap(to, path, directions, minimumOut);
334	    }
...
336	    function swapETHForTokens(
337	        address to,
338	        address[] calldata path,
339	        uint256 directions,
340	        uint256 minimumOut,
341	        uint256 deadline
342	    ) external payable ensureDeadline(deadline) returns (uint256 amountOut) {
343	        _validatePath(path);
344	
345	        address firstLp = path[0];
346	        address inToken;
347	
348	        if (directions & 1 == 0) {
349	            inToken = IMagicLP(firstLp)._BASE_TOKEN_();
350	        } else {
351	            inToken = IMagicLP(firstLp)._QUOTE_TOKEN_();
352	        }
353	
354	        // Transfer to the first LP
355	        if (inToken != address(weth)) {
356	            revert ErrInTokenNotETH();
357	        }
358	
359	        weth.deposit{value: msg.value}();
360	        inToken.safeTransfer(address(firstLp), msg.value);
361	
362	        return _swap(to, path, directions, minimumOut);
363	    }
...
365	    function swapTokensForETH(
366	        address to,
367	        uint256 amountIn,
368	        address[] calldata path,
369	        uint256 directions,
370	        uint256 minimumOut,
371	        uint256 deadline
372	    ) external ensureDeadline(deadline) returns (uint256 amountOut) {
373	        _validatePath(path);
374	
375	        uint256 lastLpIndex = path.length - 1;
376	        address lastLp = path[lastLpIndex];
377	        address outToken;
378	
379	        if ((directions >> lastLpIndex) & 1 == 0) {
380	            outToken = IMagicLP(lastLp)._QUOTE_TOKEN_();
381	        } else {
382	            outToken = IMagicLP(lastLp)._BASE_TOKEN_();
383	        }
384	
385	        if (outToken != address(weth)) {
386	            revert ErrOutTokenNotETH();
387	        }
388	
389	        address firstLp = path[0];
390	
391	        // Transfer to the first LP
392	        if (directions & 1 == 0) {
393	            IMagicLP(firstLp)._BASE_TOKEN_().safeTransferFrom(msg.sender, firstLp, amountIn);
394	        } else {
395	            IMagicLP(firstLp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, firstLp, amountIn);
396	        }
397	
398	        amountOut = _swap(address(this), path, directions, minimumOut);
399	        weth.withdraw(amountOut);
400	
401	        to.safeTransferETH(amountOut);
402	    }
...
404	    function sellBaseTokensForTokens(
405	        address lp,
406	        address to,
407	        uint256 amountIn,
408	        uint256 minimumOut,
409	        uint256 deadline
410	    ) external ensureDeadline(deadline) returns (uint256 amountOut) {
411	        IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);
412	        return _sellBase(lp, to, minimumOut);
413	    }
...
415	    function sellBaseETHForTokens(
416	        address lp,
417	        address to,
418	        uint256 minimumOut,
419	        uint256 deadline
420	    ) external payable ensureDeadline(deadline) returns (uint256 amountOut) {
421	        address baseToken = IMagicLP(lp)._BASE_TOKEN_();
422	
423	        if (baseToken != address(weth)) {
424	            revert ErrInvalidBaseToken();
425	        }
426	
427	        weth.deposit{value: msg.value}();
428	        baseToken.safeTransfer(lp, msg.value);
429	        return _sellBase(lp, to, minimumOut);
430	    }
...
432	    function sellBaseTokensForETH(
433	        address lp,
434	        address to,
435	        uint256 amountIn,
436	        uint256 minimumOut,
437	        uint256 deadline
438	    ) external ensureDeadline(deadline) returns (uint256 amountOut) {
439	        if (IMagicLP(lp)._QUOTE_TOKEN_() != address(weth)) {
440	            revert ErrInvalidQuoteToken();
441	        }
442	
443	        IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);
444	        amountOut = _sellBase(lp, address(this), minimumOut);
445	        weth.withdraw(amountOut);
446	        to.safeTransferETH(amountOut);
447	    }

GitHub : 162..176, 178..190, 192..227, 229..249, 314..334, 336..363, 365..402, 404..413, 415..430, 432..447, 449..459, 461..476, 478..493

[N-33]<a name="n-33"></a> Unused event definition

Note that there may be cases where an event superficially appears to be used, but this is only because there are multiple definitions of the event in different files. In such cases, the event definition should be moved into a separate file. The instances below are the unused definitions

There are 3 instance(s) of this issue:


- src/blast/BlastMagicLP.sol
13	    event LogYieldClaimed(uint256 gasAmount, uint256 nativeAmount, uint256 token0Amount, uint256 token1Amount);

GitHub : 13


- src/mimswap/periphery/Factory.sol
26	    event LogSetMaintainer(address indexed newMaintainer);

GitHub : 26


- src/staking/LockingMultiRewards.sol
26	    event LogRewardsDurationUpdated(address token, uint256 newDuration);

GitHub : 26

[N-34]<a name="n-34"></a> Function names should use lowerCamelCase

According to the Solidity style guide function names should be in mixedCase (lowerCamelCase)

There are 22 instance(s) of this issue:


- src/mimswap/MagicLP.sol
138	    function correctRState() external {
...
193	    function getPMMState() public view returns (PMMPricing.PMMState memory state) {
...
204	    function getPMMStateForCall() external view returns (uint256 i, uint256 K, uint256 B, uint256 Q, uint256 B0, uint256 Q0, uint256 R) {

GitHub : 138, 193, 204


- src/mimswap/libraries/Math.sol
51	    function _GeneralIntegrate(uint256 V0, uint256 V1, uint256 V2, uint256 i, uint256 k) internal pure returns (uint256) {
...
79	    function _SolveQuadraticFunctionForTarget(uint256 V1, uint256 delta, uint256 i, uint256 k) internal pure returns (uint256) {
...
131	    function _SolveQuadraticFunctionForTrade(uint256 V0, uint256 V1, uint256 delta, uint256 i, uint256 k) internal pure returns (uint256) {

GitHub : 51, 79, 131


- src/mimswap/libraries/PMMPricing.sol
104	    function _ROneSellBaseToken(
105	        PMMState memory state,
106	        uint256 payBaseAmount
107	    )
108	        internal
109	        pure
110	        returns (
111	            uint256 // receiveQuoteToken
112	        )
113	    {
...
119	    function _ROneSellQuoteToken(
120	        PMMState memory state,
121	        uint256 payQuoteAmount
122	    )
123	        internal
124	        pure
125	        returns (
126	            uint256 // receiveBaseToken
127	        )
128	    {
...
134	    function _RBelowSellQuoteToken(
135	        PMMState memory state,
136	        uint256 payQuoteAmount
137	    )
138	        internal
139	        pure
140	        returns (
141	            uint256 // receiveBaseToken
142	        )
143	    {
...
147	    function _RBelowSellBaseToken(
148	        PMMState memory state,
149	        uint256 payBaseAmount
150	    )
151	        internal
152	        pure
153	        returns (
154	            uint256 // receiveQuoteToken
155	        )
156	    {

GitHub : 104..113, 119..128, 134..143, 147..156, 162..171, 175..184


- src/mimswap/periphery/Router.sol

GitHub : 73..81, 192..199, 229..235, 274..281, 336..342, 365..372, 415..420, 432..438, 461..466, 478..484

[N-35]<a name="n-35"></a> Overflows in unchecked blocks

While integers with a large number of bits are unlikely to overflow on human time scales, it is not strictly correct to use an unchecked block around them, because eventually they will overflow, and unchecked blocks are meant for cases where it's mathematically impossible for an operation to trigger an overflow (e.g. a prior require() statement prevents the overflow case)

There are 9 instance(s) of this issue:


- src/mimswap/MagicLP.sol
552	            unchecked {
553	                _BASE_PRICE_CUMULATIVE_LAST_ += getMidPrice() * timeElapsed;
554	            }

GitHub : 552..554


- src/mimswap/periphery/Router.sol
555	            unchecked {
556	                ++i;
557	            }

GitHub : 555..557


- src/staking/LockingMultiRewards.sol
449	            unchecked {
450	                ++i;
451	            }
...
504	            unchecked {
505	                ++lockCount;
506	            }
...
549	            unchecked {
550	                ++i;
551	            }
...
565	            unchecked {
566	                ++i;
567	            }
...
584	                unchecked {
585	                    ++j;
586	                }
...
589	            unchecked {
590	                ++i;
591	            }
...
653	            unchecked {
654	                ++i;
655	            }

GitHub : 449..451, 504..506, 549..551, 565..567, 584..586, 589..591, 653..655

[N-36]<a name="n-36"></a> Missing event and or timelock for critical parameter change

Events help non-contract tools to track changes, and timelocks prevent users from being surprised by changes

There are 21 instance(s) of this issue:


- src/blast/BlastBox.sol
72	    function setFeeTo(address feeTo_) external onlyOwner {
...
81	    function setTokenEnabled(address token, bool enabled) external onlyOwner {

GitHub : 72, 81


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

GitHub : 40


- src/blast/BlastMagicLP.sol
80	    function setFeeTo(address feeTo_) external onlyImplementation onlyImplementationOwner {
...
89	    function setOperator(address operator, bool status) external onlyImplementation onlyImplementationOwner {

GitHub : 80, 89


- src/blast/BlastOnboarding.sol
147	    function setFeeTo(address feeTo_) external onlyOwner {
...
175	    function setTokenSupported(address token, bool supported) external onlyOwner {
...
185	    function setCap(address token, uint256 cap) external onlyOwner onlySupportedTokens(token) {
...
190	    function setBootstrapper(address bootstrapper_) external onlyOwner {

GitHub : 147, 175, 185, 190


- src/blast/BlastOnboardingBoot.sol
137	    function setStaking(LockingMultiRewards _staking) external onlyOwner {

GitHub : 137, 146


- src/blast/BlastTokenRegistry.sol

GitHub : 14


- src/mimswap/MagicLP.sol

GitHub : 470..479, 528, 545, 560


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 46, 57


- src/mimswap/periphery/Factory.sol

GitHub : 96, 105


- src/staking/LockingMultiRewards.sol

GitHub : 317

[N-37]<a name="n-37"></a> Setters should prevent re-setting of the same value

This especially problematic when the setter also emits the same value, which may be confusing to offline parsers

There are 21 instance(s) of this issue:


- src/blast/BlastBox.sol
72	    function setFeeTo(address feeTo_) external onlyOwner {
...
81	    function setTokenEnabled(address token, bool enabled) external onlyOwner {

GitHub : 72, 81


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

GitHub : 40


- src/blast/BlastMagicLP.sol
80	    function setFeeTo(address feeTo_) external onlyImplementation onlyImplementationOwner {
...
89	    function setOperator(address operator, bool status) external onlyImplementation onlyImplementationOwner {

GitHub : 80, 89


- src/blast/BlastOnboarding.sol
147	    function setFeeTo(address feeTo_) external onlyOwner {
...
175	    function setTokenSupported(address token, bool supported) external onlyOwner {
...
185	    function setCap(address token, uint256 cap) external onlyOwner onlySupportedTokens(token) {
...
190	    function setBootstrapper(address bootstrapper_) external onlyOwner {

GitHub : 147, 175, 185, 190


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

GitHub : 129, 137, 146


- src/blast/BlastTokenRegistry.sol

GitHub : 14


- src/mimswap/MagicLP.sol

GitHub : 91..98, 470..479


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 46, 57


- src/mimswap/periphery/Factory.sol

GitHub : 96, 105


- src/staking/LockingMultiRewards.sol

GitHub : 317, 536

[N-38]<a name="n-38"></a> Events may be emitted out of order due to reentrancy

Ensure that events follow the best practice of check-effects-interaction, and are emitted before external calls

There are 16 instance(s) of this issue:


- src/blast/BlastBox.sol
81	    function setTokenEnabled(address token, bool enabled) external onlyOwner {

GitHub : 81


- src/blast/BlastOnboarding.sol
101	    function deposit(address token, uint256 amount, bool lock_) external whenNotPaused onlyState(State.Opened) onlySupportedTokens(token) {
...
132	    function withdraw(address token, uint256 amount) external whenNotPaused onlySupportedTokens(token) {
...
175	    function setTokenSupported(address token, bool supported) external onlyOwner {
...
205	    function rescue(address token, address to, uint256 amount) external onlyOwner {

GitHub : 101, 132, 175, 205


- src/blast/BlastOnboardingBoot.sol
55	    function claim(bool lock) external returns (uint256 shares) {
...
96	    function bootstrap(uint256 minAmountOut) external onlyOwner onlyState(State.Closed) returns (address, address, uint256) {

GitHub : 55, 96


- src/blast/libraries/BlastYields.sol
20	    function enableTokenClaimable(address token) internal {
...
29	    function configureDefaultClaimables(address governor_) internal {

GitHub : 20, 29


- src/mimswap/MagicLP.sol
290	    function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external nonReentrant {

GitHub : 290, 413..420, 461


- src/mimswap/periphery/Factory.sol

GitHub : 81


- src/staking/LockingMultiRewards.sol

GitHub : 170, 324, 361

[N-39]<a name="n-39"></a> Execution at deadlines should be allowed

The condition may be wrong in these cases, as when block.timestamp is equal to the compared > or < variable these blocks will not be executed.

There are 4 instance(s) of this issue:


- src/mimswap/MagicLP.sol
421	        if (deadline < block.timestamp) {

GitHub : 421


- src/mimswap/periphery/Router.sol
48	        if (block.timestamp > deadline) {

GitHub : 48


- src/staking/LockingMultiRewards.sol
379	        if (block.timestamp < reward.periodFinish) {
...
422	            if (locks[index].unlockTime > block.timestamp) {

GitHub : 379, 422

[N-40]<a name="n-40"></a> Function can return unassigned variable

Make sure that functions with a return value always return a valid and assigned value. Even if the default value is as expected, it should be assigned with the default value for code clarity and to reduce confusion.

There are 1 instance(s) of this issue:


- src/mimswap/libraries/DecimalMath.sol
//@audit Variable `target` defined here, but never assigned
47	    function powFloor(uint256 target, uint256 e) internal pure returns (uint256) {
...
//@audit Variable returned here uninitialized
51	            return target;

GitHub : 51

[N-41]<a name="n-41"></a> Missing zero address check in initializer

There are 1 instance(s) of this issue:


- 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	    }

GitHub : 129..133

[N-42]<a name="n-42"></a> Events should be emitted before external calls

Ensure that events follow the best practice of check-effects-interaction, and are emitted before external calls

There are 38 instance(s) of this issue:


- src/blast/BlastBox.sol
// @audit functions: `nativeYieldTokens`, `enableTokenClaimable` called before this event
90	        emit LogTokenDepositEnabled(token, enabled);

GitHub : 90


- src/blast/BlastOnboarding.sol
// @audit functions: `safeTransferFrom` called before this event
120	        emit LogDeposit(msg.sender, token, amount, lock_);
...
// @audit functions: `safeTransfer` called before this event
140	        emit LogWithdraw(msg.sender, token, amount);
...
// @audit functions: `nativeYieldTokens`, `enableTokenClaimable` called before this event
182	        emit LogTokenSupported(token, supported);
...
// @audit functions: `safeTransfer` called before this event
211	        emit LogTokenRescue(token, to, amount);

GitHub : 120, 140, 182, 211


- src/blast/BlastOnboardingBoot.sol
// @audit functions: `stakeFor` called before this event
71	        emit LogClaimed(msg.sender, shares, lock);
...
// @audit functions: `safeApprove`, `safeApprove`, `createPool`, `setOperator`, `transferOwnership`, `safeApprove` called before this event
124	        emit LogLiquidityBootstrapped(pool, address(staking), totalPoolShares);
...
// @audit functions: `factory` called before this event
132	        emit LogInitialized(_router);

GitHub : 71, 124, 132


- src/blast/libraries/BlastYields.sol
// @audit functions: `getConfiguration`, `configure` called before this event
26	        emit LogBlastTokenClaimableEnabled(address(this), token);
...
// @audit functions: `configure` called before this event
31	        emit LogBlastNativeClaimableEnabled(address(this));

GitHub : 26, 31, 44, 57, 62, 72, 77


- src/mimswap/MagicLP.sol

GitHub : 259, 264, 282, 287, 323, 325, 345, 347, 352, 409, 454, 467


- src/mimswap/periphery/Factory.sol

GitHub : 88, 141, 152


- src/staking/LockingMultiRewards.sol

GitHub : 183, 331, 392, 447, 475, 513, 638, 651

[N-43]<a name="n-43"></a> Use @inheritdoc for overridden functions

It is recommended to use @inheritdoc for overridden functions.

There are 13 instance(s) of this issue:


- src/blast/BlastBox.sol
36	    function _onBeforeDeposit(
37	        IERC20 token,
38	        address /*from*/,
39	        address /*to*/,
40	        uint256 /*amount*/,
41	        uint256 /*share*/
42	    ) internal view override {
...
97	    /// @dev Called on DegenBox's constructor
98	    function _configure() internal override {
...
103	    function isOwner(address _account) internal view override returns (bool) {

GitHub : 36..42, 97..98, 103


- src/blast/BlastMagicLP.sol
36	    /// VIEWS
37	    //////////////////////////////////////////////////////////////////////////////////////
38	
39	    function version() external pure override returns (string memory) {
...
95	    /// INTERNALS
96	    //////////////////////////////////////////////////////////////////////////////////////
97	
98	    function _afterInitialized() internal override {

GitHub : 36..39, 95..98


- src/blast/BlastOnboarding.sol
223	    /// PROXY IMPLEMENTATION
224	    //////////////////////////////////////////////////////////////////////////////////////
225	
226	    function _implementation() internal view override returns (address) {

GitHub : 223..226


- src/blast/BlastWrappers.sol
59	    function init(bytes calldata data) public payable override {

GitHub : 59


- src/mimswap/MagicLP.sol
152	    /// VIEWS
153	    //////////////////////////////////////////////////////////////////////////////////////
154	
155	    function name() public view override returns (string memory) {
...
159	    function symbol() public pure override returns (string memory) {
...
163	    function decimals() public view override returns (uint8) {

GitHub : 152..155, 159, 163, 593


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 29, 37

[N-44]<a name="n-44"></a> Contract name does not follow the Solidity Style Guide

According to the Solidity Style Guide, contracts and libraries should be named using the CapWords style.

There are 7 instance(s) of this issue:


- src/blast/BlastMagicLP.sol
10	contract BlastMagicLP is MagicLP {
11	    event LogFeeToChanged(address indexed feeTo);

GitHub : 10..11


- src/blast/BlastOnboardingBoot.sol
23	contract BlastOnboardingBootDataV1 is BlastOnboardingData {
24	    address public pool;

GitHub : 23..24


- src/blast/BlastWrappers.sol
19	contract BlastMIMSwapRouter is Router {
20	    constructor(IWETH weth_, IFactory factory, address governor_) Router(weth_, factory) {
...
28	contract BlastMIMSwapFactory is Factory {
29	    constructor(
...
42	contract BlastCauldronV4 is CauldronV4 {
43	    error ErrInvalidGovernorAddress();

GitHub : 19..20, 28..29, 42..43


- src/mimswap/MagicLP.sol
25	contract MagicLP is ERC20, ReentrancyGuard, Owned {
26	    using Math for uint256;

GitHub : 25..26


- src/mimswap/libraries/PMMPricing.sol
20	library PMMPricing {
21	    enum RState {

GitHub : 20..21

[N-45]<a name="n-45"></a> Variable names for immutables should use UPPER_CASE_WITH_UNDERSCORES

For immutable variable names, each word should use all capital letters, with underscores separating each word (UPPER_CASE_WITH_UNDERSCORES)

There are 16 instance(s) of this issue:


- src/blast/BlastBox.sol
20	    BlastTokenRegistry public immutable registry;

GitHub : 20


- src/blast/BlastMagicLP.sol
17	    BlastTokenRegistry public immutable registry;

GitHub : 17


- src/blast/BlastWrappers.sol
45	    address private immutable _governor;

GitHub : 45


- src/mimswap/MagicLP.sol
61	    MagicLP public immutable implementation;

GitHub : 61


- src/mimswap/periphery/Router.sol
33	    IWETH public immutable weth;
...
34	    IFactory public immutable factory;

GitHub : 33, 34


- src/oracles/aggregators/MagicLpAggregator.sol
10	    IMagicLP public immutable pair;
...
11	    IAggregator public immutable baseOracle;
...
12	    IAggregator public immutable quoteOracle;
...
13	    uint8 public immutable baseDecimals;

GitHub : 10, 11, 12, 13, 14


- src/staking/LockingMultiRewards.sol

GitHub : 83, 84, 85, 86, 87

[N-46]<a name="n-46"></a> Use a single file for system wide constants

Consider grouping all the system constants under a single file.

There are 22 instance(s) of this issue:


- src/blast/BlastOnboardingBoot.sol
13	address constant USDB = 0x4300000000000000000000000000000000000003;
...
14	address constant MIM = 0x76DA31D7C9CbEAE102aff34D3398bC450c8374c1;
...
15	uint256 constant FEE_RATE = 0.0005 ether; // 0.05%
...
16	uint256 constant K = 0.00025 ether; // 0.00025, 1.25% price fluctuation, similar to A2000 in curve
...
17	uint256 constant I = 0.998 ether; // 1 MIM = 0.998 USDB
...
18	uint256 constant USDB_TO_MIN = 1.002 ether; // 1 USDB = 1.002 MIM
...
19	uint256 constant MIM_TO_MIN = I;

GitHub : 13, 14, 15, 16, 17, 18, 19


- src/blast/libraries/BlastPoints.sol
7	    address public constant BLAST_POINTS_OPERATOR = 0xD1025F1359422Ca16D9084908d629E0dBa60ff28;
...
8	    IBlastPoints public constant BLAST_POINTS = IBlastPoints(0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800);

GitHub : 7, 8


- src/blast/libraries/BlastYields.sol
14	    IBlast constant BLAST_YIELD_PRECOMPILE = IBlast(0x4300000000000000000000000000000000000002);

GitHub : 14


- src/mimswap/MagicLP.sol

GitHub : 63, 64, 65, 66


- src/mimswap/libraries/DecimalMath.sol

GitHub : 20, 21


- src/mimswap/periphery/Router.sol

GitHub : 31


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 16


- src/staking/LockingMultiRewards.sol

GitHub : 78, 79, 80, 81

[N-47]<a name="n-47"></a> Unsafe uint to int conversion

The int type in Solidity uses the two's complement system, so it is possible to accidentally overflow a very large uint to an int, even if they share the same number of bytes (e.g. a uint256 number > type(uint128).max will overflow a int256 cast).

Consider using the SafeCast library to prevent any overflows.

There are 1 instance(s) of this issue:


- src/oracles/aggregators/MagicLpAggregator.sol
45	        return int256(minAnswer * (baseReserve + quoteReserve) / pair.totalSupply());

GitHub : 45

[N-48]<a name="n-48"></a> Use SafeCast to safely downcast variables

Downcasting int/uints in Solidity can be unsafe due to the potential for data loss and unintended behavior. When downcasting a larger integer type to a smaller one (e.g., uint256 to uint128), the value may exceed the range of the target type, leading to truncation and loss of significant digits. This data loss can result in unexpected state changes, incorrect calculations, or other contract vulnerabilities, ultimately compromising the contracts functionality and reliability. To prevent these risks, developers should carefully consider the range of values their variables may hold and ensure that proper checks are in place to prevent out-of-range values before performing downcasting. Also consider using OZ SafeCast functionality.

There are 30 instance(s) of this issue:


- src/mimswap/MagicLP.sol
125	        _BLOCK_TIMESTAMP_LAST_ = uint32(block.timestamp % 2 ** 32);
...
139	        if (_RState_ == uint32(PMMPricing.RState.BELOW_ONE) && _BASE_RESERVE_ < _BASE_TARGET_) {
...
140	            _RState_ = uint32(PMMPricing.RState.ONE);
...
144	        if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_ < _QUOTE_TARGET_) {
...
145	            _RState_ = uint32(PMMPricing.RState.ONE);
...
256	        if (_RState_ != uint32(newRState)) {
...
258	            _RState_ = uint32(newRState);
...
279	        if (_RState_ != uint32(newRState)) {
...
281	            _RState_ = uint32(newRState);
...
320	            if (_RState_ != uint32(newRState)) {

GitHub : 125, 139, 140, 144, 145, 256, 258, 279, 281, 320, 322, 342, 344, 438, 439, 493, 494, 495, 513, 514, 517, 518, 536, 537, 538, 539, 540, 546


- src/staking/LockingMultiRewards.sol

GitHub : 389, 533

[N-49]<a name="n-49"></a> Consider adding a block/deny-list

Doing so will significantly increase centralization, but will help to prevent hackers from using stolen tokens

There are 5 instance(s) of this issue:


- src/blast/BlastGovernor.sol
7	contract BlastGovernor is OperatableV2 {
8	    event LogFeeToChanged(address indexed feeTo);

GitHub : 7..8


- src/blast/BlastOnboarding.sol
64	contract BlastOnboarding is BlastOnboardingData, Proxy {
65	    using SafeTransferLib for address;

GitHub : 64..65


- src/mimswap/MagicLP.sol
25	contract MagicLP is ERC20, ReentrancyGuard, Owned {
26	    using Math for uint256;

GitHub : 25..26


- src/mimswap/periphery/Router.sol
12	contract Router {
13	    using SafeTransferLib for address;

GitHub : 12..13


- src/staking/LockingMultiRewards.sol
14	contract LockingMultiRewards is OperatableV2, Pausable {
15	    using SafeTransferLib for address;

GitHub : 14..15

[N-50]<a name="n-50"></a> Expressions for constant values should use immutable rather than constant

While it does not save gas for some simple binary expressions because the compiler knows that developers often make this mistake, it's still best to use the right tool for the task at hand. There is a difference between constant variables and immutable variables, and they should each be used in their appropriate contexts. constants should be used for literal values written into the code, and immutable variables should be used for expressions, or values calculated in, or passed into the constructor.

There are 7 instance(s) of this issue:


- src/blast/BlastOnboardingBoot.sol
19	uint256 constant MIM_TO_MIN = I;

GitHub : 19


- src/blast/libraries/BlastPoints.sol
8	    IBlastPoints public constant BLAST_POINTS = IBlastPoints(0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800);

GitHub : 8


- src/blast/libraries/BlastYields.sol
14	    IBlast constant BLAST_YIELD_PRECOMPILE = IBlast(0x4300000000000000000000000000000000000002);

GitHub : 14


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

GitHub : 63, 64


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

GitHub : 20, 21

[N-51]<a name="n-51"></a> Lines are too long

Maximum suggested line length is 120 characters according to the documentation.

There are 54 instance(s) of this issue:


- src/blast/BlastGovernor.sol
53	    function execute(address to, uint256 value, bytes calldata data) external onlyOwner returns (bool success, bytes memory result) {

GitHub : 53


- src/blast/BlastMagicLP.sol
53	    function claimTokenYields() external onlyClones onlyImplementationOperators returns (uint256 token0Amount, uint256 token1Amount) {

GitHub : 53


- src/blast/BlastOnboarding.sol
101	    function deposit(address token, uint256 amount, bool lock_) external whenNotPaused onlyState(State.Opened) onlySupportedTokens(token) {
...
123	    function lock(address token, uint256 amount) external whenNotPaused onlyState(State.Opened) onlySupportedTokens(token) {

GitHub : 101, 123


- src/blast/BlastOnboardingBoot.sol
86	    function previewTotalPoolShares() external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
96	    function bootstrap(uint256 minAmountOut) external onlyOwner onlyState(State.Closed) returns (address, address, uint256) {

GitHub : 86, 96


- src/mimswap/MagicLP.sol
32	    event Swap(address fromToken, address toToken, uint256 fromAmount, uint256 toAmount, address trader, address receiver);
...
36	    event ParametersChanged(uint256 newLpFeeRate, uint256 newI, uint256 newK, uint256 newBaseReserve, uint256 newQuoteReserve);
...
156	        return string(abi.encodePacked("MagicLP ", IERC20Metadata(_BASE_TOKEN_).symbol(), "/", IERC20Metadata(_QUOTE_TOKEN_).symbol()));
...
170	    ) public view returns (uint256 receiveQuoteAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newBaseTarget) {
...
183	    ) public view returns (uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget) {
...
204	    function getPMMStateForCall() external view returns (uint256 i, uint256 K, uint256 B, uint256 Q, uint256 B0, uint256 Q0, uint256 R) {
...
290	    function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external nonReentrant {
...
310	            (uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget) = querySellQuote(
...
325	            emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo);
...
332	            (uint256 receiveQuoteAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newBaseTarget) = querySellBase(
...
347	            emit Swap(address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, assetTo);
...
360	    function buyShares(address to) external nonReentrant returns (uint256 shares, uint256 baseInput, uint256 quoteInput) {
...
381	            shares = quoteBalance < DecimalMath.mulFloor(baseBalance, _I_) ? DecimalMath.divFloor(quoteBalance, _I_) : baseBalance;
...
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();
...
439	        _QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_) - (uint256(_QUOTE_TARGET_) * shareAmount).divCeil(totalShares));

GitHub : 32, 36, 156, 170, 183, 204, 290, 310, 325, 332, 347, 360, 381, 402, 403, 439


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

GitHub : 34


- src/mimswap/libraries/Math.sol
51	    function _GeneralIntegrate(uint256 V0, uint256 V1, uint256 V2, uint256 i, uint256 k) internal pure returns (uint256) {
...
79	    function _SolveQuadraticFunctionForTarget(uint256 V1, uint256 delta, uint256 i, uint256 k) internal pure returns (uint256) {
...
131	    function _SolveQuadraticFunctionForTrade(uint256 V0, uint256 V1, uint256 delta, uint256 i, uint256 k) internal pure returns (uint256) {
...
184	        uint256 squareRoot = DecimalMath.mulFloor((DecimalMath.ONE - k) * 4, DecimalMath.mulFloor(k, V0) * V0); // 4(1-k)kQ0^2

GitHub : 51, 79, 131, 184


- src/mimswap/libraries/PMMPricing.sol
39	    function sellBaseToken(PMMState memory state, uint256 payBaseAmount) internal pure returns (uint256 receiveQuoteAmount, RState newR) {
...
55	                    // [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount
...
65	                receiveQuoteAmount = backToOneReceiveQuote + _ROneSellBaseToken(state, payBaseAmount - backToOnePayBase);
...
76	    function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount) internal pure returns (uint256 receiveBaseAmount, RState newR) {
...
96	                receiveBaseAmount = backToOneReceiveBase + _ROneSellQuoteToken(state, payQuoteAmount - backToOnePayQuote);
...
129	        return Math._SolveQuadraticFunctionForTrade(state.B0, state.B0, payQuoteAmount, DecimalMath.reciprocalFloor(state.i), state.K);
...
144	        return Math._GeneralIntegrate(state.Q0, state.Q + payQuoteAmount, state.Q, DecimalMath.reciprocalFloor(state.i), state.K);
...
185	        return Math._SolveQuadraticFunctionForTrade(state.B0, state.B, payQuoteAmount, DecimalMath.reciprocalFloor(state.i), state.K);

GitHub : 39, 55, 65, 76, 96, 129, 144, 185


- src/mimswap/periphery/Factory.sol
81	    function create(address baseToken_, address quoteToken_, uint256 lpFeeRate_, uint256 i_, uint256 k_) external returns (address clone) {
...
86	        IMagicLP(clone).init(address(baseToken_), address(quoteToken_), lpFeeRate_, address(maintainerFeeRateModel), i_, k_);

GitHub : 81, 86


- src/mimswap/periphery/Router.sol
88	        clone = IFactory(factory).create(useTokenAsQuote ? address(weth) : token, useTokenAsQuote ? token : address(weth), lpFeeRate, i, k);
...
101	        shares = quoteInAmount < DecimalMath.mulFloor(baseInAmount, i) ? DecimalMath.divFloor(quoteInAmount, i) : baseInAmount;
...
138	            shares = quoteBalance < DecimalMath.mulFloor(baseBalance, i) ? DecimalMath.divFloor(quoteBalance, i) : baseBalance;
...
169	    ) external ensureDeadline(deadline) returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
199	    ) external payable ensureDeadline(deadline) returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
251	    function previewRemoveLiquidity(address lp, uint256 sharesIn) external view returns (uint256 baseAmountOut, uint256 quoteAmountOut) {
...
523	            uint256 shares = quoteInAmount < DecimalMath.mulFloor(baseInAmount, i) ? DecimalMath.divFloor(quoteInAmount, i) : baseInAmount;
...
541	    function _swap(address to, address[] calldata path, uint256 directions, uint256 minimumOut) internal returns (uint256 amountOut) {

GitHub : 88, 101, 138, 169, 199, 251, 523, 541


- src/staking/LockingMultiRewards.sol
11	/// @author Based from Curve Finance's MultiRewards contract https://github.com/curvefi/multi-rewards/blob/master/contracts/MultiRewards.sol
...
12	/// @author Based from Ellipsis Finance's EpsStaker https://github.com/ellipsis-finance/ellipsis/blob/master/contracts/EpsStaker.sol
...
13	/// @author Based from Convex Finance's CvxLockerV2 https://github.com/convex-eth/platform/blob/main/contracts/contracts/CvxLockerV2.sol
...
109	    /// @param _lockingBoostMultiplerInBips The multiplier for the locking boost. 30000 means if you stake 100, you get 300 locked
...
252	    // |                             ^ lock starts (adjusted)                                    ^ unlock ends (nextUnlockTime)
...
277	    function _rewardPerToken(address rewardToken, uint256 lastTimeRewardApplicable_, uint256 totalSupply_) public view returns (uint256) {
...
292	    function _earned(address user, uint256 balance_, address rewardToken, uint256 rewardPerToken_) internal view returns (uint256) {
...
322	    /// @notice This function can recover any token except for the staking token beyond the balance necessary for rewards.
...
533	        _rewardData[token_].lastUpdateTime = uint248(lastTimeRewardApplicable_); // safe to cast as this will never overflow

GitHub : 11, 12, 13, 109, 252, 277, 292, 322, 533

proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2

There are 20 instance(s) of this issue:


- src/blast/BlastDapp.sol
// @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2
// @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastDapp.sol#L2) --- - src/blast/BlastBox.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

3 pragma solidity >=0.8.0;

*GitHub* : [3](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastBox.sol#L3) --- - src/blast/BlastGovernor.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L2) --- - src/blast/BlastMagicLP.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

3 pragma solidity >=0.8.0;

*GitHub* : [3](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L3) --- - src/blast/BlastOnboarding.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L2) --- - src/blast/BlastOnboardingBoot.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L2) --- - src/blast/BlastTokenRegistry.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastTokenRegistry.sol#L2) --- - src/blast/BlastWrappers.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastWrappers.sol#L2) --- - src/blast/libraries/BlastPoints.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/libraries/BlastPoints.sol#L2) --- - src/blast/libraries/BlastYields.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/libraries/BlastYields.sol#L2) --- - src/mimswap/MagicLP.sol *GitHub* : [8](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L8) --- - src/mimswap/auxiliary/FeeRateModel.sol *GitHub* : [8](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/auxiliary/FeeRateModel.sol#L8) --- - src/mimswap/auxiliary/FeeRateModelImpl.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/auxiliary/FeeRateModelImpl.sol#L2) --- - src/mimswap/libraries/DecimalMath.sol *GitHub* : [7](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/DecimalMath.sol#L7) --- - src/mimswap/libraries/Math.sol *GitHub* : [8](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/Math.sol#L8) --- - src/mimswap/libraries/PMMPricing.sol *GitHub* : [8](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/PMMPricing.sol#L8) --- - src/mimswap/periphery/Factory.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L2) --- - src/mimswap/periphery/Router.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L2) --- - src/oracles/aggregators/MagicLpAggregator.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/oracles/aggregators/MagicLpAggregator.sol#L2) --- - src/staking/LockingMultiRewards.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L2)

#0 - c4-pre-sort

2024-03-15T15:00:37Z

141345 marked the issue as sufficient quality report

#1 - 141345

2024-03-16T01:04:09Z

24 pfapostol l r nc 5 0 44

L 1 i L 2 l L 3 n L 4 n L 5 n L 6 l L 7 l L 8 n L 9 n L 10 n L 11 n

NC 1 i NC 2 n NC 3 n NC 4 n NC 5 n NC 6 n NC 7 n NC 8 n NC 9 i NC 10 n NC 11 i NC 12 n NC 13 n NC 14 n NC 15 n NC 16 l NC 17 n NC 18 i NC 19 i NC 20 n NC 21 n NC 22 n NC 23 i NC 24 n NC 25 n NC 26 n NC 27 n NC 28 n NC 29 i NC 30 n NC 31 n NC 32 n NC 33 n NC 34 i NC 35 n NC 36 l NC 37 n NC 38 n NC 39 n NC 40 n NC 41 i NC 42 n NC 43 n NC 44 i NC 45 i NC 46 n NC 47 n NC 48 n NC 49 n NC 50 n NC 51 i

#2 - c4-judge

2024-03-29T16:34:14Z

thereksfour marked the issue as grade-a

#3 - c4-judge

2024-04-06T06:59:39Z

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-b
sufficient quality report
G-07

Awards

24.8718 USDC - $24.87

External Links

The content section shows only 10 examples, subsequent cases are listed as links.

Summary

IssueInstancesGas Savings
[G-01]<array>.length Should Not Be Looked Up In Every Loop Of A For-loop7679
[G-02]Setting the constructor to payable16208
[G-03]++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops160
[G-04]Pre-increments and pre-decrements are cheaper than post-increments and post-decrements190
[G-05]Use assembly to check for address(0)29174
[G-06]Internal functions only called once can be inlined to save gas12360
[G-07]Usage of uint/int smaller than 32 bytes (256 bits) incurs overhead15330
[G-08]Functions guaranteed to revert when called by normal users can be marked payable40840
[G-09]>= costs less gas than >1648
[G-10]Multiple mappings can be replaced with a single struct mapping13260000
[G-11]State variables should be cached in stack variables rather than re-reading them from storage18618042
[G-12]Stack variable used as a cheaper cache for a state variable is only used once2472
[G-13]State Variables can be packed into fewer storage slots120000
[G-14]State variables access within a loop63180
[G-15]Using private for constants saves gas18151308
[G-16]Using bool for storage incurs overhead7119700
[G-17]State variables only set in the constructor should be declared immutable120000
[G-18]Consider activating via-ir for deploying10
[G-19]Function calls should be cached instead of re-calling the function14672
[G-20]Add unchecked blocks for divisions where the operands cannot overflow406360
[G-21]Multiplication/division by two should use bit shifting7140
[G-22]Consider using bytes32 rather than a string4512
[G-23]Optimize Zero Checks Using Assembly844872
[G-24]Consider using assembly to write address storage values if the address variable is mutable260
[G-25]Consider Caching Multiple Accesses to Mappings/Arrays222200
[G-26]Optimize Gas by Using Do-While Loops92295
[G-27]Use Assembly for Efficient Event Emission59106200
[G-28]Optimize External Calls with Assembly for Memory Efficiency12928380
[G-29]Use Assembly for Hash Calculations11005
[G-30]Consider Using Solady's Gas Optimized Lib for Math960
[G-31]Trade-offs Between Modifiers and Internal Functions991039500
[G-32]Optimize Boolean States with uint256(1/2)7119000
[G-33]Consider Packing Small uint When it's Possible8166400
[G-34]Avoid Unnecessary Public Variables711562000
[G-35]Optimize by Using Assembly for Low-Level Calls' Return Data1159
[G-36]Optimize Unsigned Integer Comparison With Zero1456
[G-37]Delete Unused State Variables27540000
[G-38]Optimize Gas by Using Only Named Returns622728
[G-39]Optimize Address Storage Value Management with assembly180
[G-40]Use calldata instead of memory for function arguments that do not get mutated130
[G-41]Gas savings can be achieved by changing the model for assigning value to the structure2520

[G-01]<a name="g-01"></a> <array>.length Should Not Be Looked Up In Every Loop Of A For-loop

The overheads outlined below are PER LOOP, excluding the first loop

storage arrays incur a Gwarmaccess (100 gas) memory arrays use MLOAD (3 gas) calldata arrays use CALLDATALOAD (3 gas)

Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset

There are 7 instance(s) of this issue:


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

GitHub : 165


- src/staking/LockingMultiRewards.sol
405	        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; ) {

GitHub : 405, 547, 561, 575, 580, 614

[G-02]<a name="g-02"></a> Setting the constructor to payable

Saves ~13 gas per instance

There are 16 instance(s) of this issue:


- src/blast/BlastDapp.sol
7	    constructor() {

GitHub : 7


- src/blast/BlastBox.sol
24	    constructor(IERC20 weth_, BlastTokenRegistry registry_, address feeTo_) DegenBox(weth_) {

GitHub : 24


- src/blast/BlastGovernor.sol
15	    constructor(address feeTo_, address _owner) OperatableV2(_owner) {

GitHub : 15


- src/blast/BlastMagicLP.sol
23	    constructor(BlastTokenRegistry registry_, address feeTo_, address owner_) MagicLP(owner_) {

GitHub : 23


- src/blast/BlastOnboarding.sol
58	    constructor() Owned(msg.sender) {
...
84	    constructor(BlastTokenRegistry registry_, address feeTo_) {

GitHub : 58, 84


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

GitHub : 12


- src/blast/BlastWrappers.sol
20	    constructor(IWETH weth_, IFactory factory, address governor_) Router(weth_, factory) {
...
29	    constructor(
30	        address implementation_,
31	        IFeeRateModel maintainerFeeRateModel_,
32	        address owner_,
33	        address governor_
34	    ) Factory(implementation_, maintainerFeeRateModel_, owner_) {
...
47	    constructor(address box_, address mim_, address governor_) CauldronV4(IBentoBoxV1(box_), IERC20(mim_)) {

GitHub : 20, 29..34, 47


- src/mimswap/MagicLP.sol

GitHub : 84


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 22


- src/mimswap/periphery/Factory.sol

GitHub : 38


- src/mimswap/periphery/Router.sol

GitHub : 38


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 21


- src/staking/LockingMultiRewards.sol

GitHub : 112..118

[G-03]<a name="g-03"></a> ++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop

There are 1 instance(s) of this issue:


- 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	        }

GitHub : 165..172

[G-04]<a name="g-04"></a> Pre-increments and pre-decrements are cheaper than post-increments and post-decrements

Saves 5 gas per iteration

There are 1 instance(s) of this issue:


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

GitHub : 165

[G-05]<a name="g-05"></a> Use assembly to check for address(0)

There are 29 instance(s) of this issue:


- src/blast/BlastBox.sol
25	        if (feeTo_ == address(0)) {
...
28	        if (address(registry_) == address(0)) {
...
73	        if (feeTo_ == address(0)) {

GitHub : 25, 28, 73


- src/blast/BlastGovernor.sol
16	        if (feeTo_ == address(0)) {
...
41	        if(_feeTo == address(0)) {

GitHub : 16, 41


- src/blast/BlastMagicLP.sol
24	        if (feeTo_ == address(0)) {
...
27	        if (address(registry_) == address(0)) {
...
81	        if (feeTo_ == address(0)) {

GitHub : 24, 27, 81


- src/blast/BlastOnboarding.sol
85	        if (address(registry_) == address(0)) {
...
89	        if (feeTo_ == address(0)) {

GitHub : 85, 89, 148


- src/blast/BlastOnboardingBoot.sol

GitHub : 97


- src/blast/BlastTokenRegistry.sol

GitHub : 15


- src/blast/BlastWrappers.sol

GitHub : 21, 35, 48


- src/mimswap/MagicLP.sol

GitHub : 102, 102, 102


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 23, 35, 47


- src/mimswap/periphery/Factory.sol

GitHub : 39, 42, 97, 106


- src/mimswap/periphery/Router.sol

GitHub : 39, 39


- src/staking/LockingMultiRewards.sol

GitHub : 301

[G-06]<a name="g-06"></a> Internal functions only called once can be inlined to save gas

There are 12 instance(s) of this issue:


- src/blast/libraries/BlastYields.sol
42	    function claimMaxGasYields(address contractAddress, address recipient) internal returns (uint256 amount) {
...
55	    function claimAllNativeYields(address contractAddress, address recipient) internal returns (uint256 amount) {

GitHub : 42, 55


- src/mimswap/MagicLP.sol
528	    function _resetTargetAndReserve() internal returns (uint256 baseBalance, uint256 quoteBalance) {
...
601	    function _afterInitialized() internal virtual {}

GitHub : 528, 601


- src/mimswap/libraries/DecimalMath.sol
47	    function powFloor(uint256 target, uint256 e) internal pure returns (uint256) {

GitHub : 47


- src/mimswap/libraries/PMMPricing.sol
134	    function _RBelowSellQuoteToken(
135	        PMMState memory state,
136	        uint256 payQuoteAmount
137	    )
138	        internal
139	        pure
140	        returns (
141	            uint256 // receiveBaseToken
142	        )
143	    {
...
147	    function _RBelowSellBaseToken(
148	        PMMState memory state,
149	        uint256 payBaseAmount
150	    )
151	        internal
152	        pure
153	        returns (
154	            uint256 // receiveQuoteToken
155	        )
156	    {
...
162	    function _RAboveSellBaseToken(
163	        PMMState memory state,
164	        uint256 payBaseAmount
165	    )
166	        internal
167	        pure
168	        returns (
169	            uint256 // receiveQuoteToken
170	        )
171	    {
...
175	    function _RAboveSellQuoteToken(
176	        PMMState memory state,
177	        uint256 payQuoteAmount
178	    )
179	        internal
180	        pure
181	        returns (
182	            uint256 // receiveBaseToken
183	        )
184	    {

GitHub : 134..143, 147..156, 162..171, 175..184


- src/oracles/aggregators/MagicLpAggregator.sol
33	    function _getReserves() internal view virtual returns (uint256, uint256) {

GitHub : 33


- src/staking/LockingMultiRewards.sol

GitHub : 544, 572

[G-07]<a name="g-07"></a> Usage of uint/int 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.

Each 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.

https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html

Use a larger size then downcast where needed.

There are 15 instance(s) of this issue:


- src/mimswap/MagicLP.sol
72	    uint112 public _BASE_RESERVE_;
...
73	    uint112 public _QUOTE_RESERVE_;
...
74	    uint32 public _BLOCK_TIMESTAMP_LAST_;
...
76	    uint112 public _BASE_TARGET_;
...
77	    uint112 public _QUOTE_TARGET_;
...
78	    uint32 public _RState_;
...
163	    function decimals() public view override returns (uint8) {
...
546	        uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32);
...
547	        uint32 timeElapsed = blockTimestamp - _BLOCK_TIMESTAMP_LAST_;

GitHub : 72, 73, 74, 76, 77, 78, 163, 546, 547


- src/mimswap/periphery/Router.sol
598	    function _validateDecimals(uint8 baseDecimals, uint8 quoteDecimals) internal pure {

GitHub : 598, 598


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 29, 48, 48


- src/staking/LockingMultiRewards.sol

GitHub : 55

[G-08]<a name="g-08"></a> Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost

There are 40 instance(s) of this issue:


- src/blast/BlastBox.sol
68	    function callBlastPrecompile(bytes calldata data) external onlyOwner {
...
72	    function setFeeTo(address feeTo_) external onlyOwner {
...
81	    function setTokenEnabled(address token, bool enabled) external onlyOwner {

GitHub : 68, 72, 81


- src/blast/BlastGovernor.sol
40	    function setFeeTo(address _feeTo) external onlyOwner {
...
49	    function callBlastPrecompile(bytes calldata data) external onlyOwner {
...
53	    function execute(address to, uint256 value, bytes calldata data) external onlyOwner returns (bool success, bytes memory result) {

GitHub : 40, 49, 53


- src/blast/BlastMagicLP.sol
72	    function callBlastPrecompile(bytes calldata data) external onlyClones onlyImplementationOwner {
...
80	    function setFeeTo(address feeTo_) external onlyImplementation onlyImplementationOwner {
...
89	    function setOperator(address operator, bool status) external onlyImplementation onlyImplementationOwner {

GitHub : 72, 80, 89


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

GitHub : 147, 156, 160, 164, 175, 185, 190, 195, 200, 205, 214, 218


- src/blast/BlastOnboardingBoot.sol

GitHub : 96, 129, 137, 146


- src/blast/BlastTokenRegistry.sol

GitHub : 14


- src/mimswap/MagicLP.sol

GitHub : 461, 470..479, 504


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 46, 57


- src/mimswap/periphery/Factory.sol

GitHub : 96, 105, 116, 120..126


- src/staking/LockingMultiRewards.sol

GitHub : 300, 317, 324, 337, 341

[G-09]<a name="g-09"></a> >= costs less gas than >

The compiler uses opcodes GT and ISZERO for solidity code that uses >, but only requires LT for >=, which saves 3 gas. Similarly for <=.

There are 16 instance(s) of this issue:


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

GitHub : 114


- src/mimswap/MagicLP.sol
294	        if (data.length > 0) {
...
395	        } else if (baseReserve > 0 && quoteReserve > 0) {
...
395	        } else if (baseReserve > 0 && quoteReserve > 0) {
...
450	        if (data.length > 0) {
...
549	        if (timeElapsed > 0 && _BASE_RESERVE_ != 0 && _QUOTE_RESERVE_ != 0) {
...
582	        if (amount > 0) {
...
588	        if (amount > 0) {

GitHub : 294, 395, 395, 450, 549, 582, 588


- src/mimswap/libraries/Math.sol
22	        if (remainder > 0) {

GitHub : 22


- src/mimswap/periphery/Router.sol
147	        } else if (baseReserve > 0 && quoteReserve > 0) {

GitHub : 147, 147, 527, 527, 590


- src/staking/LockingMultiRewards.sol

GitHub : 417, 636

[G-10]<a name="g-10"></a> Multiple mappings can be replaced with a single struct mapping

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.

There are 13 instance(s) of this issue:


- src/blast/BlastOnboarding.sol
36	    mapping(address token => bool) public supportedTokens;
...
37	    mapping(address token => Balances) public totals;
...
38	    mapping(address token => uint256 cap) public caps;
...
41	    mapping(address user => mapping(address token => Balances)) public balances;

GitHub : 36, 37, 38, 41


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

GitHub : 35, 36


- 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;

GitHub : 89, 90, 91, 92, 94, 95, 96

[G-11]<a name="g-11"></a> State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. 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.

There are 186 instance(s) of this issue:


- src/blast/BlastMagicLP.sol
// @audit State variable `_BASE_TOKEN_` called 2 times in one function
56	        if (registry.nativeYieldTokens(_BASE_TOKEN_)) {
...
// @audit State variable `_BASE_TOKEN_` called 2 times in one function
57	            token0Amount = BlastYields.claimAllTokenYields(_BASE_TOKEN_, feeTo_);
...
// @audit State variable `_QUOTE_TOKEN_` called 2 times in one function
59	        if (registry.nativeYieldTokens(_QUOTE_TOKEN_)) {
...
// @audit State variable `_QUOTE_TOKEN_` called 2 times in one function
60	            token1Amount = BlastYields.claimAllTokenYields(_QUOTE_TOKEN_, feeTo_);
...
// @audit State variable `nativeYieldTokens` called 2 times in one function
56	        if (registry.nativeYieldTokens(_BASE_TOKEN_)) {
...
// @audit State variable `nativeYieldTokens` called 2 times in one function
59	        if (registry.nativeYieldTokens(_QUOTE_TOKEN_)) {

GitHub : 56, 57, 59, 60, 56, 59


- src/blast/BlastMagicLP.sol
// @audit State variable `_BASE_TOKEN_` called 2 times in one function
105	        if (registry.nativeYieldTokens(_BASE_TOKEN_)) {
...
// @audit State variable `_BASE_TOKEN_` called 2 times in one function
106	            BlastYields.enableTokenClaimable(_BASE_TOKEN_);
...
// @audit State variable `_QUOTE_TOKEN_` called 2 times in one function
109	        if (registry.nativeYieldTokens(_QUOTE_TOKEN_)) {
...
// @audit State variable `_QUOTE_TOKEN_` called 2 times in one function
110	            BlastYields.enableTokenClaimable(_QUOTE_TOKEN_);

GitHub : 105, 106, 109, 110, 105, 109


- src/blast/BlastOnboarding.sol

GitHub : 106, 109, 118, 114, 114, 105, 108, 112, 114


- src/blast/BlastOnboarding.sol

GitHub : 124, 125, 126, 127


- src/blast/BlastOnboarding.sol

GitHub : 133, 134, 135, 136


- src/blast/BlastOnboardingBoot.sol

GitHub : 59, 68


- src/blast/BlastOnboardingBoot.sol

GitHub : 87, 88


- src/blast/BlastOnboardingBoot.sol

GitHub : 97, 106, 117, 122, 124, 126, 103, 104, 106, 117, 118, 119, 122, 124, 126, 106, 108, 122, 124, 126, 101, 102


- src/blast/BlastOnboardingBoot.sol

GitHub : 130, 131


- src/blast/BlastOnboardingBoot.sol

GitHub : 147, 148


- src/blast/BlastOnboardingBoot.sol

GitHub : 162, 162, 156, 156


- src/mimswap/MagicLP.sol

GitHub : 99, 118


- src/mimswap/MagicLP.sol

GitHub : 139, 141, 146, 139, 141, 146, 142, 144, 147, 142, 144, 147, 139, 140, 144, 145


- src/mimswap/MagicLP.sol

GitHub : 245, 264, 262, 264, 256, 258


- src/mimswap/MagicLP.sol

GitHub : 285, 287, 268, 287, 279, 281


- src/mimswap/MagicLP.sol

GitHub : 302, 308, 315, 331, 298, 325, 347, 319, 341, 302, 309, 330, 337, 299, 325, 347, 320, 322, 342, 344


- src/mimswap/MagicLP.sol

GitHub : 382, 402, 402, 402, 381, 381, 383, 383, 385, 403, 403, 403


- src/mimswap/MagicLP.sol

GitHub : 438, 438, 438, 439, 439, 439


- src/mimswap/MagicLP.sol

GitHub : 512, 513, 514, 513, 513, 516, 517, 518, 517, 517


- src/mimswap/MagicLP.sol

GitHub : 547, 557


- src/mimswap/MagicLP.sol

GitHub : 571, 572, 574, 575


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 35, 39


- src/mimswap/periphery/Factory.sol

GitHub : 86, 88


- src/staking/LockingMultiRewards.sol

GitHub : 279, 282, 283, 285


- src/staking/LockingMultiRewards.sol

GitHub : 305, 314, 309, 313


- src/staking/LockingMultiRewards.sol

GitHub : 318, 319


- src/staking/LockingMultiRewards.sol

GitHub : 362, 369


- src/staking/LockingMultiRewards.sol

GitHub : 417, 430, 431


- src/staking/LockingMultiRewards.sol

GitHub : 483, 490, 501, 510, 482, 502


- src/staking/LockingMultiRewards.sol

GitHub : 532, 533


- src/staking/LockingMultiRewards.sol

GitHub : 547, 548


- src/staking/LockingMultiRewards.sol

GitHub : 561, 562


- src/staking/LockingMultiRewards.sol

GitHub : 575, 576


- src/staking/LockingMultiRewards.sol

GitHub : 598, 648, 614, 615, 616, 619

[G-12]<a name="g-12"></a> 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 24 instance(s) of this issue:


- src/blast/BlastMagicLP.sol
47	    function claimGasYields() external onlyClones onlyImplementationOperators returns (uint256) {

GitHub : 47


- src/blast/BlastOnboardingBoot.sol
86	    function previewTotalPoolShares() external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
96	    function bootstrap(uint256 minAmountOut) external onlyOwner onlyState(State.Closed) returns (address, address, uint256) {
...
155	    function _claimable(address user) internal view returns (uint256 shares) {

GitHub : 86, 96, 155


- src/mimswap/MagicLP.sol
167	    function querySellBase(
168	        address trader,
169	        uint256 payBaseAmount
170	    ) public view returns (uint256 receiveQuoteAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newBaseTarget) {
...
180	    function querySellQuote(
181	        address trader,
182	        uint256 payQuoteAmount
183	    ) public view returns (uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget) {
...
290	    function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external nonReentrant {
...
413	    function sellShares(
414	        uint256 shareAmount,
415	        address to,
416	        uint256 baseMinAmount,
417	        uint256 quoteMinAmount,
418	        bytes calldata data,
419	        uint256 deadline
420	    ) external nonReentrant returns (uint256 baseAmount, uint256 quoteAmount) {
...
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 {

GitHub : 167..170, 180..183, 290, 413..420, 470..479


- src/mimswap/libraries/Math.sol
19	    function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {

GitHub : 19, 51, 79, 131


- src/mimswap/periphery/Factory.sol

GitHub : 81


- src/mimswap/periphery/Router.sol

GitHub : 251, 509..513


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 37


- src/staking/LockingMultiRewards.sol

GitHub : 277, 292, 479, 544, 557, 572, 597

[G-13]<a name="g-13"></a> State Variables can be packed into fewer storage slots

Each slot saved can avoid an extra Gsset (21000 gas). Subsequent reads as well as writes have smaller gas savings

There are 1 instance(s) of this issue:


- src/blast/BlastOnboardingBoot.sol
23	contract BlastOnboardingBootDataV1 is BlastOnboardingData {
24	    address public pool;

GitHub : 23..24

[G-14]<a name="g-14"></a> State variables access within a loop

State variable reads and writes are more expensive than local variable reads and writes. Therefore, it is recommended to replace state variable reads and writes within loops with a local variable. Gas savings should be multiplied by the average loop length.

There are 6 instance(s) of this issue:


- 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	        }

GitHub : 165..172


- 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	        }
...
547	        for (uint256 i; i < rewardTokens.length; ) {
548	            _updateRewardsGlobal(rewardTokens[i], totalSupply_);
549	            unchecked {
550	                ++i;
551	            }
552	        }
...
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	        }
...
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	        }
...
614	        for (uint256 i; i < rewardTokens.length; ) {
615	            address rewardToken = rewardTokens[i];
616	            uint256 rewardAmount = rewards[user][rewardToken];
617	
618	            // in all scenario, reset the reward amount immediately
619	            rewards[user][rewardToken] = 0;
620	
621	            // don't assume the rewardTokens array is always the same length as the items array
622	            // as new reward tokens can be added by the owner
623	            if (i < rewardItemLength) {
624	                RewardLockItem storage item = _rewardLock.items[i];
625	
626	                // expired lock, claim existing unlocked rewards if any
627	                if (expired) {
628	                    uint256 amount = item.amount;
629	
630	                    // since this current lock is expired and that item index
631	                    // matches the reward index, override the current amount
632	                    // with the new locked amount.
633	                    item.amount = rewardAmount;
634	
635	                    // use cached amount
636	                    if (amount > 0) {
637	                        rewardToken.safeTransfer(user, amount);
638	                        emit LogRewardPaid(user, rewardToken, amount);
639	                    }
640	                } else {
641	                    // not expired, just add to the existing lock
642	                    item.amount += rewardAmount;
643	                }
644	            }
645	            // new reward token, create a new lock item
646	            // could mean it's adding to an existing lock or creating a new one
647	            else {
648	                _userRewardLock[user].items.push(RewardLockItem({token: rewardToken, amount: rewardAmount}));
649	            }
650	
651	            emit LogRewardLocked(user, rewardToken, rewardAmount);
652	
653	            unchecked {
654	                ++i;
655	            }
656	        }

GitHub : 405..452, 547..552, 561..568, 575..592, 614..656

[G-15]<a name="g-15"></a> Using private for constants saves gas

Saves deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table.

There are 18 instance(s) of this issue:


- src/blast/BlastOnboardingBoot.sol
13	address constant USDB = 0x4300000000000000000000000000000000000003;
...
14	address constant MIM = 0x76DA31D7C9CbEAE102aff34D3398bC450c8374c1;
...
15	uint256 constant FEE_RATE = 0.0005 ether; // 0.05%
...
16	uint256 constant K = 0.00025 ether; // 0.00025, 1.25% price fluctuation, similar to A2000 in curve
...
17	uint256 constant I = 0.998 ether; // 1 MIM = 0.998 USDB
...
18	uint256 constant USDB_TO_MIN = 1.002 ether; // 1 USDB = 1.002 MIM
...
19	uint256 constant MIM_TO_MIN = I;

GitHub : 13, 14, 15, 16, 17, 18, 19


- src/blast/libraries/BlastPoints.sol
7	    address public constant BLAST_POINTS_OPERATOR = 0xD1025F1359422Ca16D9084908d629E0dBa60ff28;
...
8	    IBlastPoints public constant BLAST_POINTS = IBlastPoints(0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800);

GitHub : 7, 8


- src/blast/libraries/BlastYields.sol
14	    IBlast constant BLAST_YIELD_PRECOMPILE = IBlast(0x4300000000000000000000000000000000000002);

GitHub : 14


- src/mimswap/MagicLP.sol

GitHub : 63, 64, 65, 66


- src/mimswap/libraries/DecimalMath.sol

GitHub : 20, 21


- src/mimswap/periphery/Router.sol

GitHub : 31


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 16

[G-16]<a name="g-16"></a> Using bool for storage incurs overhead

Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from false to true, after having been true in the past. See source.

There are 7 instance(s) of this issue:


- src/blast/BlastBox.sol
21	    mapping(address => bool) public enabledTokens;

GitHub : 21


- src/blast/BlastMagicLP.sol
21	    mapping(address => bool) public operators;

GitHub : 21


- src/blast/BlastOnboarding.sol
36	    mapping(address token => bool) public supportedTokens;

GitHub : 36


- src/blast/BlastOnboardingBoot.sol
28	    bool public ready;
...
30	    mapping(address user => bool claimed) public claimed;

GitHub : 28, 30


- src/blast/BlastTokenRegistry.sol
10	    mapping(address => bool) public nativeYieldTokens;

GitHub : 10


- src/mimswap/MagicLP.sol
68	    bool internal _INITIALIZED_;

GitHub : 68

[G-17]<a name="g-17"></a> State variables only set in the constructor should be declared immutable

Accessing state variables within a function involves an SLOAD operation, but immutable variables can be accessed directly without the need of it, thus reducing gas costs. As these state variables are assigned only in the constructor, consider declaring them immutable.

There are 1 instance(s) of this issue:


- src/blast/BlastOnboarding.sol
33	    BlastTokenRegistry public registry;

GitHub : 33

[G-18]<a name="g-18"></a> Consider activating via-ir for deploying

The IR-based code generator was developed to make code generation more performant by enabling optimization passes that can be applied across functions.

It is possible to activate the IR-based code generator through the command line by using the flag --via-ir or by including the option {"viaIR": true}.

Keep in mind that compiling with this option may take longer. However, you can simply test it before deploying your code. If you find that it provides better performance, you can add the --via-ir flag to your deploy command.

There are 1 instance(s) of this issue:

GitHub : project\2024-03-abracadabra-money

[G-19]<a name="g-19"></a> Function calls should be cached instead of re-calling the function

Consider caching the result instead of re-calling the function when possible. Note: this also includes casts, which cost between 42-46 gas, depending on the type.

There are 14 instance(s) of this issue:


- src/mimswap/MagicLP.sol
290	    function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external nonReentrant {
...
360	    function buyShares(address to) external nonReentrant returns (uint256 shares, uint256 baseInput, uint256 quoteInput) {
...
413	    function sellShares(
414	        uint256 shareAmount,
415	        address to,
416	        uint256 baseMinAmount,
417	        uint256 quoteMinAmount,
418	        bytes calldata data,
419	        uint256 deadline
420	    ) external nonReentrant returns (uint256 baseAmount, uint256 quoteAmount) {

GitHub : 290, 360, 413..420


- src/mimswap/libraries/Math.sol
131	    function _SolveQuadraticFunctionForTrade(uint256 V0, uint256 V1, uint256 delta, uint256 i, uint256 k) internal pure returns (uint256) {

GitHub : 131


- src/mimswap/libraries/PMMPricing.sol
203	    function getMidPrice(PMMState memory state) internal pure returns (uint256) {

GitHub : 203


- src/mimswap/periphery/Router.sol
73	    function createPoolETH(
74	        address token,
75	        bool useTokenAsQuote,
76	        uint256 lpFeeRate,
77	        uint256 i,
78	        uint256 k,
79	        address to,
80	        uint256 tokenInAmount
81	    ) external payable returns (address clone, uint256 shares) {
...
112	    function previewAddLiquidity(
113	        address lp,
114	        uint256 baseInAmount,
115	        uint256 quoteInAmount
116	    ) external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
192	    function addLiquidityETH(
193	        address lp,
194	        address to,
195	        address payable refundTo,
196	        uint256 tokenInAmount,
197	        uint256 minimumShares,
198	        uint256 deadline
199	    ) external payable ensureDeadline(deadline) returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
229	    function addLiquidityETHUnsafe(
230	        address lp,
231	        address to,
232	        uint256 tokenInAmount,
233	        uint256 minimumShares,
234	        uint256 deadline
235	    ) external payable ensureDeadline(deadline) returns (uint256 shares) {
...
251	    function previewRemoveLiquidity(address lp, uint256 sharesIn) external view returns (uint256 baseAmountOut, uint256 quoteAmountOut) {

GitHub : 73..81, 112..116, 192..199, 229..235, 251, 274..281, 314..321, 365..372, 509..513

[G-20]<a name="g-20"></a> Add unchecked blocks for divisions where the operands cannot overflow

uint divisions can't overflow, while int divisions can overflow only in one specific case.

Consider adding an unchecked block to have some gas savings.

There are 40 instance(s) of this issue:


- src/blast/BlastOnboardingBoot.sol
163	        return (userLocked * totalPoolShares) / totalLocked;

GitHub : 163


- src/mimswap/MagicLP.sol
435	        baseAmount = (baseBalance * shareAmount) / totalShares;
...
436	        quoteAmount = (quoteBalance * shareAmount) / totalShares;
...
513	            _BASE_TARGET_ = uint112((uint256(_BASE_TARGET_) * baseBalance) / uint256(_BASE_RESERVE_));
...
517	            _QUOTE_TARGET_ = uint112((uint256(_QUOTE_TARGET_) * quoteBalance) / uint256(_QUOTE_RESERVE_));

GitHub : 435, 436, 513, 517


- src/mimswap/libraries/DecimalMath.sol
24	        return (target * d) / ONE;
...
32	        return (target * ONE) / d;
...
40	        return ONE2 / target;
...
53	            uint p = powFloor(target, e / 2);
...
54	            p = (p * p) / ONE;

GitHub : 24, 32, 40, 53, 54, 56


- src/mimswap/libraries/Math.sol

GitHub : 20, 30, 34, 34, 59, 62, 64, 96, 99, 97, 155, 158, 158, 156, 160, 170, 181


- src/mimswap/libraries/PMMPricing.sol

GitHub : 209, 205


- src/mimswap/periphery/Router.sol

GitHub : 257, 258


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 45


- src/staking/LockingMultiRewards.sol

GitHub : 144, 236, 241, 258, 283, 294, 388

[G-21]<a name="g-21"></a> Multiplication/division by two should use bit shifting

X * 2 is equivalent to X << 1 and X / 2 is the same as X >> 1.

The MUL and DIV opcodes cost 5 gas, whereas SHL and SHR only cost 3 gas.

There are 7 instance(s) of this issue:


- src/mimswap/libraries/DecimalMath.sol
53	            uint p = powFloor(target, e / 2);

GitHub : 53


- src/mimswap/libraries/Math.sol
30	        uint256 z = x / 2 + 1;
...
34	            z = (x / z + z) / 2;
...
93	        uint256 ki = (4 * k) * i;
...
101	        uint256 premium = DecimalMath.divFloor(_sqrt - DecimalMath.ONE, k * 2) + DecimalMath.ONE;
...
184	        uint256 squareRoot = DecimalMath.mulFloor((DecimalMath.ONE - k) * 4, DecimalMath.mulFloor(k, V0) * V0); // 4(1-k)kQ0^2
...
188	        uint256 denominator = (DecimalMath.ONE - k) * 2; // 2(1-k)

GitHub : 30, 34, 93, 101, 184, 188

[G-22]<a name="g-22"></a> Consider using bytes32 rather than a string

Using the bytes types for fixed-length strings is more efficient than having the EVM have to incur the overhead of string processing. Consider whether the value needs to be a string. A good reason to keep it as a string would be if the variable is defined in an interface that this project does not own.

There are 4 instance(s) of this issue:


- src/blast/BlastMagicLP.sol
39	    function version() external pure override returns (string memory) {

GitHub : 39


- src/mimswap/MagicLP.sol
155	    function name() public view override returns (string memory) {
...
159	    function symbol() public pure override returns (string memory) {
...
236	    function version() external pure virtual returns (string memory) {

GitHub : 155, 159, 236

[G-23]<a name="g-23"></a> Optimize Zero Checks Using Assembly

The usage of inline assembly to check if variable is the zero can save gas compared to traditional require or if statement checks.

The assembly check uses the extcodesize operation which is generally cheaper in terms of gas.

More information can be found here.

There are 84 instance(s) of this issue:


- src/blast/BlastBox.sol
25	        if (feeTo_ == address(0)) {
...
28	        if (address(registry_) == address(0)) {
...
73	        if (feeTo_ == address(0)) {

GitHub : 25, 28, 73


- src/blast/BlastGovernor.sol
16	        if (feeTo_ == address(0)) {
...
41	        if(_feeTo == address(0)) {

GitHub : 16, 41


- src/blast/BlastMagicLP.sol
24	        if (feeTo_ == address(0)) {
...
27	        if (address(registry_) == address(0)) {
...
81	        if (feeTo_ == address(0)) {

GitHub : 24, 27, 81


- src/blast/BlastOnboarding.sol
85	        if (address(registry_) == address(0)) {
...
89	        if (feeTo_ == address(0)) {

GitHub : 85, 89, 114, 148


- src/blast/BlastOnboardingBoot.sol

GitHub : 64, 97, 158


- src/blast/BlastTokenRegistry.sol

GitHub : 15


- src/blast/BlastWrappers.sol

GitHub : 21, 35, 48


- src/mimswap/MagicLP.sol

GitHub : 102, 102, 102, 108, 294, 369, 375, 395, 395, 377, 385, 450, 483, 549, 549, 549, 582, 588


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 23, 35, 47


- src/mimswap/libraries/DecimalMath.sol

GitHub : 48


- src/mimswap/libraries/Math.sol

GitHub : 22, 52, 58, 80, 89, 94, 132, 136, 140, 153, 192


- src/mimswap/periphery/Factory.sol

GitHub : 39, 42, 97, 106


- src/mimswap/periphery/Router.sol

GitHub : 39, 39, 125, 131, 147, 147, 132, 327, 348, 379, 392, 521, 527, 527, 545, 560, 593, 599, 599


- src/staking/LockingMultiRewards.sol

GitHub : 131, 156, 171, 278, 301, 410, 459, 490, 636

[G-24]<a name="g-24"></a> Consider using assembly to write address storage values if the address variable is mutable

Writing address storage values using assembly can be more gas efficient when the address variable is mutable. The following instances show mutable address storage variables that could be optimized using assembly.

There are 26 instance(s) of this issue:


- src/blast/BlastBox.sol
33	        feeTo = feeTo_;
...
77	        feeTo = feeTo_;

GitHub : 33, 77


- src/blast/BlastGovernor.sol
20	        feeTo = feeTo_;
...
45	        feeTo = _feeTo;

GitHub : 20, 45


- src/blast/BlastMagicLP.sol
32	        feeTo = feeTo_;
...
85	        feeTo = feeTo_;

GitHub : 32, 85


- src/blast/BlastOnboarding.sol
94	        feeTo = feeTo_;
...
152	        feeTo = feeTo_;
...
191	        bootstrapper = bootstrapper_;

GitHub : 94, 152, 191


- src/mimswap/MagicLP.sol
119	        _BASE_TOKEN_ = baseTokenAddress;

GitHub : 119, 120


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 27, 51, 58


- src/mimswap/periphery/Factory.sol

GitHub : 45, 85, 101


- src/mimswap/periphery/Router.sol

GitHub : 66, 88, 204, 238, 286, 351, 349, 382, 380

[G-25]<a name="g-25"></a> Consider Caching Multiple Accesses to Mappings/Arrays

Leveraging a local variable to cache these values when accessed more than once can yield a gas saving of approximately 42 units per access. This reduction is attributed to eliminating the need for recalculating the key's keccak256 hash (which costs Gkeccak256 - 30 gas) and the associated stack operations. For arrays, this also prevents the overhead of re-computing offsets in memory or calldata.

There are 22 instance(s) of this issue:


- src/blast/BlastOnboarding.sol
101	    function deposit(address token, uint256 amount, bool lock_) external whenNotPaused onlyState(State.Opened) onlySupportedTokens(token) {
...
123	    function lock(address token, uint256 amount) external whenNotPaused onlyState(State.Opened) onlySupportedTokens(token) {
...
132	    function withdraw(address token, uint256 amount) external whenNotPaused onlySupportedTokens(token) {

GitHub : 101, 123, 132


- src/blast/BlastOnboardingBoot.sol
55	    function claim(bool lock) external returns (uint256 shares) {
...
86	    function previewTotalPoolShares() external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
96	    function bootstrap(uint256 minAmountOut) external onlyOwner onlyState(State.Closed) returns (address, address, uint256) {
...
155	    function _claimable(address user) internal view returns (uint256 shares) {

GitHub : 55, 86, 96, 155


- src/mimswap/periphery/Factory.sol
53	    function getPoolCount(address token0, address token1) external view returns (uint256) {
...
120	    function removePool(
121	        address creator,
122	        address baseToken,
123	        address quoteToken,
124	        uint256 poolIndex,
125	        uint256 userPoolIndex
126	    ) external onlyOwner {
...
148	    function _addPool(address creator, address baseToken, address quoteToken, address pool) internal {

GitHub : 53, 120..126, 148


- src/staking/LockingMultiRewards.sol

GitHub : 277, 292, 300, 361, 397, 479, 528, 536, 544, 557, 572, 597

[G-26]<a name="g-26"></a> Optimize Gas by Using Do-While Loops

Using do-while loops instead of for loops can be more gas-efficient. Even if you add an if condition to account for the case where the loop doesn't execute at all, a do-while loop can still be cheaper in terms of gas.

There are 9 instance(s) of this issue:


- 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	        }

GitHub : 165..172


- src/mimswap/libraries/Math.sol
32	        while (z < y) {
33	            y = z;
34	            z = (x / z + z) / 2;
35	        }

GitHub : 32..35


- src/mimswap/periphery/Router.sol
544	        for (uint256 i = 0; i < iterations; ) {
545	            if (directions & 1 == 0) {
546	                // Sell base
547	                IMagicLP(path[i]).sellBase(address(path[i + 1]));
548	            } else {
549	                // Sell quote
550	                IMagicLP(path[i]).sellQuote(address(path[i + 1]));
551	            }
552	
553	            directions >>= 1;
554	
555	            unchecked {
556	                ++i;
557	            }
558	        }

GitHub : 544..558


- 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	        }
...
547	        for (uint256 i; i < rewardTokens.length; ) {
548	            _updateRewardsGlobal(rewardTokens[i], totalSupply_);
549	            unchecked {
550	                ++i;
551	            }
552	        }
...
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	        }
...
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	        }
...
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	            }
...
614	        for (uint256 i; i < rewardTokens.length; ) {
615	            address rewardToken = rewardTokens[i];
616	            uint256 rewardAmount = rewards[user][rewardToken];
617	
618	            // in all scenario, reset the reward amount immediately
619	            rewards[user][rewardToken] = 0;
620	
621	            // don't assume the rewardTokens array is always the same length as the items array
622	            // as new reward tokens can be added by the owner
623	            if (i < rewardItemLength) {
624	                RewardLockItem storage item = _rewardLock.items[i];
625	
626	                // expired lock, claim existing unlocked rewards if any
627	                if (expired) {
628	                    uint256 amount = item.amount;
629	
630	                    // since this current lock is expired and that item index
631	                    // matches the reward index, override the current amount
632	                    // with the new locked amount.
633	                    item.amount = rewardAmount;
634	
635	                    // use cached amount
636	                    if (amount > 0) {
637	                        rewardToken.safeTransfer(user, amount);
638	                        emit LogRewardPaid(user, rewardToken, amount);
639	                    }
640	                } else {
641	                    // not expired, just add to the existing lock
642	                    item.amount += rewardAmount;
643	                }
644	            }
645	            // new reward token, create a new lock item
646	            // could mean it's adding to an existing lock or creating a new one
647	            else {
648	                _userRewardLock[user].items.push(RewardLockItem({token: rewardToken, amount: rewardAmount}));
649	            }
650	
651	            emit LogRewardLocked(user, rewardToken, rewardAmount);
652	
653	            unchecked {
654	                ++i;
655	            }
656	        }

GitHub : 405..452, 547..552, 561..568, 575..592, 580..587, 614..656

[G-27]<a name="g-27"></a> Use Assembly for Efficient Event Emission

To efficiently emit events, consider utilizing assembly by making use of scratch space and the free memory pointer. This approach can potentially avoid the costs associated with memory expansion.

However, it's crucial to cache and restore the free memory pointer for safe optimization. Good examples of such practices can be found in well-optimized Solady's codebases. Please review your code and consider the potential gas savings of this approach.

There are 59 instance(s) of this issue:


- src/blast/BlastBox.sol
78	        emit LogFeeToChanged(feeTo_);
...
90	        emit LogTokenDepositEnabled(token, enabled);

GitHub : 78, 90


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

GitHub : 46


- src/blast/BlastMagicLP.sol
86	        emit LogFeeToChanged(feeTo_);
...
91	        emit LogOperatorChanged(operator, status);

GitHub : 86, 91


- 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);

GitHub : 120, 129, 140, 153, 182, 187, 192, 197, 202, 211


- src/blast/BlastOnboardingBoot.sol

GitHub : 71, 124, 132, 143, 148


- src/blast/BlastTokenRegistry.sol

GitHub : 20


- src/blast/libraries/BlastYields.sol

GitHub : 26, 31, 44, 57, 62, 72, 77


- src/mimswap/MagicLP.sol

GitHub : 259, 264, 282, 287, 323, 325, 345, 347, 352, 409, 454, 467, 501


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 52, 59


- src/mimswap/periphery/Factory.sol

GitHub : 88, 102, 111, 141, 152


- src/staking/LockingMultiRewards.sol

GitHub : 183, 318, 331, 392, 436, 447, 475, 513, 611, 638, 651

[G-28]<a name="g-28"></a> Optimize External Calls with Assembly for Memory Efficiency

Using interfaces to make external contract calls in Solidity is convenient but can be inefficient in terms of memory utilization. Each such call involves creating a new memory location to store the data being passed, thus incurring memory expansion costs.

Inline assembly allows for optimized memory usage by re-using already allocated memory spaces or using the scratch space for smaller datasets. This can result in notable gas savings, especially for contracts that make frequent external calls.

Additionally, using inline assembly enables important safety checks like verifying if the target address has code deployed to it using extcodesize(addr) before making the call, mitigating risks associated with contract interactions.

There are 129 instance(s) of this issue:


- src/blast/BlastBox.sol
57	        if (!registry.nativeYieldTokens(token_)) {
...
86	        if (registry.nativeYieldTokens(token)) {

GitHub : 57, 86


- src/blast/BlastMagicLP.sol
48	        address feeTo_ = BlastMagicLP(address(implementation)).feeTo();
...
54	        address feeTo_ = BlastMagicLP(address(implementation)).feeTo();
...
56	        if (registry.nativeYieldTokens(_BASE_TOKEN_)) {
...
59	        if (registry.nativeYieldTokens(_QUOTE_TOKEN_)) {
...
105	        if (registry.nativeYieldTokens(_BASE_TOKEN_)) {
...
109	        if (registry.nativeYieldTokens(_QUOTE_TOKEN_)) {
...
119	        if (!BlastMagicLP(address(implementation)).operators(msg.sender) && msg.sender != implementation.owner()) {
...
119	        if (!BlastMagicLP(address(implementation)).operators(msg.sender) && msg.sender != implementation.owner()) {

GitHub : 48, 54, 56, 59, 105, 109, 119, 119


- src/blast/BlastOnboarding.sol

GitHub : 169, 178


- src/blast/BlastOnboardingBoot.sol

GitHub : 69, 89, 106, 118, 119, 131


- src/blast/libraries/BlastPoints.sol

GitHub : 11


- src/blast/libraries/BlastYields.sol

GitHub : 21, 25, 30, 43, 56, 61, 71, 71, 76


- src/mimswap/MagicLP.sol

GitHub : 156, 156, 164, 174, 187, 225, 253, 276, 295, 319, 341, 451, 608


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 39


- src/mimswap/periphery/Factory.sol

GitHub : 86


- src/mimswap/periphery/Router.sol

GitHub : 64, 64, 66, 70, 85, 83, 88, 90, 90, 93, 117, 119, 120, 129, 136, 172, 173, 186, 187, 202, 208, 204, 216, 216, 236, 239, 238, 243, 243, 252, 253, 255, 271, 284, 295, 296, 286, 287, 308, 330, 328, 351, 349, 359, 359, 382, 380, 395, 393, 399, 411, 421, 427, 427, 439, 443, 445, 456, 467, 473, 473, 485, 489, 491, 500, 514, 515, 516, 521, 522, 550, 547, 563, 561, 572, 579


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 25, 25, 26, 26, 34, 38, 38, 39, 39, 45

[G-29]<a name="g-29"></a> Use Assembly for Hash Calculations

In certain cases, using inline assembly to calculate hashes can lead to significant gas savings. Solidity's built-in keccak256 function is convenient but costs more gas than the equivalent assembly code. However, it's important to note that using assembly should be done with care as it's less readable and could increase the risk of introducing errors.

There are 1 instance(s) of this issue:


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

GitHub : 163

[G-30]<a name="g-30"></a> Consider Using Solady's Gas Optimized Lib for Math

Utilizing gas-optimized math functions from libraries like Solady can lead to more efficient smart contracts. This is particularly beneficial in contracts where these operations are frequently used.

There are 96 instance(s) of this issue:


- src/blast/BlastOnboardingBoot.sol
163	        return (userLocked * totalPoolShares) / totalLocked;
...
163	        return (userLocked * totalPoolShares) / totalLocked;

GitHub : 163, 163


- src/mimswap/MagicLP.sol
435	        baseAmount = (baseBalance * shareAmount) / totalShares;
...
435	        baseAmount = (baseBalance * shareAmount) / totalShares;
...
436	        quoteAmount = (quoteBalance * shareAmount) / totalShares;
...
436	        quoteAmount = (quoteBalance * shareAmount) / totalShares;
...
438	        _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) - (uint256(_BASE_TARGET_) * shareAmount).divCeil(totalShares));
...
439	        _QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_) - (uint256(_QUOTE_TARGET_) * shareAmount).divCeil(totalShares));
...
513	            _BASE_TARGET_ = uint112((uint256(_BASE_TARGET_) * baseBalance) / uint256(_BASE_RESERVE_));
...
513	            _BASE_TARGET_ = uint112((uint256(_BASE_TARGET_) * baseBalance) / uint256(_BASE_RESERVE_));

GitHub : 435, 435, 436, 436, 438, 439, 513, 513, 517, 517, 553


- src/mimswap/libraries/DecimalMath.sol

GitHub : 24, 24, 28, 32, 32, 36, 40, 53, 54, 54, 56, 56


- src/mimswap/libraries/Math.sol

GitHub : 20, 21, 30, 34, 34, 56, 59, 62, 62, 64, 64, 93, 93, 96, 96, 99, 99, 97, 97, 101, 152, 155, 155, 158, 158, 158, 158, 156, 156, 156, 160, 160, 170, 170, 170, 170, 171, 181, 184, 184, 185, 188


- src/mimswap/libraries/PMMPricing.sol

GitHub : 209, 209, 205, 205


- src/mimswap/periphery/Router.sol

GitHub : 257, 257, 258, 258


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 38, 39, 43, 44, 45, 45


- src/staking/LockingMultiRewards.sol

GitHub : 144, 204, 236, 236, 241, 241, 258, 258, 283, 283, 283, 294, 294, 380, 388

[G-31]<a name="g-31"></a> Trade-offs Between Modifiers and Internal Functions

In Solidity, both internal functions and modifiers are used to refactor and manage code, but they come with their own trade-offs, especially in terms of gas cost and flexibility.

Modifiers:
  • Less runtime gas cost (saves around 24 gas per function call).
  • Increases deployment gas cost due to repetitive code.
  • Can only be executed at the start or end of a function.
Internal Functions:
  • Lower deployment cost.
  • Can be executed at any point in a function.
  • Slightly higher runtime gas cost (24 gas) due to the need to jump to the function's location in bytecode.
Recommendations:
  • Use modifiers for high-frequency functions where runtime gas cost matters the most.
  • Use internal functions where the priority is reducing deployment gas cost or when you need more flexibility in the function's logic.

Example analysis shows that using modifiers can increase deployment costs by over 35k gas but save 24 gas per function call during runtime. Choose wisely based on your specific use case.

There are 99 instance(s) of this issue:


- src/blast/BlastBox.sol
52	    function claimGasYields() external onlyOperators returns (uint256) {
...
56	    function claimTokenYields(address token_) external onlyOperators returns (uint256 amount) {
...
68	    function callBlastPrecompile(bytes calldata data) external onlyOwner {
...
72	    function setFeeTo(address feeTo_) external onlyOwner {
...
81	    function setTokenEnabled(address token, bool enabled) external onlyOwner {

GitHub : 52, 56, 68, 72, 81


- src/blast/BlastGovernor.sol
28	    function claimNativeYields(address contractAddress) external onlyOperators returns (uint256) {
...
32	    function claimMaxGasYields(address contractAddress) external onlyOperators returns (uint256) {
...
40	    function setFeeTo(address _feeTo) external onlyOwner {
...
49	    function callBlastPrecompile(bytes calldata data) external onlyOwner {
...
53	    function execute(address to, uint256 value, bytes calldata data) external onlyOwner returns (bool success, bytes memory result) {

GitHub : 28, 32, 40, 49, 53


- src/blast/BlastMagicLP.sol

GitHub : 47, 47, 53, 53, 64, 64, 72, 72, 80, 80, 89, 89, 118..123


- src/blast/BlastOnboarding.sol

GitHub : 43..48, 50..56, 101, 101, 101, 123, 123, 123, 132, 132, 147, 156, 160, 164, 175, 185, 185, 190, 195, 195, 200, 200, 205, 214, 218


- src/blast/BlastOnboardingBoot.sol

GitHub : 96, 96, 129, 137, 146, 146


- src/blast/BlastTokenRegistry.sol

GitHub : 14


- src/mimswap/MagicLP.sol

GitHub : 134, 244, 267, 290, 360, 420, 461, 479, 479, 504, 504, 607..612, 614..619, 621..626


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 46, 57


- src/mimswap/periphery/Factory.sol

GitHub : 96, 105, 116, 126


- src/mimswap/periphery/Router.sol

GitHub : 47..52, 169, 185, 199, 235, 321, 342, 372, 410, 420, 438, 455, 466, 484


- src/staking/LockingMultiRewards.sol

GitHub : 150, 155, 300, 317, 324, 337, 341, 349, 361, 397

[G-32]<a name="g-32"></a> Optimize Boolean States with uint256(1/2)

Boolean variables in Solidity are more expensive than uint256 or any type that takes up a full word, due to additional gas costs associated with write operations. When using boolean variables, each write operation emits an extra SLOAD to read the slot's contents, replace the bits taken up by the boolean, and then write back. This process cannot be disabled and leads to extra gas consumption.

By using uint256(1) and uint256(2) for representing true and false states, you can avoid a Gwarmaccess (100 gas) cost and also avoid a Gsset (20000 gas) cost when changing from false to true, after having been true in the past. This approach helps in optimizing gas usage, making your contract more cost-effective.

Usage in OpenZeppelin ReentrancyGuard.sol

There are 7 instance(s) of this issue:


- src/blast/BlastBox.sol
21	    mapping(address => bool) public enabledTokens;

GitHub : 21


- src/blast/BlastMagicLP.sol
21	    mapping(address => bool) public operators;

GitHub : 21


- src/blast/BlastOnboarding.sol
36	    mapping(address token => bool) public supportedTokens;

GitHub : 36


- src/blast/BlastOnboardingBoot.sol
28	    bool public ready;
...
30	    mapping(address user => bool claimed) public claimed;

GitHub : 28, 30


- src/blast/BlastTokenRegistry.sol
10	    mapping(address => bool) public nativeYieldTokens;

GitHub : 10


- src/mimswap/MagicLP.sol
68	    bool internal _INITIALIZED_;

GitHub : 68

[G-33]<a name="g-33"></a> Consider Packing Small uint When it's Possible

Packing uint variables into the same storage slot can help in reducing gas costs. This is particularly useful when storing or reading multiple smaller uints (e.g., uint80) in a single transaction. Consider using bit manipulation to pack these variables.

If you pack two uint variables into a single uint storage slot, you'd perform only one SLOAD operation (800 gas) instead of two (1,600 gas) when you read them. This saves 800 gas for each read operation involving the two variables.

Similarly, when you need to update both variables, a single SSTORE operation would cost you 20,000 gas instead of 40,000 gas, saving you another 20,000 gas.

Example:

uint160 packedVariables;

function packVariables(uint80 x, uint80 y) external {
  packedVariables = uint160(x) << 80 | uint160(y);
}

There are 8 instance(s) of this issue:


- src/mimswap/MagicLP.sol
72	    uint112 public _BASE_RESERVE_;
...
73	    uint112 public _QUOTE_RESERVE_;
...
74	    uint32 public _BLOCK_TIMESTAMP_LAST_;
...
76	    uint112 public _BASE_TARGET_;
...
77	    uint112 public _QUOTE_TARGET_;
...
78	    uint32 public _RState_;

GitHub : 72, 73, 74, 76, 77, 78


- src/oracles/aggregators/MagicLpAggregator.sol
13	    uint8 public immutable baseDecimals;
...
14	    uint8 public immutable quoteDecimals;

GitHub : 13, 14

[G-34]<a name="g-34"></a> Avoid Unnecessary Public Variables

Public storage variables increase the contract's size due to the implicit generation of public getter functions. This makes the contract larger and could increase deployment and interaction costs.

If you do not require other contracts to read these variables, consider making them private or internal.

Example:

/// 145426 gas to deploy
contract PublicState {
  address public first;
  address public second;
}
/// 77126 gas to deploy
contract PrivateState {
  address private first;
  address private second;
}

There are 71 instance(s) of this issue:


- src/blast/BlastBox.sol
20	    BlastTokenRegistry public immutable registry;
...
21	    mapping(address => bool) public enabledTokens;
...
22	    address public feeTo;

GitHub : 20, 21, 22


- src/blast/BlastGovernor.sol
11	    address public feeTo;

GitHub : 11


- src/blast/BlastMagicLP.sol
17	    BlastTokenRegistry public immutable registry;
...
20	    address public feeTo;
...
21	    mapping(address => bool) public operators;

GitHub : 17, 20, 21


- src/blast/BlastOnboarding.sol
30	    State public state;
...
31	    address public bootstrapper;
...
32	    address public feeTo;

GitHub : 30, 31, 32, 33, 36, 37, 38, 41


- src/blast/BlastOnboardingBoot.sol

GitHub : 24, 25, 26, 27, 28, 29, 30


- src/blast/BlastTokenRegistry.sol

GitHub : 10


- src/blast/libraries/BlastPoints.sol

GitHub : 7, 8


- src/mimswap/MagicLP.sol

GitHub : 61, 63, 64, 65, 66, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 19, 20


- src/mimswap/periphery/Factory.sol

GitHub : 32, 33, 35, 36


- src/mimswap/periphery/Router.sol

GitHub : 31, 33, 34


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 10, 11, 12, 13, 14, 16


- src/staking/LockingMultiRewards.sol

GitHub : 83, 84, 85, 86, 87, 94, 95, 96, 98, 100, 101, 102, 103

[G-35]<a name="g-35"></a> Optimize by Using Assembly for Low-Level Calls' Return Data

Even second return value from a low-level call is not assigned, it is still copied to memory, leading to additional gas costs. By employing assembly, you can bypass this memory copying, achieving a 159 gas saving.

There are 1 instance(s) of this issue:


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

GitHub : 54

[G-36]<a name="g-36"></a> Optimize Unsigned Integer Comparison With Zero

For unsigned integers, checking whether the integer is not equal to zero (!= 0) is less gas-intensive than checking whether it is greater than zero (> 0).

This is because the Ethereum Virtual Machine (EVM) can perform a simple bitwise operation to check if any bit is set (which directly translates to != 0), while checking for > 0 requires additional logic.

As such, when dealing with unsigned integers in Solidity, it is recommended to use the != 0 comparison for gas optimization.

There are 14 instance(s) of this issue:


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

GitHub : 114


- src/mimswap/MagicLP.sol
294	        if (data.length > 0) {
...
395	        } else if (baseReserve > 0 && quoteReserve > 0) {
...
395	        } else if (baseReserve > 0 && quoteReserve > 0) {
...
450	        if (data.length > 0) {
...
549	        if (timeElapsed > 0 && _BASE_RESERVE_ != 0 && _QUOTE_RESERVE_ != 0) {
...
582	        if (amount > 0) {
...
588	        if (amount > 0) {

GitHub : 294, 395, 395, 450, 549, 582, 588


- src/mimswap/libraries/Math.sol
22	        if (remainder > 0) {

GitHub : 22


- src/mimswap/periphery/Router.sol
147	        } else if (baseReserve > 0 && quoteReserve > 0) {

GitHub : 147, 147, 527, 527


- src/staking/LockingMultiRewards.sol

GitHub : 636

[G-37]<a name="g-37"></a> Delete Unused State Variables

State variables that aren't used in the contract not only clutter the codebase but also consume unnecessary gas during deployment. Specifically, setting non-zero initial values for state variables costs significant gas. By removing these unused state variables, you can save on both deployment gas and potential future storage gas costs. This optimization not only reduces gas expenditures but also enhances code clarity and maintainability. Always ensure a thorough review to confirm that these variables are indeed redundant before removal.

There are 27 instance(s) of this issue:


- 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) {
...
86	    function previewTotalPoolShares() external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
86	    function previewTotalPoolShares() external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
...
155	    function _claimable(address user) internal view returns (uint256 shares) {

GitHub : 78, 86, 86, 86, 155


- src/mimswap/MagicLP.sol
215	    function getMidPrice() public view returns (uint256 midPrice) {
...
224	    function getUserFeeRate(address user) external view returns (uint256 lpFeeRate, uint256 mtFeeRate) {
...
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) {

GitHub : 215, 224, 224, 228, 232


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 34, 34


- src/mimswap/auxiliary/FeeRateModelImpl.sol

GitHub : 13


- src/mimswap/periphery/Router.sol

GitHub : 22, 185, 235, 268, 268, 321, 342, 410, 420, 455, 466


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 34, 34


- src/staking/LockingMultiRewards.sol

GitHub : 69

[G-38]<a name="g-38"></a> Optimize Gas by Using Only Named Returns

The Solidity compiler can generate more efficient bytecode when using named returns. It's recommended to replace anonymous returns with named returns for potential gas savings.

Example:

/// 985 gas cost
function add(uint256 x, uint256 y) public pure returns (uint256) {
  return x + y;
}
/// 941 gas cost
function addNamed(uint256 x, uint256 y) public pure returns (uint256 res) {
  res = x + y;
}

There are 62 instance(s) of this issue:


- src/blast/BlastBox.sol
52	    function claimGasYields() external onlyOperators returns (uint256) {
...
103	    function isOwner(address _account) internal view override returns (bool) {

GitHub : 52, 103


- src/blast/BlastGovernor.sol
28	    function claimNativeYields(address contractAddress) external onlyOperators returns (uint256) {
...
32	    function claimMaxGasYields(address contractAddress) external onlyOperators returns (uint256) {

GitHub : 28, 32


- src/blast/BlastMagicLP.sol
39	    function version() external pure override returns (string memory) {
...
47	    function claimGasYields() external onlyClones onlyImplementationOperators returns (uint256) {

GitHub : 39, 47


- src/blast/BlastOnboarding.sol
160	    function claimGasYields() external onlyOwner returns (uint256) {
...
226	    function _implementation() internal view override returns (address) {

GitHub : 160, 226


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

GitHub : 96


- src/blast/libraries/BlastYields.sol
38	    function claimMaxGasYields(address recipient) internal returns (uint256) {

GitHub : 38, 60, 75


- src/mimswap/MagicLP.sol

GitHub : 155, 159, 163, 236


- src/mimswap/libraries/DecimalMath.sol

GitHub : 23, 27, 31, 35, 39, 43, 47


- src/mimswap/libraries/Math.sol

GitHub : 19, 51, 79, 131


- src/mimswap/libraries/PMMPricing.sol

GitHub : 104..113, 119..128, 134..143, 147..156, 162..171, 175..184, 203


- src/mimswap/periphery/Factory.sol

GitHub : 53, 57, 65..72, 155..162


- src/oracles/aggregators/MagicLpAggregator.sol

GitHub : 29, 33, 37, 48


- src/staking/LockingMultiRewards.sol

GitHub : 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 253, 257, 261, 265, 269, 273, 277, 288, 292

[G-39]<a name="g-39"></a> Optimize Address Storage Value Management with assembly

There are 18 instance(s) of this issue:


- src/blast/BlastBox.sol
33	        feeTo = feeTo_;
...
77	        feeTo = feeTo_;

GitHub : 33, 77


- src/blast/BlastGovernor.sol
20	        feeTo = feeTo_;
...
45	        feeTo = _feeTo;

GitHub : 20, 45


- src/blast/BlastMagicLP.sol
32	        feeTo = feeTo_;
...
85	        feeTo = feeTo_;

GitHub : 32, 85


- src/blast/BlastOnboarding.sol
94	        feeTo = feeTo_;
...
152	        feeTo = feeTo_;
...
191	        bootstrapper = bootstrapper_;

GitHub : 94, 152, 191


- src/blast/BlastWrappers.sol
55	        _governor = governor_;

GitHub : 55


- src/mimswap/MagicLP.sol

GitHub : 119, 120


- src/mimswap/auxiliary/FeeRateModel.sol

GitHub : 27, 51, 58


- src/mimswap/periphery/Factory.sol

GitHub : 45, 101


- src/staking/LockingMultiRewards.sol

GitHub : 135

[G-40]<a name="g-40"></a> 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 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 13 instance(s) of this issue:


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

GitHub : 164


- src/mimswap/libraries/PMMPricing.sol
39	    function sellBaseToken(PMMState memory state, uint256 payBaseAmount) internal pure returns (uint256 receiveQuoteAmount, RState newR) {
...
76	    function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount) internal pure returns (uint256 receiveBaseAmount, RState newR) {
...
105	        PMMState memory state,
...
120	        PMMState memory state,
...
135	        PMMState memory state,
...
148	        PMMState memory state,
...
163	        PMMState memory state,
...
176	        PMMState memory state,
...
190	    function adjustedTarget(PMMState memory state) internal pure {

GitHub : 39, 76, 105, 120, 135, 148, 163, 176, 190, 203


- src/staking/LockingMultiRewards.sol

GitHub : 397, 572

[G-41]<a name="g-41"></a> Gas savings can be achieved by changing the model for assigning value to the structure

Change this structName a = structName({item1: val1,item2: val2}); ==> structName a; a.item1 = val1; a.item2 = val2;

There are 2 instance(s) of this issue:


- src/staking/LockingMultiRewards.sol
501	            _userLocks[user].push(LockedBalance({amount: amount, unlockTime: _nextUnlockTime}));
...
648	                _userRewardLock[user].items.push(RewardLockItem({token: rewardToken, amount: rewardAmount}));

GitHub : 501, 648

proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2

There are 20 instance(s) of this issue:


- src/blast/BlastDapp.sol
// @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2
// @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastDapp.sol#L2) --- - src/blast/BlastBox.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

3 pragma solidity >=0.8.0;

*GitHub* : [3](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastBox.sol#L3) --- - src/blast/BlastGovernor.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastGovernor.sol#L2) --- - src/blast/BlastMagicLP.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

3 pragma solidity >=0.8.0;

*GitHub* : [3](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastMagicLP.sol#L3) --- - src/blast/BlastOnboarding.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboarding.sol#L2) --- - src/blast/BlastOnboardingBoot.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastOnboardingBoot.sol#L2) --- - src/blast/BlastTokenRegistry.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastTokenRegistry.sol#L2) --- - src/blast/BlastWrappers.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/BlastWrappers.sol#L2) --- - src/blast/libraries/BlastPoints.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/libraries/BlastPoints.sol#L2) --- - src/blast/libraries/BlastYields.sol ```solidity // @proof: https://github.com/code-423n4/2024-03-abracadabra-money/blob/1f4693fdbf33e9ad28132643e2d6f7635834c6c6/foundry.toml#L2 // @context ```toml/n2solc = "0.8.20"

2 pragma solidity >=0.8.0;

*GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/blast/libraries/BlastYields.sol#L2) --- - src/mimswap/MagicLP.sol *GitHub* : [8](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/MagicLP.sol#L8) --- - src/mimswap/auxiliary/FeeRateModel.sol *GitHub* : [8](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/auxiliary/FeeRateModel.sol#L8) --- - src/mimswap/auxiliary/FeeRateModelImpl.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/auxiliary/FeeRateModelImpl.sol#L2) --- - src/mimswap/libraries/DecimalMath.sol *GitHub* : [7](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/DecimalMath.sol#L7) --- - src/mimswap/libraries/Math.sol *GitHub* : [8](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/Math.sol#L8) --- - src/mimswap/libraries/PMMPricing.sol *GitHub* : [8](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/libraries/PMMPricing.sol#L8) --- - src/mimswap/periphery/Factory.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Factory.sol#L2) --- - src/mimswap/periphery/Router.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/mimswap/periphery/Router.sol#L2) --- - src/oracles/aggregators/MagicLpAggregator.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/oracles/aggregators/MagicLpAggregator.sol#L2) --- - src/staking/LockingMultiRewards.sol *GitHub* : [2](https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/staking/LockingMultiRewards.sol#L2)

#0 - c4-pre-sort

2024-03-15T14:59:02Z

141345 marked the issue as sufficient quality report

#1 - 141345

2024-03-16T00:20:38Z

81 pfapostol l r nc 1 0 23

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

#2 - c4-judge

2024-03-29T16:45:48Z

thereksfour marked the issue as grade-b

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