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
Rank: 29/36
Findings: 2
Award: $40.20
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: ether_sky
Also found by: 0x11singh99, 0xE1, 0xJaeger, Bauchibred, Bigsam, Bozho, Breeje, DarkTower, HChang26, SpicyMeatball, Trust, ZanyBonzy, albahaca, bareli, blutorque, grearlake, hals, hassan-truscova, hihen, oualidpro, pfapostol, ravikiranweb3, slvDev, zhaojie
15.328 USDC - $15.33
The content section shows only 10 examples, subsequent cases are listed as links.
Issue | Instances | |
---|---|---|
[L-01] | Permanent DoS due to non-shrinking array usage in an unbounded loop | 5 |
[L-02] | Missing checks for address(0) when assigning values to address state variables | 6 |
[L-03] | Empty receive functions can cause gas issues | 2 |
[L-04] | Contract inherits Pausable but does not expose pausing/unpausing functionality | 1 |
[L-05] | Emitting storage values instead of the memory one. | 8 |
[L-06] | Functions calling contracts/addresses with transfer hooks are missing reentrancy guards | 37 |
[L-07] | onlyOwner functions not accessible if owner renounces ownership | 34 |
[L-08] | Subtraction may underflow if multiplication is too large | 1 |
[L-09] | Consider bounding input array length | 3 |
[L-10] | Use of abi.encodePacked with dynamic types inside keccak256 | 1 |
[L-11] | Using > />= without specifying an upper bound in version pragma is unsafe | 20 |
Issue | Instances | |
---|---|---|
[N-01] | Empty function blocks | 4 |
[N-02] | Functions which are either private or internal should have a preceding _ in their name | 25 |
[N-03] | Function names should differ to make the code more readable | 4 |
[N-04] | Emits without msg.sender parameter | 6 |
[N-05] | A function which defines named returns in it's declaration doesn't need to use return | 21 |
[N-06] | Use multiple require() and if statements instead of && | 11 |
[N-07] | Change public to external for functions that are not called internally | 17 |
[N-08] | Constants in comparisons should appear on the left side | 123 |
[N-09] | Do not use underscore at the end of variable name | 98 |
[N-10] | Event is missing indexed field | 39 |
[N-11] | Variable names that consist of all capital letters should be reserved for constant /immutable variables | 35 |
[N-12] | Consider using delete rather than assigning zero/false to clear values | 3 |
[N-13] | Array is push() ed but not pop() ed | 5 |
[N-14] | Unused contract variables | 27 |
[N-15] | else -block not required | 5 |
[N-16] | Remaining eth may not be refunded to users | 7 |
[N-17] | Condition will not revert when block.timestamp is == to the compared variable | 3 |
[N-18] | External calls in an un-bounded for -loop may result in a DOS | 8 |
[N-19] | constructor missing zero address check | 11 |
[N-20] | Variable initialization with default value | 2 |
[N-21] | Complex math should be split into multiple steps | 21 |
[N-22] | Duplicated require/if statements should be refactored | 69 |
[N-23] | Variable names don't follow the Solidity naming convention | 38 |
[N-24] | Contract functions should use an interface | 124 |
[N-25] | Custom error without details | 78 |
[N-26] | State variables should include comments | 83 |
[N-27] | Events that mark critical parameter changes should contain both the old and the new value | 20 |
[N-28] | Constant redefined elsewhere | 2 |
[N-29] | Function ordering does not follow the Solidity style guide | 12 |
[N-30] | Array indicies should be referenced via enum s rather than via numeric literals | 3 |
[N-31] | Use of override is unnecessary | 13 |
[N-32] | Unused function parameter | 13 |
[N-33] | Unused event definition | 3 |
[N-34] | Function names should use lowerCamelCase | 22 |
[N-35] | Overflows in unchecked blocks | 9 |
[N-36] | Missing event and or timelock for critical parameter change | 21 |
[N-37] | Setters should prevent re-setting of the same value | 21 |
[N-38] | Events may be emitted out of order due to reentrancy | 16 |
[N-39] | Execution at deadlines should be allowed | 4 |
[N-40] | Function can return unassigned variable | 1 |
[N-41] | Missing zero address check in initializer | 1 |
[N-42] | Events should be emitted before external calls | 38 |
[N-43] | Use @inheritdoc for overridden functions | 13 |
[N-44] | Contract name does not follow the Solidity Style Guide | 7 |
[N-45] | Variable names for immutable s should use UPPER_CASE_WITH_UNDERSCORES | 16 |
[N-46] | Use a single file for system wide constants | 22 |
[N-47] | Unsafe uint to int conversion | 1 |
[N-48] | Use SafeCast to safely downcast variables | 30 |
[N-49] | Consider adding a block/deny-list | 5 |
[N-50] | Expressions for constant values should use immutable rather than constant | 7 |
[N-51] | Lines are too long | 54 |
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
address(0)
when assigning values to address state variablesThere 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;
- src/mimswap/auxiliary/FeeRateModel.sol
57 function setImplementation(address implementation_) public onlyOwner {
GitHub : 58
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
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
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);
- 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);
- 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
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);
- 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);
- 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
onlyOwner
functions not accessible if owner renounces ownershipThe 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 {
- 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) {
- 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
- src/blast/BlastTokenRegistry.sol
GitHub : 14
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/periphery/Factory.sol
GitHub : 96, 105, 116, 120..126
- src/staking/LockingMultiRewards.sol
GitHub : 300, 317, 324, 337, 341
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
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 {
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
>
/>=
without specifying an upper bound in version pragma is unsafeThere 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
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
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
- src/mimswap/libraries/PMMPricing.sol
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) {
msg.sender
parameterIf 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);
- 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);
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) {
- 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) {
- 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
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 }
- src/staking/LockingMultiRewards.sol
326 if (tokenAddress == stakingToken && tokenAmount > stakingToken.balanceOf(address(this)) - stakingTokenBalance) { 327 revert ErrSkimmingTooMuch(); 328 }
public
to external
for functions that are not called internallyContracts 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
Constants
in comparisons should appear on the left sideDoing 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)) {
- src/blast/BlastGovernor.sol
16 if (feeTo_ == address(0)) { ... 41 if(_feeTo == address(0)) {
- src/blast/BlastMagicLP.sol
24 if (feeTo_ == address(0)) { ... 27 if (address(registry_) == address(0)) { ... 81 if (feeTo_ == address(0)) {
- src/blast/BlastOnboarding.sol
85 if (address(registry_) == address(0)) { ... 89 if (feeTo_ == address(0)) {
- src/blast/BlastOnboardingBoot.sol
- src/blast/BlastTokenRegistry.sol
GitHub : 15
- src/blast/BlastWrappers.sol
- 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
- 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
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 {
- 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
- 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
- src/oracles/aggregators/MagicLpAggregator.sol
- src/staking/LockingMultiRewards.sol
GitHub : 150, 277, 277, 292, 292, 349, 458, 529, 528, 528, 528, 536, 536, 536, 536, 545, 559, 573, 577
indexed
fieldThere 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);
- 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
- src/blast/BlastTokenRegistry.sol
GitHub : 7
- src/blast/libraries/BlastYields.sol
- src/mimswap/MagicLP.sol
GitHub : 30, 31, 32, 33, 34, 35, 36
- src/mimswap/periphery/Factory.sol
- src/staking/LockingMultiRewards.sol
GitHub : 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28
constant
/immutable
variablesIf 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
delete
rather than assigning zero/false to clear valuesThe 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;
- src/staking/LockingMultiRewards.sol
619 rewards[user][rewardToken] = 0;
GitHub : 619
push()
ed but not pop()
edArray 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);
- 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}));
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) {
- 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
- 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
- src/staking/LockingMultiRewards.sol
GitHub : 69
else
-block not requiredOne 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 }
- 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 }
- 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
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
block.timestamp
is ==
to the compared variableThe 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
for
-loop may result in a DOSConsider 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
constructor
missing zero address checkIt 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_)) {
- 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
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
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
- src/staking/LockingMultiRewards.sol
require/if
statements should be refactoredThese 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)) {
- 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)) {
- src/blast/BlastWrappers.sol
- src/mimswap/MagicLP.sol
GitHub : 256, 279, 294, 320, 342, 450, 508, 512, 516, 532, 571, 574, 582, 588
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/libraries/Math.sol
- src/mimswap/libraries/PMMPricing.sol
GitHub : 45, 77, 80, 191, 193, 204, 40
- src/mimswap/periphery/Factory.sol
- 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
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();
- 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
- src/mimswap/libraries/PMMPricing.sol
- src/mimswap/periphery/Factory.sol
- src/staking/LockingMultiRewards.sol
GitHub : 89, 90, 91, 92, 371, 372, 481, 482, 529, 545, 559, 573, 577, 598
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 {
- 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) {
- 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
- 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
error
without detailsConsider 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();
- 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
- 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
- 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
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;
- 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;
- 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
- 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
- src/mimswap/libraries/DecimalMath.sol
- src/mimswap/periphery/Factory.sol
- src/mimswap/periphery/Router.sol
- 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
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);
- 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);
- src/blast/BlastOnboardingBoot.sol
37 event LogReadyChanged(bool ready); ... 41 event LogStakingChanged(address indexed staking);
- src/mimswap/MagicLP.sol
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/periphery/Factory.sol
- src/staking/LockingMultiRewards.sol
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
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) {
- 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) {
enum
s rather than via numeric literalsThere 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];
override
is unnecessaryStarting 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) {
- src/blast/BlastMagicLP.sol
39 function version() external pure override returns (string memory) { ... 98 function _afterInitialized() internal override {
- 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) {
- src/oracles/aggregators/MagicLpAggregator.sol
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
event
definitionNote 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
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) {
- 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) {
- 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
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
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 {
- 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 {
- 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 {
- src/blast/BlastOnboardingBoot.sol
137 function setStaking(LockingMultiRewards _staking) external onlyOwner {
- src/blast/BlastTokenRegistry.sol
GitHub : 14
- src/mimswap/MagicLP.sol
GitHub : 470..479, 528, 545, 560
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/periphery/Factory.sol
- src/staking/LockingMultiRewards.sol
GitHub : 317
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 {
- 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 {
- 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 {
- src/blast/BlastOnboardingBoot.sol
129 function initialize(Router _router) external onlyOwner {
- src/blast/BlastTokenRegistry.sol
GitHub : 14
- src/mimswap/MagicLP.sol
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/periphery/Factory.sol
- src/staking/LockingMultiRewards.sol
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 {
- 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) {
- src/blast/libraries/BlastYields.sol
20 function enableTokenClaimable(address token) internal { ... 29 function configureDefaultClaimables(address governor_) internal {
- src/mimswap/MagicLP.sol
290 function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external nonReentrant {
- src/mimswap/periphery/Factory.sol
GitHub : 81
- src/staking/LockingMultiRewards.sol
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) {
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
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
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);
- 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);
- 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
- src/staking/LockingMultiRewards.sol
GitHub : 183, 331, 392, 447, 475, 513, 638, 651
@inheritdoc
for overridden functionsIt 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) {
- 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 {
- 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
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
immutable
s should use UPPER_CASE_WITH_UNDERSCORESFor 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;
- 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;
- src/staking/LockingMultiRewards.sol
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);
- src/blast/libraries/BlastYields.sol
14 IBlast constant BLAST_YIELD_PRECOMPILE = IBlast(0x4300000000000000000000000000000000000002);
GitHub : 14
- src/mimswap/MagicLP.sol
- src/mimswap/libraries/DecimalMath.sol
- src/mimswap/periphery/Router.sol
GitHub : 31
- src/oracles/aggregators/MagicLpAggregator.sol
GitHub : 16
- src/staking/LockingMultiRewards.sol
uint
to int
conversionThe 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
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
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
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;
- src/mimswap/libraries/DecimalMath.sol
20 uint256 internal constant ONE = 10 ** 18; ... 21 uint256 internal constant ONE2 = 10 ** 36;
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) {
- 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) {
- 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
- 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_);
- 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
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
🌟 Selected for report: hihen
Also found by: 0x11singh99, Bozho, Sathish9098, albahaca, clara, dharma09, oualidpro, pfapostol, slvDev
24.8718 USDC - $24.87
The content section shows only 10 examples, subsequent cases are listed as links.
Issue | Instances | Gas Savings | |
---|---|---|---|
[G-01] | <array>.length Should Not Be Looked Up In Every Loop Of A For-loop | 7 | 679 |
[G-02] | Setting the constructor to payable | 16 | 208 |
[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 -loops | 1 | 60 |
[G-04] | Pre-increments and pre-decrements are cheaper than post-increments and post-decrements | 1 | 90 |
[G-05] | Use assembly to check for address(0) | 29 | 174 |
[G-06] | Internal functions only called once can be inlined to save gas | 12 | 360 |
[G-07] | Usage of uint /int smaller than 32 bytes (256 bits) incurs overhead | 15 | 330 |
[G-08] | Functions guaranteed to revert when called by normal users can be marked payable | 40 | 840 |
[G-09] | >= costs less gas than > | 16 | 48 |
[G-10] | Multiple mappings can be replaced with a single struct mapping | 13 | 260000 |
[G-11] | State variables should be cached in stack variables rather than re-reading them from storage | 186 | 18042 |
[G-12] | Stack variable used as a cheaper cache for a state variable is only used once | 24 | 72 |
[G-13] | State Variables can be packed into fewer storage slots | 1 | 20000 |
[G-14] | State variables access within a loop | 6 | 3180 |
[G-15] | Using private for constants saves gas | 18 | 151308 |
[G-16] | Using bool for storage incurs overhead | 7 | 119700 |
[G-17] | State variables only set in the constructor should be declared immutable | 1 | 20000 |
[G-18] | Consider activating via-ir for deploying | 1 | 0 |
[G-19] | Function calls should be cached instead of re-calling the function | 14 | 672 |
[G-20] | Add unchecked blocks for divisions where the operands cannot overflow | 40 | 6360 |
[G-21] | Multiplication/division by two should use bit shifting | 7 | 140 |
[G-22] | Consider using bytes32 rather than a string | 4 | 512 |
[G-23] | Optimize Zero Checks Using Assembly | 84 | 4872 |
[G-24] | Consider using assembly to write address storage values if the address variable is mutable | 26 | 0 |
[G-25] | Consider Caching Multiple Accesses to Mappings/Arrays | 22 | 2200 |
[G-26] | Optimize Gas by Using Do-While Loops | 9 | 2295 |
[G-27] | Use Assembly for Efficient Event Emission | 59 | 106200 |
[G-28] | Optimize External Calls with Assembly for Memory Efficiency | 129 | 28380 |
[G-29] | Use Assembly for Hash Calculations | 1 | 1005 |
[G-30] | Consider Using Solady's Gas Optimized Lib for Math | 96 | 0 |
[G-31] | Trade-offs Between Modifiers and Internal Functions | 99 | 1039500 |
[G-32] | Optimize Boolean States with uint256(1/2) | 7 | 119000 |
[G-33] | Consider Packing Small uint When it's Possible | 8 | 166400 |
[G-34] | Avoid Unnecessary Public Variables | 71 | 1562000 |
[G-35] | Optimize by Using Assembly for Low-Level Calls' Return Data | 1 | 159 |
[G-36] | Optimize Unsigned Integer Comparison With Zero | 14 | 56 |
[G-37] | Delete Unused State Variables | 27 | 540000 |
[G-38] | Optimize Gas by Using Only Named Returns | 62 | 2728 |
[G-39] | Optimize Address Storage Value Management with assembly | 18 | 0 |
[G-40] | Use calldata instead of memory for function arguments that do not get mutated | 13 | 0 |
[G-41] | Gas savings can be achieved by changing the model for assigning value to the structure | 2 | 520 |
<array>.length
Should Not Be Looked Up In Every Loop Of A For-loopThe 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
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_) {
- 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_)) {
- 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
++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
-loopsThe 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
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
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)) {
- src/blast/BlastGovernor.sol
16 if (feeTo_ == address(0)) { ... 41 if(_feeTo == address(0)) {
- src/blast/BlastMagicLP.sol
24 if (feeTo_ == address(0)) { ... 27 if (address(registry_) == address(0)) { ... 81 if (feeTo_ == address(0)) {
- src/blast/BlastOnboarding.sol
85 if (address(registry_) == address(0)) { ... 89 if (feeTo_ == address(0)) {
- src/blast/BlastOnboardingBoot.sol
GitHub : 97
- src/blast/BlastTokenRegistry.sol
GitHub : 15
- src/blast/BlastWrappers.sol
- src/mimswap/MagicLP.sol
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/periphery/Factory.sol
- src/mimswap/periphery/Router.sol
- src/staking/LockingMultiRewards.sol
GitHub : 301
Internal
functions only called once can be inlined to save gasThere 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) {
- src/mimswap/MagicLP.sol
528 function _resetTargetAndReserve() internal returns (uint256 baseBalance, uint256 quoteBalance) { ... 601 function _afterInitialized() internal virtual {}
- 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
uint
/int
smaller than 32 bytes (256 bits) incurs overheadWhen 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 {
- src/oracles/aggregators/MagicLpAggregator.sol
- src/staking/LockingMultiRewards.sol
GitHub : 55
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 {
- 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) {
- 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 {
- 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
- src/blast/BlastTokenRegistry.sol
GitHub : 14
- src/mimswap/MagicLP.sol
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/periphery/Factory.sol
GitHub : 96, 105, 116, 120..126
- src/staking/LockingMultiRewards.sol
GitHub : 300, 317, 324, 337, 341
>=
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
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;
- src/mimswap/periphery/Factory.sol
35 mapping(address base => mapping(address quote => address[] pools)) public pools; ... 36 mapping(address creator => address[] pools) public userPools;
- 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
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
- src/blast/BlastOnboarding.sol
- src/blast/BlastOnboardingBoot.sol
- src/blast/BlastOnboardingBoot.sol
- 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
- src/blast/BlastOnboardingBoot.sol
- src/blast/BlastOnboardingBoot.sol
- src/mimswap/MagicLP.sol
- 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
- src/mimswap/MagicLP.sol
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/periphery/Factory.sol
- src/staking/LockingMultiRewards.sol
- src/staking/LockingMultiRewards.sol
- src/staking/LockingMultiRewards.sol
- src/staking/LockingMultiRewards.sol
- src/staking/LockingMultiRewards.sol
- src/staking/LockingMultiRewards.sol
GitHub : 483, 490, 501, 510, 482, 502
- src/staking/LockingMultiRewards.sol
- src/staking/LockingMultiRewards.sol
- src/staking/LockingMultiRewards.sol
- src/staking/LockingMultiRewards.sol
- src/staking/LockingMultiRewards.sol
GitHub : 598, 648, 614, 615, 616, 619
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) {
- 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) {
- src/mimswap/periphery/Factory.sol
GitHub : 81
- src/mimswap/periphery/Router.sol
- src/oracles/aggregators/MagicLpAggregator.sol
GitHub : 37
- src/staking/LockingMultiRewards.sol
GitHub : 277, 292, 479, 544, 557, 572, 597
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
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
private
for constants saves gasSaves 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);
- src/blast/libraries/BlastYields.sol
14 IBlast constant BLAST_YIELD_PRECOMPILE = IBlast(0x4300000000000000000000000000000000000002);
GitHub : 14
- src/mimswap/MagicLP.sol
- src/mimswap/libraries/DecimalMath.sol
- src/mimswap/periphery/Router.sol
GitHub : 31
- src/oracles/aggregators/MagicLpAggregator.sol
GitHub : 16
bool
for storage incurs overheadUse 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;
- src/blast/BlastTokenRegistry.sol
10 mapping(address => bool) public nativeYieldTokens;
GitHub : 10
- src/mimswap/MagicLP.sol
68 bool internal _INITIALIZED_;
GitHub : 68
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
via-ir
for deployingThe 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
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) {
- 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
unchecked
blocks for divisions where the operands cannot overflowuint
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_));
- 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
- src/mimswap/periphery/Router.sol
- src/oracles/aggregators/MagicLpAggregator.sol
GitHub : 45
- src/staking/LockingMultiRewards.sol
GitHub : 144, 236, 241, 258, 283, 294, 388
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
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) {
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)) {
- src/blast/BlastGovernor.sol
16 if (feeTo_ == address(0)) { ... 41 if(_feeTo == address(0)) {
- src/blast/BlastMagicLP.sol
24 if (feeTo_ == address(0)) { ... 27 if (address(registry_) == address(0)) { ... 81 if (feeTo_ == address(0)) {
- src/blast/BlastOnboarding.sol
85 if (address(registry_) == address(0)) { ... 89 if (feeTo_ == address(0)) {
- src/blast/BlastOnboardingBoot.sol
- src/blast/BlastTokenRegistry.sol
GitHub : 15
- src/blast/BlastWrappers.sol
- 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
- 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
- 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
assembly
to write address storage values if the address variable is mutableWriting 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_;
- src/blast/BlastGovernor.sol
20 feeTo = feeTo_; ... 45 feeTo = _feeTo;
- src/blast/BlastMagicLP.sol
32 feeTo = feeTo_; ... 85 feeTo = feeTo_;
- src/blast/BlastOnboarding.sol
94 feeTo = feeTo_; ... 152 feeTo = feeTo_; ... 191 bootstrapper = bootstrapper_;
- src/mimswap/MagicLP.sol
119 _BASE_TOKEN_ = baseTokenAddress;
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/periphery/Factory.sol
- src/mimswap/periphery/Router.sol
GitHub : 66, 88, 204, 238, 286, 351, 349, 382, 380
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) {
- 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) {
- 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 {
- src/staking/LockingMultiRewards.sol
GitHub : 277, 292, 300, 361, 397, 479, 528, 536, 544, 557, 572, 597
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
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);
- src/blast/BlastGovernor.sol
46 emit LogFeeToChanged(_feeTo);
GitHub : 46
- src/blast/BlastMagicLP.sol
86 emit LogFeeToChanged(feeTo_); ... 91 emit LogOperatorChanged(operator, status);
- 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
- 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
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)) {
- 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
- 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
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
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;
- 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
- src/mimswap/periphery/Router.sol
- 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
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.
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 {
- 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) {
- 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
- src/mimswap/periphery/Factory.sol
- 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
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;
- src/blast/BlastTokenRegistry.sol
10 mapping(address => bool) public nativeYieldTokens;
GitHub : 10
- src/mimswap/MagicLP.sol
68 bool internal _INITIALIZED_;
GitHub : 68
uint
When it's PossiblePacking 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;
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;
- 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;
- 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
- 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
- src/mimswap/periphery/Factory.sol
- src/mimswap/periphery/Router.sol
- 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
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
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) {
- src/staking/LockingMultiRewards.sol
GitHub : 636
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) {
- 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
- 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
- src/staking/LockingMultiRewards.sol
GitHub : 69
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) {
- src/blast/BlastGovernor.sol
28 function claimNativeYields(address contractAddress) external onlyOperators returns (uint256) { ... 32 function claimMaxGasYields(address contractAddress) external onlyOperators returns (uint256) {
- src/blast/BlastMagicLP.sol
39 function version() external pure override returns (string memory) { ... 47 function claimGasYields() external onlyClones onlyImplementationOperators returns (uint256) {
- src/blast/BlastOnboarding.sol
160 function claimGasYields() external onlyOwner returns (uint256) { ... 226 function _implementation() internal view override returns (address) {
- 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) {
- src/mimswap/MagicLP.sol
- src/mimswap/libraries/DecimalMath.sol
GitHub : 23, 27, 31, 35, 39, 43, 47
- src/mimswap/libraries/Math.sol
- 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
- 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
assembly
There are 18 instance(s) of this issue:
- src/blast/BlastBox.sol
33 feeTo = feeTo_; ... 77 feeTo = feeTo_;
- src/blast/BlastGovernor.sol
20 feeTo = feeTo_; ... 45 feeTo = _feeTo;
- src/blast/BlastMagicLP.sol
32 feeTo = feeTo_; ... 85 feeTo = feeTo_;
- src/blast/BlastOnboarding.sol
94 feeTo = feeTo_; ... 152 feeTo = feeTo_; ... 191 bootstrapper = bootstrapper_;
- src/blast/BlastWrappers.sol
55 _governor = governor_;
GitHub : 55
- src/mimswap/MagicLP.sol
- src/mimswap/auxiliary/FeeRateModel.sol
- src/mimswap/periphery/Factory.sol
- src/staking/LockingMultiRewards.sol
GitHub : 135
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
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}));
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